<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom">
  <title>Silvan&#39;s Blog🐳</title>
  
  <subtitle>true</subtitle>
  <link href="https://silvan.chat/atom.xml" rel="self"/>
  
  <link href="https://silvan.chat/"/>
  <updated>2025-11-17T02:41:37.836Z</updated>
  <id>https://silvan.chat/</id>
  
  <author>
    <name>Silvan🐳</name>
    
  </author>
  
  <generator uri="https://hexo.io/">Hexo</generator>
  
  <entry>
    <title>苍穹外卖</title>
    <link href="https://silvan.chat/2025/11/10/%E8%8B%8D%E7%A9%B9%E5%A4%96%E5%8D%96-%E8%8B%8D%E7%A9%B9%E5%A4%96%E5%8D%96/"/>
    <id>https://silvan.chat/2025/11/10/%E8%8B%8D%E7%A9%B9%E5%A4%96%E5%8D%96-%E8%8B%8D%E7%A9%B9%E5%A4%96%E5%8D%96/</id>
    <published>2025-11-10T11:24:19.755Z</published>
    <updated>2025-11-17T02:41:37.836Z</updated>
    
    <content type="html"><![CDATA[<h1 id="day01"><a href="#day01" class="headerlink" title="day01"></a>day01</h1><h2 id="前端环境搭建"><a href="#前端环境搭建" class="headerlink" title="前端环境搭建"></a>前端环境搭建</h2><ul><li><p><strong>前端工程基于 nginx</strong><br>在黑马提供的资料中找到前端运行的nginx，将其移动到一个<code>非中文目录</code>下</p><div class="img-wrap"><div class="img-bg"><img class="img" src="https://raw.githubusercontent.com/silvan2077/markdown_pic/main/Picgo/image-20221106202239828.png"/></div></div><div class="note warning no-icon flat"><ul><li>Nginx目录必须放在没有中文的目录中才能正常运行！！</li><li>当前的Nginx的配置文件已经配置了反向代理，通过此配置可以将前端请求转发到后端服务。</li></ul></div></li><li><p><strong>启动nginx，访问测试</strong><br>双击nginx.exe即可启动nginx服务，默认访问端口为80。<br><a href="http://localhost:80">http://localhost:80</a></p><div class="img-wrap"><div class="img-bg"><img class="img" src="https://raw.githubusercontent.com/silvan2077/markdown_pic/main/Picgo/image-20221106202720190.png"/></div></div><details class="folding-tag" ><summary> 80端口被占用 </summary>              <div class='content'>              <ul><li>如果访问页面是以下页面，说明80端口被IIS占用，此时选择关闭IIS服务<div class="img-wrap"><div class="img-bg"><img class="img" src="https://raw.githubusercontent.com/silvan2077/markdown_pic/main/Picgo/20251110193721.png"/></div></div></li></ul><ol><li>以管理员身份运行cmd</li><li>输入<code>net stop http</code></li><li>提示是否停止该服务时，输入<code>Y</code></li><li>完成后输入<code>sc config http start=disabled</code></li><li>重新访问<a href="http://localhost:80">http://localhost:80</a>，即可正常访问前端页面。</li></ol>              </div>            </details></li></ul><h2 id="后端环境搭建"><a href="#后端环境搭建" class="headerlink" title="后端环境搭建"></a>后端环境搭建</h2><h3 id="熟悉项目结构"><a href="#熟悉项目结构" class="headerlink" title="熟悉项目结构"></a>熟悉项目结构</h3><p>导入后端资料到IDEA中<br><div class="img-wrap"><div class="img-bg"><img class="img" src="https://raw.githubusercontent.com/silvan2077/markdown_pic/main/Picgo/image-20221107092342140.png"/></div></div></p><div class="table-container"><table><thead><tr><th><strong>序号</strong></th><th><strong>名称</strong></th><th><strong>说明</strong></th></tr></thead><tbody><tr><td>1</td><td>sky-take-out</td><td>maven父工程，统一管理依赖版本，聚合其他子模块</td></tr><tr><td>2</td><td>sky-common</td><td>子模块，存放公共类，例如：工具类、常量类、异常类等</td></tr><tr><td>3</td><td>sky-pojo</td><td>子模块，存放实体类、VO、DTO等</td></tr><tr><td>4</td><td>sky-server</td><td>子模块，后端服务，存放配置文件、Controller、Service、Mapper等</td></tr></tbody></table></div><ul><li><p><strong>sky-common:</strong> 模块中存放的是公共类，供其他模块使用</p><div class="img-wrap"><div class="img-bg"><img class="img" src="https://raw.githubusercontent.com/silvan2077/markdown_pic/main/Picgo/image-20221107093606590.png"/></div></div><p>sky-common模块的每个包的作用：<br>| 名称        | 说明                           |<br>| —————- | ——————————————— |<br>| constant    | 存放相关常量类                 |<br>| context     | 存放上下文类                   |<br>| enumeration | 项目的枚举类存储               |<br>| exception   | 存放自定义异常类               |<br>| json        | 处理json转换的类               |<br>| properties  | 存放SpringBoot相关的配置属性类 |<br>| result      | 返回结果类的封装               |<br>| utils       | 常用工具类                     |</p></li><li><p><strong>sky-pojo:</strong> 模块中存放的是一些 entity、DTO、VO</p><div class="img-wrap"><div class="img-bg"><img class="img" src="https://raw.githubusercontent.com/silvan2077/markdown_pic/main/Picgo/image-20221107094611987.png"/></div></div><p>sky-pojo模块的每个包的作用：</p></li></ul><div class="table-container"><table><thead><tr><th><strong>名称</strong></th><th><strong>说明</strong></th></tr></thead><tbody><tr><td>Entity</td><td>实体，通常和数据库中的表对应</td></tr><tr><td>DTO</td><td>数据传输对象，通常用于程序中各层之间传递数据</td></tr><tr><td>VO</td><td>视图对象，为前端展示数据提供的对象</td></tr><tr><td>POJO</td><td>普通Java对象，只有属性和对应的getter和setter</td></tr></tbody></table></div><ul><li><strong>sky-server:</strong> 模块中存放的是 配置文件、配置类、拦截器、controller、service、mapper、启动类等</li></ul><div class="img-wrap"><div class="img-bg"><img class="img" src="https://raw.githubusercontent.com/silvan2077/markdown_pic/main/Picgo/image-20221107094852361.png"/></div></div><p>sky-server模块的每个包的作用：</p><div class="table-container"><table><thead><tr><th>名称</th><th>说明</th></tr></thead><tbody><tr><td>config</td><td>存放配置类</td></tr><tr><td>controller</td><td>存放controller类</td></tr><tr><td>interceptor</td><td>存放拦截器类</td></tr><tr><td>mapper</td><td>存放mapper接口</td></tr><tr><td>service</td><td>存放service类</td></tr><tr><td>SkyApplication</td><td>启动类</td></tr></tbody></table></div><h3 id="数据库环境搭建"><a href="#数据库环境搭建" class="headerlink" title="数据库环境搭建"></a>数据库环境搭建</h3><ul><li><p>使用navicate打开资料中的sql文件并执行，创建数据库</p><div class="img-wrap"><div class="img-bg"><img class="img" src="https://raw.githubusercontent.com/silvan2077/markdown_pic/main/Picgo/image-20221107101030138.png"/></div></div></li><li><p>执行完毕后，共创建了11张表</p><div class="tabs" id="后端环境搭建-数据库环境搭建"><ul class="nav-tabs"><li class="tab active"><button type="button" data-href="#后端环境搭建-数据库环境搭建-1">创建表</button></li><li class="tab"><button type="button" data-href="#后端环境搭建-数据库环境搭建-2">表说明</button></li></ul><div class="tab-contents"><div class="tab-item-content active" id="后端环境搭建-数据库环境搭建-1"><div class="img-wrap"><div class="img-bg"><img class="img" src="https://raw.githubusercontent.com/silvan2077/markdown_pic/main/Picgo/20251111145316.png"/></div></div><button type="button" class="tab-to-top" aria-label="scroll to top"><i class="fas fa-arrow-up"></i></button></div><div class="tab-item-content" id="后端环境搭建-数据库环境搭建-2"><div class="table-container"><table><thead><tr><th><strong>序号</strong></th><th><strong>表名</strong></th><th><strong>中文名</strong></th></tr></thead><tbody><tr><td>1</td><td>employee</td><td>员工表</td></tr><tr><td>2</td><td>category</td><td>分类表</td></tr><tr><td>3</td><td>dish</td><td>菜品表</td></tr><tr><td>4</td><td>dish_flavor</td><td>菜品口味表</td></tr><tr><td>5</td><td>setmeal</td><td>套餐表</td></tr><tr><td>6</td><td>setmeal_dish</td><td>套餐菜品关系表</td></tr><tr><td>7</td><td>user</td><td>用户表</td></tr><tr><td>8</td><td>address_book</td><td>地址表</td></tr><tr><td>9</td><td>shopping_cart</td><td>购物车表</td></tr><tr><td>10</td><td>orders</td><td>订单表</td></tr><tr><td>11</td><td>order_detail</td><td>订单明细表</td></tr></tbody></table></div><button type="button" class="tab-to-top" aria-label="scroll to top"><i class="fas fa-arrow-up"></i></button></div></div></div><p>这里只简单了解有哪些表，在提供的资料中的<code>数据库设计文档</code>中对表的结构和字段有详细介绍</p></li></ul><h3 id="前后端联调"><a href="#前后端联调" class="headerlink" title="前后端联调"></a>前后端联调</h3><p>后端的初始工程中已经实现了<strong>登录</strong>功能，直接进行前后端联调测试即可(别忘了到yml文件中配置数据库连接信息，否则会登录失败)<br><div class="img-wrap"><div class="img-bg"><img class="img" src="https://raw.githubusercontent.com/silvan2077/markdown_pic/main/Picgo/20251111150853.png"/></div></div><br>实现思路：<br><div class="img-wrap"><div class="img-bg"><img class="img" src="https://raw.githubusercontent.com/silvan2077/markdown_pic/main/Picgo/image-20221107110539832.png"/></div></div></p><h3 id="nginx反向代理和负载均衡"><a href="#nginx反向代理和负载均衡" class="headerlink" title="nginx反向代理和负载均衡"></a>nginx反向代理和负载均衡</h3><div class="note info no-icon flat"><p><strong>思考：前端发送的请求，是如何请求到后端服务的？</strong></p><ul><li>前端请求地址：<a href="http://localhost/api/employee/login">http://localhost/api/employee/login</a></li><li>后端接口地址：<a href="http://localhost:8080/admin/employee/login">http://localhost:8080/admin/employee/login</a></li></ul></div><div class="tabs" id="nginx反向代理和负载均衡"><ul class="nav-tabs"><li class="tab active"><button type="button" data-href="#nginx反向代理和负载均衡-1">前端请求地址</button></li><li class="tab"><button type="button" data-href="#nginx反向代理和负载均衡-2">后端接口地址</button></li></ul><div class="tab-contents"><div class="tab-item-content active" id="nginx反向代理和负载均衡-1"><div class="img-wrap"><div class="img-bg"><img class="img" src="https://raw.githubusercontent.com/silvan2077/markdown_pic/main/Picgo/image-20221107151607921.png"/></div></div><button type="button" class="tab-to-top" aria-label="scroll to top"><i class="fas fa-arrow-up"></i></button></div><div class="tab-item-content" id="nginx反向代理和负载均衡-2"><div class="img-wrap"><div class="img-bg"><img class="img" src="https://raw.githubusercontent.com/silvan2077/markdown_pic/main/Picgo/image-20221107151623005.png"/></div></div><button type="button" class="tab-to-top" aria-label="scroll to top"><i class="fas fa-arrow-up"></i></button></div></div></div><h4 id="nginx反向代理"><a href="#nginx反向代理" class="headerlink" title="nginx反向代理"></a>nginx反向代理</h4><p>nginx反向代理就是将前端发送的动态请求通过nginx转发到后端服务器<br><div class="img-wrap"><div class="img-bg"><img class="img" src="https://raw.githubusercontent.com/silvan2077/markdown_pic/main/Picgo/image-20221107152112092.png"/></div></div></p><p><strong><em>nginx反向代理的好处：</em></strong></p><ul><li><strong>提高访问速度</strong><ul><li>因为nginx本身可以缓存，如果访问同一接口并做了数据缓存，此时则不需要访问服务端，nginx就可以直接返回数据，进而提高了访问速度</li></ul></li><li><strong>进行负载均衡</strong><ul><li>负载均衡就是将大量请求按照我们指定的方式均衡的分配给集群中的每台服务器</li></ul></li><li><strong>保证后端服务安全</strong><ul><li>不暴露后端服务地址，将nginx作为请求访问的入口，到达nginx后转发到具体的后端服务器，从而保证后端服务器的安全</li></ul></li></ul><p><strong><em>nginx反向代理的配置方式：</em></strong></p><ul><li>在nginx目录下，修改nginx.config文件<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></pre></td><td class="code"><pre><span class="line">server&#123;</span><br><span class="line">    listen 80;</span><br><span class="line">    server_name localhost;</span><br><span class="line">    </span><br><span class="line">    # 当访问 http://localhost:80/api/../.. 这样的接口的时候，它会通过 location /api/ &#123;&#125; 这样的反向代理到 http://localhost:8080/admin/上来。</span><br><span class="line">    location /api/&#123;</span><br><span class="line">        proxy_pass http://localhost:8080/admin/; #反向代理</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><strong>proxy_pass：</strong>该指令是用来设置代理服务器的地址，可以是主机名称，IP地址加端口号等形式。</li></ul><h4 id="nginx负载均衡"><a href="#nginx负载均衡" class="headerlink" title="nginx负载均衡"></a>nginx负载均衡</h4><p>当服务以集群的方式部署时，nginx在转发请求到服务器时就需要做相应的负载均衡。负载均衡从本质上来说也是基于反向代理来实现的，最终效果都是转发请求</p><p><strong><em>nginx负载均衡配置方式：</em></strong><br><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></pre></td><td class="code"><pre><span class="line">upstream webservers&#123;</span><br><span class="line">    # 声明了两组服务器</span><br><span class="line">    server 192.168.100.128:8080;</span><br><span class="line">    server 192.168.100.129:8080;</span><br><span class="line">&#125;</span><br><span class="line">server&#123;</span><br><span class="line">    listen 80;</span><br><span class="line">    server_name localhost;</span><br><span class="line">    </span><br><span class="line">    location /api/&#123;</span><br><span class="line">        proxy_pass http://webservers/admin; #负载均衡</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><br><strong>upstream：</strong> 如果代理服务器是一组服务器的话，我们可以使用upstream指令配置后端服务器组。<br><strong>注：</strong> upstream后面的名称可自定义，但要上下保持一致。</p><div class="note info no-icon flat"><p>以上配置的含义是：监听80端口号，当访问<code>http://localhost:80/api/../..</code>这样的接口时，便会通过 <code>location /api/ {}</code> 这样的反向代理到 <code>http://webservers/admin</code>，根据<code>webservers</code>名称找到一组服务器，根据设置的<code>负载均衡策略</code>(默认是轮询)转发到具体的服务器。</p></div><p><strong>负载均衡的策略：</strong></p><div class="table-container"><table><thead><tr><th><strong>名称</strong></th><th><strong>说明</strong></th></tr></thead><tbody><tr><td>轮询</td><td>默认方式</td></tr><tr><td>weight</td><td>权重方式，默认为1，权重越高，被分配的客户端请求就越多</td></tr><tr><td>ip_hash</td><td>依据ip分配方式，这样每个访客可以固定访问一个后端服务</td></tr><tr><td>least_conn</td><td>依据最少连接方式，把请求优先分配给连接数少的后端服务</td></tr><tr><td>url_hash</td><td>依据url分配方式，这样相同的url会被分配到同一个后端服务</td></tr><tr><td>fair</td><td>依据响应时间方式，响应时间短的服务将会被优先分配</td></tr></tbody></table></div><p>具体配置方式：<br><div class="tabs" id="负载均衡策略"><ul class="nav-tabs"><li class="tab active"><button type="button" data-href="#负载均衡策略-1">轮询</button></li><li class="tab"><button type="button" data-href="#负载均衡策略-2">weight</button></li><li class="tab"><button type="button" data-href="#负载均衡策略-3">ip_hash</button></li><li class="tab"><button type="button" data-href="#负载均衡策略-4">least_conn</button></li><li class="tab"><button type="button" data-href="#负载均衡策略-5">url_hash</button></li><li class="tab"><button type="button" data-href="#负载均衡策略-6">fair</button></li></ul><div class="tab-contents"><div class="tab-item-content active" id="负载均衡策略-1"><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></pre></td><td class="code"><pre><span class="line">upstream webservers&#123;</span><br><span class="line">    server 192.168.100.128:8080;</span><br><span class="line">    server 192.168.100.129:8080;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><button type="button" class="tab-to-top" aria-label="scroll to top"><i class="fas fa-arrow-up"></i></button></div><div class="tab-item-content" id="负载均衡策略-2"><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></pre></td><td class="code"><pre><span class="line">upstream webservers&#123;</span><br><span class="line">    server 192.168.100.128:8080 weight=90;</span><br><span class="line">    server 192.168.100.129:8080 weight=10;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><button type="button" class="tab-to-top" aria-label="scroll to top"><i class="fas fa-arrow-up"></i></button></div><div class="tab-item-content" id="负载均衡策略-3"><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></pre></td><td class="code"><pre><span class="line">upstream webservers&#123;</span><br><span class="line">    ip_hash;</span><br><span class="line">    server 192.168.100.128:8080;</span><br><span class="line">    server 192.168.100.129:8080;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><button type="button" class="tab-to-top" aria-label="scroll to top"><i class="fas fa-arrow-up"></i></button></div><div class="tab-item-content" id="负载均衡策略-4"><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></pre></td><td class="code"><pre><span class="line">upstream webservers&#123;</span><br><span class="line">    least_conn;</span><br><span class="line">    server 192.168.100.128:8080;</span><br><span class="line">    server 192.168.100.129:8080;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><button type="button" class="tab-to-top" aria-label="scroll to top"><i class="fas fa-arrow-up"></i></button></div><div class="tab-item-content" id="负载均衡策略-5"><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></pre></td><td class="code"><pre><span class="line">upstream webservers&#123;</span><br><span class="line">    hash &amp;request_uri;</span><br><span class="line">    server 192.168.100.128:8080;</span><br><span class="line">    server 192.168.100.129:8080;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><button type="button" class="tab-to-top" aria-label="scroll to top"><i class="fas fa-arrow-up"></i></button></div><div class="tab-item-content" id="负载均衡策略-6"><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></pre></td><td class="code"><pre><span class="line">upstream webservers&#123;</span><br><span class="line">    server 192.168.100.128:8080;</span><br><span class="line">    server 192.168.100.129:8080;</span><br><span class="line">    fair;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><button type="button" class="tab-to-top" aria-label="scroll to top"><i class="fas fa-arrow-up"></i></button></div></div></div></p><h2 id="完善登录功能"><a href="#完善登录功能" class="headerlink" title="完善登录功能"></a>完善登录功能</h2><div class="note info no-icon flat"><p><strong>需求:</strong> 员工表的密码字段直接明文存储在数据库中，存在安全风险，需要对密码进行加密后，再进行存储。</p><div class="img-wrap"><div class="img-bg"><img class="img" src="https://raw.githubusercontent.com/silvan2077/markdown_pic/main/Picgo/image-20221107160739680.png"/></div></div></div><p><strong>实现步骤：</strong></p><ol><li>打开数据库中的employee表，修改明文密码为加密后的123456：<code>e10abc3949ba59abbe56e057f20f883e</code></li><li>修改Java代码，前端提交的密码进行MD5加密后再跟数据库中保存的密码进行比对<br>打开EmployeeServiceImpl类,修改代码<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><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="comment"> * <span class="doctag">@param</span> employeeLoginDTO</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@return</span></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> Employee <span class="title function_">login</span><span class="params">(EmployeeLoginDTO employeeLoginDTO)</span> &#123;</span><br><span class="line">    <span class="type">String</span> <span class="variable">username</span> <span class="operator">=</span> employeeLoginDTO.getUsername();</span><br><span class="line">    <span class="type">String</span> <span class="variable">password</span> <span class="operator">=</span> employeeLoginDTO.getPassword();</span><br><span class="line"></span><br><span class="line">    <span class="comment">//1、根据用户名查询数据库中的数据</span></span><br><span class="line">    <span class="type">Employee</span> <span class="variable">employee</span> <span class="operator">=</span> employeeMapper.getByUsername(username);</span><br><span class="line"></span><br><span class="line">    <span class="comment">//2、处理各种异常情况（用户名不存在、密码不对、账号被锁定）</span></span><br><span class="line">    <span class="keyword">if</span> (employee == <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_">AccountNotFoundException</span>(MessageConstant.ACCOUNT_NOT_FOUND);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">//密码比对</span></span><br><span class="line">    password = DigestUtils.md5DigestAsHex(password.getBytes());</span><br><span class="line">    <span class="keyword">if</span> (!password.equals(employee.getPassword())) &#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_">PasswordErrorException</span>(MessageConstant.PASSWORD_ERROR);</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">if</span> (employee.getStatus().equals(StatusConstant.DISABLE) ) &#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_">AccountLockedException</span>(MessageConstant.ACCOUNT_LOCKED);</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">return</span> employee;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></li></ol><h2 id="导入接口文档"><a href="#导入接口文档" class="headerlink" title="导入接口文档"></a>导入接口文档</h2><p>该项目使用的开发方式是当前企业主流的<code>前后端分离开发</code>，该方式要求在开发前先将接口定义好，这样前后端才能并行开发。</p><h3 id="前后端分离开发流程"><a href="#前后端分离开发流程" class="headerlink" title="前后端分离开发流程"></a>前后端分离开发流程</h3><div class="img-wrap"><div class="img-bg"><img class="img" src="https://raw.githubusercontent.com/silvan2077/markdown_pic/main/Picgo/image-20221107181446913.png"/></div></div><div class="note info no-icon flat"><p>开发流程：</p><ol><li>定义接口并确定接口的路径、请求方式、传入参数、返回参数。</li><li>前端开发人员和后端开发人员并行开发，开发过程中也可以自测</li><li>前后端人员进行联调测试</li><li>提交给测试人员进行最终测试</li></ol></div><h3 id="操作步骤"><a href="#操作步骤" class="headerlink" title="操作步骤"></a>操作步骤</h3><ul><li>课程中使用的是Yapi，因为种种原因无法使用，因此这里替换为ApiFox</li><li>ApiFox下载地址：<a href="https://apifox.com/">https://apifox.com/</a></li></ul><div class="note info no-icon flat"><p>ApiFox -&gt; 新建项目 -&gt; 项目管理 -&gt; 导入数据 -&gt; 选择Yapi -&gt; 将资料中的json文件导入</p></div><p>导入效果：<br><div class="img-wrap"><div class="img-bg"><img class="img" src="https://raw.githubusercontent.com/silvan2077/markdown_pic/main/Picgo/20251111170626.png"/></div></div></p><h2 id="Swagger"><a href="#Swagger" class="headerlink" title="Swagger"></a>Swagger</h2><h3 id="介绍"><a href="#介绍" class="headerlink" title="介绍"></a>介绍</h3><p><code>Swagger</code>是一个规范和完整的框架，用于生成、描述、调用和可视化<code>RESTful</code>风格的Web服务(<a href="https://swagger.io/">https://swagger.io/</a>)<br><strong>作用：</strong></p><ol><li>使前后端分离开发更加方便，利于团队协作</li><li>接口文档自动生成，后端开发只需要关注业务逻辑，无需手动编写接口文档</li><li>Spring将Swagger纳入了自身标准。可以通过在项目中引入Swaggerfox，来简单快捷的使用Swagger</li></ol><h3 id="使用步骤"><a href="#使用步骤" class="headerlink" title="使用步骤"></a>使用步骤</h3><div class="note info no-icon flat"><p><code>knife4j</code>是为<code>JavaMVC</code>框架集成<code>Swagger</code>生成Api文档的增强解决方案,前身是<code>swagger-bootstrap-ui</code>,取名kni4j是希望它能像一把匕首一样小巧,轻量,并且功能强悍!</p></div><ol><li>导入<code>knife4j</code>的maven坐标<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>com.github.xiaoymin<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>knife4j-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">dependency</span>&gt;</span></span><br></pre></td></tr></table></figure></li><li>在<code>WebMvcConfiguration</code>配置类中加入<code>knife4j</code>的配置<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><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">     * 通过knife4j生成接口文档</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@return</span></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> Docket <span class="title function_">docket</span><span class="params">()</span> &#123;</span><br><span class="line">        <span class="type">ApiInfo</span> <span class="variable">apiInfo</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">ApiInfoBuilder</span>()</span><br><span class="line">                .title(<span class="string">&quot;苍穹外卖项目接口文档&quot;</span>)</span><br><span class="line">                .version(<span class="string">&quot;2.0&quot;</span>)</span><br><span class="line">                .description(<span class="string">&quot;苍穹外卖项目接口文档&quot;</span>)</span><br><span class="line">                .build();</span><br><span class="line">        <span class="type">Docket</span> <span class="variable">docket</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">Docket</span>(DocumentationType.SWAGGER_2)</span><br><span class="line">                .apiInfo(apiInfo)</span><br><span class="line">                .select()</span><br><span class="line">                .apis(RequestHandlerSelectors.basePackage(<span class="string">&quot;com.sky.controller&quot;</span>))</span><br><span class="line">                .paths(PathSelectors.any())</span><br><span class="line">                .build();</span><br><span class="line">        <span class="keyword">return</span> docket;</span><br><span class="line">    &#125;</span><br></pre></td></tr></table></figure></li><li>继续在该配置类下设置静态资源映射，否则无法访问<code>knife4j</code>生成的接口文档<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><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="comment">     * 设置静态资源映射</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@param</span> registry</span></span><br><span class="line"><span class="comment">*/</span></span><br><span class="line"><span class="keyword">protected</span> <span class="keyword">void</span> <span class="title function_">addResourceHandlers</span><span class="params">(ResourceHandlerRegistry registry)</span> &#123;</span><br><span class="line">        registry.addResourceHandler(<span class="string">&quot;/doc.html&quot;</span>).addResourceLocations(<span class="string">&quot;classpath:/META-INF/resources/&quot;</span>);</span><br><span class="line">        registry.addResourceHandler(<span class="string">&quot;/webjars/**&quot;</span>).addResourceLocations(<span class="string">&quot;classpath:/META-INF/resources/webjars/&quot;</span>);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></li><li>接口文档的访问地址：<a href="http://localhost:8080/doc.html">http://localhost:8080/doc.html</a></li></ol><div class="img-wrap"><div class="img-bg"><img class="img" src="https://raw.githubusercontent.com/silvan2077/markdown_pic/main/Picgo/20251111174437.png"/></div></div><p>还可以在这里直接测试接口<br><div class="img-wrap"><div class="img-bg"><img class="img" src="https://raw.githubusercontent.com/silvan2077/markdown_pic/main/Picgo/20251111174543.png"/></div></div></p><p><strong>思考：</strong> 通过Swagger就可以生成接口文档了，还需要<code>ApiFox</code>吗？</p><ol><li><p><code>ApiFox</code>是设计阶段使用的工具，管理和维护接口</p></li><li><p><code>Swagger</code>是在开发阶段使用的框架，帮助后端开发人员做后端的接口测试</p></li></ol><h3 id="常用注解"><a href="#常用注解" class="headerlink" title="常用注解"></a>常用注解</h3><p>通过注解可以控制生成的接口文档，使接口文档拥有更好的可读性</p><div class="table-container"><table><thead><tr><th><strong>注解</strong></th><th><strong>说明</strong></th></tr></thead><tbody><tr><td>@Api</td><td>用在类上，例如Controller，表示对类的说明</td></tr><tr><td>@ApiModel</td><td>用在类上，例如entity、DTO、VO</td></tr><tr><td>@ApiModelProperty</td><td>用在属性上，描述属性信息</td></tr><tr><td>@ApiOperation</td><td>用在方法上，例如Controller的方法，说明方法的用途、作用</td></tr></tbody></table></div><div class="note warning no-icon flat"><p>通过在各种类上添加上述注解，生成更清晰，可读性更好的接口文档(下面添加的注解，在提供的资料中已经默认添加)</p></div><p><strong>sky-pojo模块中：</strong><br><div class="tabs" id="swagger常用注解sky-pojo模块"><ul class="nav-tabs"><li class="tab active"><button type="button" data-href="#swagger常用注解sky-pojo模块-1">EmployeeLoginDTO</button></li><li class="tab"><button type="button" data-href="#swagger常用注解sky-pojo模块-2">EmployeeLoginVO</button></li></ul><div class="tab-contents"><div class="tab-item-content active" id="swagger常用注解sky-pojo模块-1"><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><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="meta">@Data</span></span><br><span class="line"><span class="meta">@ApiModel(description = &quot;员工登录时传递的数据模型&quot;)</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">EmployeeLoginDTO</span> <span class="keyword">implements</span> <span class="title class_">Serializable</span> &#123;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@ApiModelProperty(&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">@ApiModelProperty(&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">&#125;</span><br></pre></td></tr></table></figure><button type="button" class="tab-to-top" aria-label="scroll to top"><i class="fas fa-arrow-up"></i></button></div><div class="tab-item-content" id="swagger常用注解sky-pojo模块-2"><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><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="meta">@Data</span></span><br><span class="line"><span class="meta">@Builder</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">@ApiModel(description = &quot;员工登录返回的数据格式&quot;)</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">EmployeeLoginVO</span> <span class="keyword">implements</span> <span class="title class_">Serializable</span> &#123;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@ApiModelProperty(&quot;主键值&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="meta">@ApiModelProperty(&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">@ApiModelProperty(&quot;姓名&quot;)</span></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">@ApiModelProperty(&quot;jwt令牌&quot;)</span></span><br><span class="line">    <span class="keyword">private</span> String token;</span><br><span class="line"></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><button type="button" class="tab-to-top" aria-label="scroll to top"><i class="fas fa-arrow-up"></i></button></div></div></div></p><p><strong>sky-server模块中:</strong><br><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><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="meta">@RestController</span></span><br><span class="line"><span class="meta">@RequestMapping(&quot;/admin/employee&quot;)</span></span><br><span class="line"><span class="meta">@Slf4j</span></span><br><span class="line"><span class="meta">@Api(tags = &quot;员工相关接口&quot;)</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">EmployeeController</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> EmployeeService employeeService;</span><br><span class="line">    <span class="meta">@Autowired</span></span><br><span class="line">    <span class="keyword">private</span> JwtProperties jwtProperties;</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">@param</span> employeeLoginDTO</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@return</span></span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    <span class="meta">@PostMapping(&quot;/login&quot;)</span></span><br><span class="line">    <span class="meta">@ApiOperation(value = &quot;员工登录&quot;)</span></span><br><span class="line">    <span class="keyword">public</span> Result&lt;EmployeeLoginVO&gt; <span class="title function_">login</span><span class="params">(<span class="meta">@RequestBody</span> EmployeeLoginDTO employeeLoginDTO)</span> &#123;</span><br><span class="line">        <span class="comment">//..............</span></span><br><span class="line"></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="comment">     * 退出</span></span><br><span class="line"><span class="comment">     *</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@return</span></span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    <span class="meta">@PostMapping(&quot;/logout&quot;)</span></span><br><span class="line">    <span class="meta">@ApiOperation(&quot;员工退出&quot;)</span></span><br><span class="line">    <span class="keyword">public</span> Result&lt;String&gt; <span class="title function_">logout</span><span class="params">()</span> &#123;</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">&#125;</span><br></pre></td></tr></table></figure></p><h1 id="day02"><a href="#day02" class="headerlink" title="day02"></a>day02</h1><h2 id="课程内容"><a href="#课程内容" class="headerlink" title="课程内容"></a>课程内容</h2><ul><li>新增员工</li><li>员工分页查询</li><li>启用禁用员工账号</li><li>编辑员工</li><li>导入分类模块功能代码</li></ul><p><strong>功能实现：</strong>员工管理、菜品分类管理。</p><div class="tabs" id="day02功能实现"><ul class="nav-tabs"><li class="tab active"><button type="button" data-href="#day02功能实现-1">员工管理效果</button></li><li class="tab"><button type="button" data-href="#day02功能实现-2">菜品分类管理效果</button></li></ul><div class="tab-contents"><div class="tab-item-content active" id="day02功能实现-1"><div class="img-wrap"><div class="img-bg"><img class="img" src="https://raw.githubusercontent.com/silvan2077/markdown_pic/main/Picgo/image-20221112172846316.png"/></div></div><button type="button" class="tab-to-top" aria-label="scroll to top"><i class="fas fa-arrow-up"></i></button></div><div class="tab-item-content" id="day02功能实现-2"><div class="img-wrap"><div class="img-bg"><img class="img" src="https://raw.githubusercontent.com/silvan2077/markdown_pic/main/Picgo/image-20221112172925354.png"/></div></div><button type="button" class="tab-to-top" aria-label="scroll to top"><i class="fas fa-arrow-up"></i></button></div></div></div><h2 id="新增员工"><a href="#新增员工" class="headerlink" title="新增员工"></a>新增员工</h2><h3 id="需求分析和设计"><a href="#需求分析和设计" class="headerlink" title="需求分析和设计"></a>需求分析和设计</h3><p>点击新增员工按钮后，弹出新增员工的弹窗<br><div class="img-wrap"><div class="img-bg"><img class="img" src="https://raw.githubusercontent.com/silvan2077/markdown_pic/main/Picgo/image-20221111161120975.png"/></div></div><br>当填写完表单信息，点击<code>保存</code>按钮后，会将表单的数据提交到服务端，在服务端中接收数据，并调用相关方法保存到数据库</p><div class="note warning no-icon flat"><p><strong>注意：</strong></p><ol><li>账号必须唯一</li><li>手机号必须是合法的十一位数字(1开头，且第二位数字不为2)</li><li>身份证号为合法的18位数字(前17位为数字，最后一位为数字或X)</li><li>密码默认为123456</li></ol></div><p>在资料中找到<code>项目接口文档</code> -&gt; <code>苍穹外卖-管理端接口.html</code><br><div class="img-wrap"><div class="img-bg"><img class="img" src="https://raw.githubusercontent.com/silvan2077/markdown_pic/main/Picgo/image-20221111162912753.png"/></div></div><br>明确新增员工接口的<strong>请求路径、请求方式、请求参数、返回数据</strong><br><div class="img-wrap"><div class="img-bg"><img class="img" src="https://raw.githubusercontent.com/silvan2077/markdown_pic/main/Picgo/image-20221111162930483.png"/></div></div></p><div class="note danger no-icon flat"><p>本项目约定：</p><ul><li><strong>管理端</strong> 发出的请求，统一使用 <strong>/admin</strong> 作为前缀。</li><li><strong>用户端</strong> 发出的请求，统一使用 <strong>/user</strong> 作为前缀。</li></ul></div><h3 id="代码开发"><a href="#代码开发" class="headerlink" title="代码开发"></a>代码开发</h3><h4 id="设置DTO类"><a href="#设置DTO类" class="headerlink" title="设置DTO类"></a>设置DTO类</h4><p><strong>根据接口来设计对应的DTO</strong><br>前端传递参数列表：<br><div class="img-wrap"><div class="img-bg"><img class="img" src="https://raw.githubusercontent.com/silvan2077/markdown_pic/main/Picgo/image-20221111164002448.png"/></div></div><br><div class="note info no-icon flat"><p><strong>思考：</strong> 为什么不能使用对应实体类，还要专门封装DTO类？</p></div></p><div class="img-wrap"><div class="img-bg"><img class="img" src="https://raw.githubusercontent.com/silvan2077/markdown_pic/main/Picgo/image-20221111164453341.png"/></div></div><p>当前端提交数据和实体类中对应属性差别较大时，建议使用DTO来封装数据<br>在sky-pojo模块中已经定义了<code>EmployeeDTO</code>类<br><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><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="meta">@Data</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">EmployeeDTO</span> <span class="keyword">implements</span> <span class="title class_">Serializable</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><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 name;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">private</span> String phone;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">private</span> String sex;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">private</span> String idNumber;</span><br><span class="line"></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></p><h4 id="创建接口和实现类"><a href="#创建接口和实现类" class="headerlink" title="创建接口和实现类"></a>创建接口和实现类</h4><p><strong>Controller层</strong></p><p>在sky-server模块的<code>EmployeeController</code>类中创建新增员工方法，接收前端提交的参数<br><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><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 class="doctag">@param</span> employeeDTO</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@return</span></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="meta">@ApiOperation(&quot;新增员工&quot;)</span></span><br><span class="line"><span class="keyword">public</span> Result <span class="title function_">save</span><span class="params">(<span class="meta">@RequestBody</span> EmployeeDTO employeeDTO)</span>&#123;</span><br><span class="line">    log.info(<span class="string">&quot;新增员工：&#123;&#125;&quot;</span>,employeeDTO);</span><br><span class="line">    employeeService.save(employeeDTO);<span class="comment">//该方法后续步骤会定义</span></span><br><span class="line">    <span class="keyword">return</span> Result.success();</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><br><div class="note warning no-icon flat"><p><strong>注：</strong> Result类定义了后端统一返回结果格式。</p></div></p><p>在sky-common模块的com.sky.result包中定义了Result类用来统一返回结果格式<br><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><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 class="doctag">@param</span> &lt;T&gt;</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; <span class="keyword">implements</span> <span class="title class_">Serializable</span> &#123;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">private</span> Integer code; <span class="comment">//编码：1成功，0和其它数字为失败</span></span><br><span class="line">    <span class="keyword">private</span> String msg; <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="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">        Result&lt;T&gt; result = <span class="keyword">new</span> <span class="title class_">Result</span>&lt;T&gt;();</span><br><span class="line">        result.code = <span class="number">1</span>;</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="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 object)</span> &#123;</span><br><span class="line">        Result&lt;T&gt; result = <span class="keyword">new</span> <span class="title class_">Result</span>&lt;T&gt;();</span><br><span class="line">        result.data = object;</span><br><span class="line">        result.code = <span class="number">1</span>;</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="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 msg)</span> &#123;</span><br><span class="line">        <span class="type">Result</span> <span class="variable">result</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">Result</span>();</span><br><span class="line">        result.msg = msg;</span><br><span class="line">        result.code = <span class="number">0</span>;</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">&#125;</span><br></pre></td></tr></table></figure></p><p><strong>Service层接口</strong></p><p>在sky-server模块的com.sky.server.EmployeeService中声明新增员工方法<br><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><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="comment"> * 新增员工</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@param</span> employeeDTO</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="keyword">void</span> <span class="title function_">save</span><span class="params">(EmployeeDTO employeeDTO)</span>;</span><br></pre></td></tr></table></figure></p><p><strong>Service层实现类</strong></p><p>在sky-server模块的com.sky.server.impl.EmployeeServiceImpl中实现新增员工方法<br><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><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="comment">     * <span class="doctag">@param</span> employeeDTO</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> <span class="keyword">void</span> <span class="title function_">save</span><span class="params">(EmployeeDTO employeeDTO)</span> &#123;</span><br><span class="line">        <span class="type">Employee</span> <span class="variable">employee</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">Employee</span>();</span><br><span class="line"></span><br><span class="line">        <span class="comment">//对象属性拷贝</span></span><br><span class="line">        BeanUtils.copyProperties(employeeDTO, employee);</span><br><span class="line"></span><br><span class="line">        <span class="comment">//设置账号的状态，默认正常状态 1表示正常 0表示锁定</span></span><br><span class="line">        employee.setStatus(StatusConstant.ENABLE);</span><br><span class="line"></span><br><span class="line">        <span class="comment">//设置密码，默认密码123456</span></span><br><span class="line">        employee.setPassword(DigestUtils.md5DigestAsHex(PasswordConstant.DEFAULT_PASSWORD.getBytes()));</span><br><span class="line"></span><br><span class="line">        <span class="comment">//设置当前记录的创建时间和修改时间</span></span><br><span class="line">        employee.setCreateTime(LocalDateTime.now());</span><br><span class="line">        employee.setUpdateTime(LocalDateTime.now());</span><br><span class="line"></span><br><span class="line">        <span class="comment">//设置当前记录创建人id和修改人id</span></span><br><span class="line">        employee.setCreateUser(<span class="number">10L</span>);<span class="comment">//目前写个假数据，后期修改</span></span><br><span class="line">        employee.setUpdateUser(<span class="number">10L</span>);</span><br><span class="line"></span><br><span class="line">        employeeMapper.insert(employee);<span class="comment">//后续步骤定义</span></span><br><span class="line">    &#125;</span><br></pre></td></tr></table></figure></p><p><strong>Mapper层</strong><br>在EmployeeMapper接口中添加新增员工方法，并通过注解的方式来映射SQL语句<br><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><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="comment">     * 插入员工数据</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@param</span> employee</span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    <span class="meta">@Insert(&quot;insert into employee (name, username, password, phone, sex, id_number, create_time, update_time, create_user, update_user,status) &quot; +</span></span><br><span class="line"><span class="meta">            &quot;values &quot; +</span></span><br><span class="line"><span class="meta">            &quot;(#&#123;name&#125;,#&#123;username&#125;,#&#123;password&#125;,#&#123;phone&#125;,#&#123;sex&#125;,#&#123;idNumber&#125;,#&#123;createTime&#125;,#&#123;updateTime&#125;,#&#123;createUser&#125;,#&#123;updateUser&#125;,#&#123;status&#125;)&quot;)</span></span><br><span class="line">    <span class="keyword">void</span> <span class="title function_">insert</span><span class="params">(Employee employee)</span>;</span><br></pre></td></tr></table></figure><br><div class="note warning no-icon flat"><p>在application.yml中已开启驼峰命名，故id_number和idNumber可对应。</p></div></p><h3 id="功能测试"><a href="#功能测试" class="headerlink" title="功能测试"></a>功能测试</h3><p><strong>功能测试实现方式：</strong></p><ul><li>通过接口文档测试</li><li>通前后端联调测试</li></ul><h4 id="接口文档测试"><a href="#接口文档测试" class="headerlink" title="接口文档测试"></a>接口文档测试</h4><p><strong>启动服务：</strong>访问[<a href="http://localhost:8080/doc.html][http://localhost:8080/doc.html]，进入新增员工接口">http://localhost:8080/doc.html][http://localhost:8080/doc.html]，进入新增员工接口</a></p><p>Json数据：<br><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></pre></td><td class="code"><pre><span class="line"><span class="punctuation">&#123;</span></span><br><span class="line">  <span class="attr">&quot;id&quot;</span><span class="punctuation">:</span> <span class="number">0</span><span class="punctuation">,</span></span><br><span class="line">  <span class="attr">&quot;idNumber&quot;</span><span class="punctuation">:</span> <span class="string">&quot;111222333444555666&quot;</span><span class="punctuation">,</span></span><br><span class="line">  <span class="attr">&quot;name&quot;</span><span class="punctuation">:</span> <span class="string">&quot;xiaozhi&quot;</span><span class="punctuation">,</span></span><br><span class="line">  <span class="attr">&quot;phone&quot;</span><span class="punctuation">:</span> <span class="string">&quot;13812344321&quot;</span><span class="punctuation">,</span></span><br><span class="line">  <span class="attr">&quot;sex&quot;</span><span class="punctuation">:</span> <span class="string">&quot;1&quot;</span><span class="punctuation">,</span></span><br><span class="line">  <span class="attr">&quot;username&quot;</span><span class="punctuation">:</span> <span class="string">&quot;小智&quot;</span></span><br><span class="line"><span class="punctuation">&#125;</span></span><br></pre></td></tr></table></figure><br>发送请求后，返回的响应码是401报错<br><div class="img-wrap"><div class="img-bg"><img class="img" src="https://raw.githubusercontent.com/silvan2077/markdown_pic/main/Picgo/20251111185046.png"/></div></div><br>通过控制台打印的信息可以知道是<code>JwtTokenAdminInterceptor</code>拦截器出现的问题<br><div class="img-wrap"><div class="img-bg"><img class="img" src="https://raw.githubusercontent.com/silvan2077/markdown_pic/main/Picgo/20251111194813.png"/></div></div><br><div class="note warning no-icon flat"><p><strong>报错原因：</strong> JWT令牌失效导致<code>EmployeeController</code>的<code>save</code>方法没有被调用<br><strong>解决方法：</strong> 调用员工登录接口获得一个合法的JWT令牌</p></div></p><p><strong>获得令牌：</strong><br>使用admin用户登录获取令牌<br><div class="img-wrap"><div class="img-bg"><img class="img" src="https://raw.githubusercontent.com/silvan2077/markdown_pic/main/Picgo/image-20221111185649636.png"/></div></div></p><p><strong>添加令牌：</strong></p><p>将合法的JWT令牌添加到全局参数中<br>文档管理—&gt;全局参数设置—&gt;添加参数</p><div class="img-wrap"><div class="img-bg"><img class="img" src="https://raw.githubusercontent.com/silvan2077/markdown_pic/main/Picgo/image-20221111185901726.png"/></div></div><p><strong>接口测试：</strong><br><div class="img-wrap"><div class="img-bg"><img class="img" src="https://raw.githubusercontent.com/silvan2077/markdown_pic/main/Picgo/image-20221111190132481.png"/></div></div><br>其中，请求头部含有JWT令牌<br><div class="img-wrap"><div class="img-bg"><img class="img" src="https://raw.githubusercontent.com/silvan2077/markdown_pic/main/Picgo/image-20221111190248268.png"/></div></div></p><h4 id="前后端联调测试"><a href="#前后端联调测试" class="headerlink" title="前后端联调测试"></a>前后端联调测试</h4><p>启动服务后，访问<a href="localhost">localhost</a>，就能访问到登录界面<br>登录 -&gt; 员工管理 -&gt; 添加员工</p><div class="note warning no-icon flat"><p>期间可能会出现一些错误不必处理，因为那是分页查询没有完成的问题</p></div><div class="img-wrap"><div class="img-bg"><img class="img" src="https://raw.githubusercontent.com/silvan2077/markdown_pic/main/Picgo/image-20221111192339271.png"/></div></div><p>填写信息并保存后就可以在数据库中看到对应的信息了<br><div class="img-wrap"><div class="img-bg"><img class="img" src="https://raw.githubusercontent.com/silvan2077/markdown_pic/main/Picgo/image-20221111192446950.png"/></div></div></p><div class="note warning no-icon flat"><p>由于开发阶段前端和后端是并行开发的，后端完成某个功能后，此时前端对应的功能可能还没有开发完成，导致无法进行前后端联调测试。<strong>所以在开发阶段，后端测试主要以接口文档测试为主。</strong></p></div><h3 id="代码完善"><a href="#代码完善" class="headerlink" title="代码完善"></a>代码完善</h3><p>目前存在的问题主要有两个：</p><ol><li>如果录入的用户名已存在，报出的异常没有进行处理，导致程序异常退出</li><li>新增员工时，现在是将创建人和修改人的id设置为固定值，后期需要根据登录用户来动态设置</li></ol><h4 id="问题一"><a href="#问题一" class="headerlink" title="问题一"></a>问题一</h4><p><strong>问题描述：</strong> 数据库中username字段设置了唯一索引，当用户录入的用户名已存在时会直接抛出异常而没有进行处理</p><div class="img-wrap"><div class="img-bg"><img class="img" src="https://raw.githubusercontent.com/silvan2077/markdown_pic/main/Picgo/20251112121152.png"/></div></div><p><strong>解决方法：</strong> 添加全局异常处理器</p><p>在sky-server模块的com.sky.hander包下有一个GlobalExceptionHandler类，用于处理全局异常，在其中添加以下代码用于处理SQL异常</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></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">     * <span class="doctag">@param</span> ex</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@return</span></span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    <span class="meta">@ExceptionHandler</span></span><br><span class="line">    <span class="keyword">public</span> Result <span class="title function_">exceptionHandler</span><span class="params">(SQLIntegrityConstraintViolationException ex)</span>&#123;</span><br><span class="line">        <span class="comment">//Duplicate entry &#x27;zhangsan&#x27; for key &#x27;employee.idx_username&#x27;</span></span><br><span class="line">        <span class="type">String</span> <span class="variable">message</span> <span class="operator">=</span> ex.getMessage();</span><br><span class="line">        <span class="keyword">if</span>(message.contains(<span class="string">&quot;Duplicate entry&quot;</span>))&#123;</span><br><span class="line">            String[] split = message.split(<span class="string">&quot; &quot;</span>);</span><br><span class="line">            <span class="type">String</span> <span class="variable">username</span> <span class="operator">=</span> split[<span class="number">2</span>];</span><br><span class="line">            <span class="type">String</span> <span class="variable">msg</span> <span class="operator">=</span> username + MessageConstant.ALREADY_EXISTS;</span><br><span class="line">            <span class="keyword">return</span> Result.error(msg);</span><br><span class="line">        &#125;<span class="keyword">else</span>&#123;</span><br><span class="line">            <span class="keyword">return</span> Result.error(MessageConstant.UNKNOWN_ERROR);</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br></pre></td></tr></table></figure><p>在sky-common模块的MessageConstant类中添加以下常量<br><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><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">ALREADY_EXISTS</span> <span class="operator">=</span> <span class="string">&quot;已存在&quot;</span>;</span><br></pre></td></tr></table></figure></p><p>此时重启服务器，再次添加一个已经存在的用户名，就会返回已存在的错误信息了<br><div class="img-wrap"><div class="img-bg"><img class="img" src="https://raw.githubusercontent.com/silvan2077/markdown_pic/main/Picgo/20251112122002.png"/></div></div></p><h4 id="问题二"><a href="#问题二" class="headerlink" title="问题二"></a>问题二</h4><p><strong>问题描述：</strong> 新增员工时，现在是将创建人和修改人的id设置为固定值，后期需要根据登录用户来动态设置<br><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><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">@Override</span></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">save</span><span class="params">(EmployeeDTO employeeDTO)</span> &#123;</span><br><span class="line">        <span class="type">Employee</span> <span class="variable">employee</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">Employee</span>();</span><br><span class="line"></span><br><span class="line">        <span class="comment">//对象属性拷贝</span></span><br><span class="line">        BeanUtils.copyProperties(employeeDTO, employee);</span><br><span class="line"></span><br><span class="line">        <span class="comment">//设置账号的状态，默认正常状态 1表示正常 0表示锁定</span></span><br><span class="line">        employee.setStatus(StatusConstant.ENABLE);</span><br><span class="line"></span><br><span class="line">        <span class="comment">//设置密码，默认密码123456</span></span><br><span class="line">        employee.setPassword(DigestUtils.md5DigestAsHex(PasswordConstant.DEFAULT_PASSWORD.getBytes()));</span><br><span class="line"></span><br><span class="line">        <span class="comment">//设置当前记录的创建时间和修改时间</span></span><br><span class="line">        employee.setCreateTime(LocalDateTime.now());</span><br><span class="line">        employee.setUpdateTime(LocalDateTime.now());</span><br><span class="line"></span><br><span class="line">        <span class="comment">//设置当前记录创建人id和修改人id</span></span><br><span class="line">        employee.setCreateUser(<span class="number">10L</span>);<span class="comment">//目前写个假数据，后期修改</span></span><br><span class="line">        employee.setUpdateUser(<span class="number">10L</span>);</span><br><span class="line"></span><br><span class="line">        <span class="comment">//后续步骤定义</span></span><br><span class="line">        employeeMapper.insert(employee);</span><br><span class="line">    &#125;</span><br></pre></td></tr></table></figure></p><p><strong>解决方法：</strong> 通过某种方式动态获取当前员工的id<br><div class="img-wrap"><div class="img-bg"><img class="img" src="https://raw.githubusercontent.com/silvan2077/markdown_pic/main/Picgo/image-20221111201922482.png"/></div></div><br>员工登录成功后就会生成JWT令牌并响应给前端</p><p>在EmployeeController的login方法中已经添加过以下生成JWT令牌的代码<br><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><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">@RestController</span></span><br><span class="line"><span class="meta">@RequestMapping(&quot;/admin/employee&quot;)</span></span><br><span class="line"><span class="meta">@Slf4j</span></span><br><span class="line"><span class="meta">@Api(tags = &quot;员工相关接口&quot;)</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">EmployeeController</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> EmployeeService employeeService;</span><br><span class="line">    <span class="meta">@Autowired</span></span><br><span class="line">    <span class="keyword">private</span> JwtProperties jwtProperties;</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">@param</span> employeeLoginDTO</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@return</span></span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    <span class="meta">@PostMapping(&quot;/login&quot;)</span></span><br><span class="line">    <span class="meta">@ApiOperation(value = &quot;员工登录&quot;)</span></span><br><span class="line">    <span class="keyword">public</span> Result&lt;EmployeeLoginVO&gt; <span class="title function_">login</span><span class="params">(<span class="meta">@RequestBody</span> EmployeeLoginDTO employeeLoginDTO)</span> &#123;</span><br><span class="line">        <span class="comment">//.........</span></span><br><span class="line"></span><br><span class="line">        <span class="comment">//登录成功后，生成jwt令牌</span></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">        claims.put(JwtClaimsConstant.EMP_ID, employee.getId());</span><br><span class="line">        <span class="type">String</span> <span class="variable">token</span> <span class="operator">=</span> JwtUtil.createJWT(</span><br><span class="line">                jwtProperties.getAdminSecretKey(),</span><br><span class="line">                jwtProperties.getAdminTtl(),</span><br><span class="line">                claims);</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">return</span> Result.success(employeeLoginVO);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><br>在后续请求中，前端都会携带JWT令牌，通过JWT令牌可以解析出当前员工的id(该方法写在sky-server模块中的com.sky.interceptor包下)</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></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="meta">@Slf4j</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">JwtTokenAdminInterceptor</span> <span class="keyword">implements</span> <span class="title class_">HandlerInterceptor</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> JwtProperties jwtProperties;</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="comment">     * <span class="doctag">@param</span> request</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@param</span> response</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@param</span> handler</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@return</span></span></span><br><span class="line"><span class="comment">     * <span class="doctag">@throws</span> Exception</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_">preHandle</span><span class="params">(HttpServletRequest request, HttpServletResponse response, Object handler)</span> <span class="keyword">throws</span> Exception &#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">//1、从请求头中获取令牌</span></span><br><span class="line">        <span class="type">String</span> <span class="variable">token</span> <span class="operator">=</span> request.getHeader(jwtProperties.getAdminTokenName());</span><br><span class="line"></span><br><span class="line">        <span class="comment">//2、校验令牌</span></span><br><span class="line">        <span class="keyword">try</span> &#123;</span><br><span class="line">            log.info(<span class="string">&quot;jwt校验:&#123;&#125;&quot;</span>, token);</span><br><span class="line">            <span class="type">Claims</span> <span class="variable">claims</span> <span class="operator">=</span> JwtUtil.parseJWT(jwtProperties.getAdminSecretKey(), token);</span><br><span class="line">            <span class="type">Long</span> <span class="variable">empId</span> <span class="operator">=</span> Long.valueOf(claims.get(JwtClaimsConstant.EMP_ID).toString());</span><br><span class="line">            log.info(<span class="string">&quot;当前员工id：&quot;</span>, empId);</span><br><span class="line">            <span class="comment">//3、通过，放行</span></span><br><span class="line">            <span class="keyword">return</span> <span class="literal">true</span>;</span><br><span class="line">        &#125; <span class="keyword">catch</span> (Exception ex) &#123;</span><br><span class="line">            <span class="comment">//4、不通过，响应401状态码</span></span><br><span class="line">            response.setStatus(<span class="number">401</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">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><div class="note info no-icon flat"><p><strong>思考：</strong> 光是解析出员工id，如何传递给Service的save方法呢？<br>通过ThreadLocal来传递</p></div><h4 id="ThreadLocal"><a href="#ThreadLocal" class="headerlink" title="ThreadLocal"></a>ThreadLocal</h4><p><strong>简介：</strong><br><code>ThreadLocal</code>并不是一个线程，而是一个Thread的<code>局部变量</code><br>ThreadLocal为每个线程提供单独一份存储空间，具有线程隔离的效果，只有在线程内才能获取到对应的值，线程外则不能访问，而每一次用户的请求都是一个独立的线程，所以可以通过ThreadLocal来传递当前员工的id</p><p><strong>常用方法：</strong></p><ul><li><code>public void set(T value)</code>     设置当前线程的线程局部变量的值</li><li><code>public T get()</code>         返回当前线程所对应的线程局部变量的值</li><li><code>public void remove()</code>        移除当前线程的线程局部变量</li></ul><p><strong>解决问题：</strong></p><div class="img-wrap"><div class="img-bg"><img class="img" src="https://raw.githubusercontent.com/silvan2077/markdown_pic/main/Picgo/image-20221111212349365.png"/></div></div><p>在初始工程中的sky-common模块已经封装了ThreadLocal操作的工具类<br><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><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_">BaseContext</span> &#123;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">static</span> ThreadLocal&lt;Long&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">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title function_">setCurrentId</span><span class="params">(Long id)</span> &#123;</span><br><span class="line">        threadLocal.set(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">static</span> Long <span class="title function_">getCurrentId</span><span class="params">()</span> &#123;</span><br><span class="line">        <span class="keyword">return</span> threadLocal.get();</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_">removeCurrentId</span><span class="params">()</span> &#123;</span><br><span class="line">        threadLocal.remove();</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><br>在拦截器中解析出当前登录员工的id，并放入线程局部变量中（setCurrentId方法）<br><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><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"> * 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="meta">@Slf4j</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">JwtTokenAdminInterceptor</span> <span class="keyword">implements</span> <span class="title class_">HandlerInterceptor</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> JwtProperties jwtProperties;</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="comment">     * <span class="doctag">@param</span> request</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@param</span> response</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@param</span> handler</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@return</span></span></span><br><span class="line"><span class="comment">     * <span class="doctag">@throws</span> Exception</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_">preHandle</span><span class="params">(HttpServletRequest request, HttpServletResponse response, Object handler)</span> <span class="keyword">throws</span> Exception &#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">//2、校验令牌</span></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">Claims</span> <span class="variable">claims</span> <span class="operator">=</span> JwtUtil.parseJWT(jwtProperties.getAdminSecretKey(), token);</span><br><span class="line">            <span class="type">Long</span> <span class="variable">empId</span> <span class="operator">=</span> Long.valueOf(claims.get(JwtClaimsConstant.EMP_ID).toString());</span><br><span class="line">            log.info(<span class="string">&quot;当前员工id：&quot;</span>, empId);</span><br><span class="line">            <span class="comment">/////将用户id存储到ThreadLocal////////</span></span><br><span class="line">            BaseContext.setCurrentId(empId);</span><br><span class="line">            <span class="comment">////////////////////////////////////</span></span><br><span class="line">            <span class="comment">//3、通过，放行</span></span><br><span class="line">            <span class="keyword">return</span> <span class="literal">true</span>;</span><br><span class="line">        &#125; <span class="keyword">catch</span> (Exception ex) &#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><br>在Service中直接调用<code>BaseContext.getCurrentId()</code>方法获取当前登录员工的id<br><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">employee.setCreateUser(BaseContext.getCurrentId());</span><br><span class="line">employee.setUpdateUser(BaseContext.getCurrentId());</span><br></pre></td></tr></table></figure><br>测试:使用admin用户来添加一条记录，查看是否能保留创建者的id</p><div class="img-wrap"><div class="img-bg"><img class="img" src="https://raw.githubusercontent.com/silvan2077/markdown_pic/main/Picgo/20251112134948.png"/></div></div><h2 id="员工分页查询"><a href="#员工分页查询" class="headerlink" title="员工分页查询"></a>员工分页查询</h2><h3 id="需求分析和设计-1"><a href="#需求分析和设计-1" class="headerlink" title="需求分析和设计"></a>需求分析和设计</h3><p><strong>需求分析：</strong> 当系统之员工很多时，将数据以分页的方式来展示更加清晰明确。在分页查询页面，除了分页条件以外，还有一个查询条件”员工姓名”<br><div class="img-wrap"><div class="img-bg"><img class="img" src="https://raw.githubusercontent.com/silvan2077/markdown_pic/main/Picgo/image-20221111215309289.png"/></div></div><br><div class="note info no-icon flat"><p><strong>业务规则：</strong></p><ul><li>根据页码展示员工信息</li><li>每页展示10条数据</li><li>分页查询时可以根据需要，输入员工姓名进行查询</li></ul></div><br><strong>接口设计：</strong><br>资料 -&gt; 项目接口文档 -&gt; 苍穹外卖-管理端接口.html</p><div class="img-wrap"><div class="img-bg"><img class="img" src="https://raw.githubusercontent.com/silvan2077/markdown_pic/main/Picgo/image-20221111220031113.png"/></div></div><div class="img-wrap"><div class="img-bg"><img class="img" src="https://raw.githubusercontent.com/silvan2077/markdown_pic/main/Picgo/image-20221111220041965.png"/></div></div><div class="note warning no-icon flat"><p><strong>注意：</strong></p><ul><li>请求参数类型为Query，不是json格式提交，在路径后直接拼接。/admin/employee/page?name=zhangsan</li><li>返回数据中records数组中使用Employee实体类对属性进行封装。</li></ul></div><h3 id="代码开发-1"><a href="#代码开发-1" class="headerlink" title="代码开发"></a>代码开发</h3><h4 id="设置DTO类-1"><a href="#设置DTO类-1" class="headerlink" title="设置DTO类"></a>设置DTO类</h4><p>根据请求参数进行封装，在sky-pojo模块中创建EmployeePageQueryDTO类<br><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><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="meta">@Data</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">EmployeePageQueryDTO</span> <span class="keyword">implements</span> <span class="title class_">Serializable</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><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> page;</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> pageSize;</span><br><span class="line"></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></p><h4 id="封装分页结果类"><a href="#封装分页结果类" class="headerlink" title="封装分页结果类"></a>封装分页结果类</h4><div class="note info no-icon flat"><p>后面所有的分页查询，统一都封装为<code>PageResult</code>对象。</p></div><p>放在sky-common模块<br><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><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="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_">PageResult</span> <span class="keyword">implements</span> <span class="title class_">Serializable</span> &#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><br><span class="line">    <span class="keyword">private</span> List records; <span class="comment">//当前页数据集合</span></span><br><span class="line"></span><br><span class="line">&#125;</span><br><span class="line"></span><br></pre></td></tr></table></figure><br>员工信息分页查询后端返回的对象类型为: <code>Result&lt;PageResult&gt;</code><br><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><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 class="doctag">@param</span> &lt;T&gt;</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; <span class="keyword">implements</span> <span class="title class_">Serializable</span> &#123;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">private</span> Integer code; <span class="comment">//编码：1成功，0和其它数字为失败</span></span><br><span class="line">    <span class="keyword">private</span> String msg; <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="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">        Result&lt;T&gt; result = <span class="keyword">new</span> <span class="title class_">Result</span>&lt;T&gt;();</span><br><span class="line">        result.code = <span class="number">1</span>;</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="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 object)</span> &#123;</span><br><span class="line">        Result&lt;T&gt; result = <span class="keyword">new</span> <span class="title class_">Result</span>&lt;T&gt;();</span><br><span class="line">        result.data = object;</span><br><span class="line">        result.code = <span class="number">1</span>;</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="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 msg)</span> &#123;</span><br><span class="line">        <span class="type">Result</span> <span class="variable">result</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">Result</span>();</span><br><span class="line">        result.msg = msg;</span><br><span class="line">        result.code = <span class="number">0</span>;</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">&#125;</span><br></pre></td></tr></table></figure></p><h4 id="创建接口和实现类-1"><a href="#创建接口和实现类-1" class="headerlink" title="创建接口和实现类"></a>创建接口和实现类</h4><p><strong>Controller层</strong><br>在员工控制层添加分页查询方法<br><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><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 class="doctag">@param</span> employeePageQueryDTO</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@return</span></span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="meta">@GetMapping(&quot;/page&quot;)</span></span><br><span class="line"><span class="meta">@ApiOperation(&quot;员工分页查询&quot;)</span></span><br><span class="line"><span class="keyword">public</span> Result&lt;PageResult&gt; <span class="title function_">page</span><span class="params">(EmployeePageQueryDTO employeePageQueryDTO)</span>&#123;</span><br><span class="line">    log.info(<span class="string">&quot;员工分页查询，参数为：&#123;&#125;&quot;</span>, employeePageQueryDTO);</span><br><span class="line">    <span class="type">PageResult</span> <span class="variable">pageResult</span> <span class="operator">=</span> employeeService.pageQuery(employeePageQueryDTO);<span class="comment">//后续定义</span></span><br><span class="line">    <span class="keyword">return</span> Result.success(pageResult);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></p><p><strong>Service层接口</strong><br>声明pageQuery方法<br><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><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="comment"> * 分页查询</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@param</span> employeePageQueryDTO</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@return</span></span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line">PageResult <span class="title function_">pageQuery</span><span class="params">(EmployeePageQueryDTO employeePageQueryDTO)</span>;</span><br></pre></td></tr></table></figure></p><p><strong>Service层实现类</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></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">@param</span> employeePageQueryDTO</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@return</span></span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="keyword">public</span> PageResult <span class="title function_">pageQuery</span><span class="params">(EmployeePageQueryDTO employeePageQueryDTO)</span> &#123;</span><br><span class="line">    <span class="comment">// select * from employee limit 0,10</span></span><br><span class="line">    <span class="comment">//开始分页查询</span></span><br><span class="line">    PageHelper.startPage(employeePageQueryDTO.getPage(), employeePageQueryDTO.getPageSize());</span><br><span class="line"></span><br><span class="line">    Page&lt;Employee&gt; page = employeeMapper.pageQuery(employeePageQueryDTO);<span class="comment">//后续定义</span></span><br><span class="line"></span><br><span class="line">    <span class="type">long</span> <span class="variable">total</span> <span class="operator">=</span> page.getTotal();</span><br><span class="line">    List&lt;Employee&gt; records = page.getResult();</span><br><span class="line"></span><br><span class="line">    <span class="keyword">return</span> <span class="keyword">new</span> <span class="title class_">PageResult</span>(total, records);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><div class="note warning no-icon flat"><p><strong>注意：</strong> 该功能是使用mybatis提供的分页插件PageHelper来简化分页代码的开发，底层基于mybatis的拦截器实现,该功能需要在pom.xml文中添加依赖(初始工程已添加)<br><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-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>$&#123;pagehelper&#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></pre></td></tr></table></figure></p></div><p><strong>Mapper层</strong></p><p>在EmployeeMapper中声明pageQuery方法<br><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><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="comment"> * 分页查询</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@param</span> employeePageQueryDTO</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@return</span></span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line">Page&lt;Employee&gt; <span class="title function_">pageQuery</span><span class="params">(EmployeePageQueryDTO employeePageQueryDTO)</span>;</span><br></pre></td></tr></table></figure><br>因为该分页操作涉及动态条件查询，所以通过配置文件来写sql语句，在 <code>src/main/resources/mapper/EmployeeMapper.xml</code> 中编写SQL<br><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="tag">&lt;<span class="name">select</span> <span class="attr">id</span>=<span class="string">&quot;pageQuery&quot;</span> <span class="attr">resultType</span>=<span class="string">&quot;com.sky.entity.Employee&quot;</span>&gt;</span></span><br><span class="line">    select * from employee</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;name != null and name != &#x27;&#x27;&quot;</span>&gt;</span></span><br><span class="line">            and name like concat(&#x27;%&#x27;,#&#123;name&#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">where</span>&gt;</span></span><br><span class="line">    order by create_time desc</span><br><span class="line"><span class="tag">&lt;/<span class="name">select</span>&gt;</span></span><br></pre></td></tr></table></figure></p><h3 id="功能测试-1"><a href="#功能测试-1" class="headerlink" title="功能测试"></a>功能测试</h3><h4 id="接口文档测试-1"><a href="#接口文档测试-1" class="headerlink" title="接口文档测试"></a>接口文档测试</h4><p><strong>重启服务：</strong>访问<a href="http://localhost:8080/doc.html，进入员工分页查询">http://localhost:8080/doc.html，进入员工分页查询</a><br><div class="img-wrap"><div class="img-bg"><img class="img" src="https://raw.githubusercontent.com/silvan2077/markdown_pic/main/Picgo/image-20221112101848657.png"/></div></div><br><div class="note warning no-icon flat"><p>如果一直报401错误，可能是token失效的原因，此时需要重新登录获取token</p></div><br><strong>响应结果：</strong></p><div class="img-wrap"><div class="img-bg"><img class="img" src="https://raw.githubusercontent.com/silvan2077/markdown_pic/main/Picgo/20251112144447.png"/></div></div><h4 id="前后端联调测试-1"><a href="#前后端联调测试-1" class="headerlink" title="前后端联调测试"></a>前后端联调测试</h4><p><strong>点击员工管理并输入员工姓名</strong><br><div class="img-wrap"><div class="img-bg"><img class="img" src="https://raw.githubusercontent.com/silvan2077/markdown_pic/main/Picgo/20251112144754.png"/></div></div></p><p>此时员工的分页查询功能已经顺利完成了，但是仔细观察会发现，显示的 <strong>最后操作时间</strong> 格式很怪，这个问题在 <strong>代码完善</strong> 中解决</p><div class="img-wrap"><div class="img-bg"><img class="img" src="https://raw.githubusercontent.com/silvan2077/markdown_pic/main/Picgo/20251112144843.png"/></div></div><h3 id="代码完善-1"><a href="#代码完善-1" class="headerlink" title="代码完善"></a>代码完善</h3><p><strong>问题描述：</strong>操作时间字段显示有问题。<br><div class="img-wrap"><div class="img-bg"><img class="img" src="https://raw.githubusercontent.com/silvan2077/markdown_pic/main/Picgo/image-20221112103235539.png"/></div></div></p><p><strong>解决方式：</strong></p><ul><li><strong>方式一：</strong> 在属性上加上注解<code>@JsonFormat</code>，指定日期格式<div class="img-wrap"><div class="img-bg"><img class="img" src="https://raw.githubusercontent.com/silvan2077/markdown_pic/main/Picgo/image-20221112103501581.png"/></div></div></li><li><strong>方式二：</strong> 添加消息转换器，统一日期类型的格式处理</li></ul><div class="note info no-icon flat"><p>这里推荐使用方式二，因为方式一的处理方式，只对单一属性进行处理，需要每个时间类型都加上<code>@JsonFormat</code>注解，效率较低，而方式二的处理方式，是对所有日期类型进行统一处理，效率较高</p></div><p><strong>解决方法</strong><br>在sky-server模块下的<code>WebMvcConfiguration</code>中扩展SpringMVC的消息转换器，统一对日期类型进行格式处理<br><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><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="comment"> * 扩展Spring MVC框架的消息转化器</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@param</span> converters</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">protected</span> <span class="keyword">void</span> <span class="title function_">extendMessageConverters</span><span class="params">(List&lt;HttpMessageConverter&lt;?&gt;&gt; converters)</span> &#123;</span><br><span class="line">    log.info(<span class="string">&quot;扩展消息转换器...&quot;</span>);</span><br><span class="line">    <span class="comment">//创建一个消息转换器对象</span></span><br><span class="line">    <span class="type">MappingJackson2HttpMessageConverter</span> <span class="variable">converter</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">MappingJackson2HttpMessageConverter</span>();</span><br><span class="line">    <span class="comment">//需要为消息转换器设置一个对象转换器，对象转换器可以将Java对象序列化为json数据</span></span><br><span class="line">    converter.setObjectMapper(<span class="keyword">new</span> <span class="title class_">JacksonObjectMapper</span>());</span><br><span class="line">    <span class="comment">//将自己的消息转化器加入容器中</span></span><br><span class="line">    converters.add(<span class="number">0</span>,converter);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><br><div class="note info no-icon flat"><p>消息转化器配置中的对象转换器，是基础工程中已经配置好的<code>JacksonObjectMapper</code>，它已经对日期类型进行了格式处理，我们只需要在<code>WebMvcConfiguration</code>中扩展消息转换器，将其加入容器中即可</p></div></p><p>此时再次测试，就能看到正确的显示时间格式了<br><div class="img-wrap"><div class="img-bg"><img class="img" src="https://raw.githubusercontent.com/silvan2077/markdown_pic/main/Picgo/20251112145949.png"/></div></div></p><h2 id="启用-禁用员工账号"><a href="#启用-禁用员工账号" class="headerlink" title="启用/禁用员工账号"></a>启用/禁用员工账号</h2><h3 id="需求分析和设计-2"><a href="#需求分析和设计-2" class="headerlink" title="需求分析和设计"></a>需求分析和设计</h3><p><strong>需求分析：</strong> 在员工管理列表页面，可以对某个员工账号进行启用或者禁用操作。账号禁用的员工不能登录系统，启用后的员工可以正常登录。如果某个员工账号状态为正常，则按钮显示为 “禁用”，如果员工账号状态为已禁用，则按钮显示为”启用”，关于按钮的显示切换在前端已经完成，后端只需要改变员工账号的状态即可。</p><div class="img-wrap"><div class="img-bg"><img class="img" src="https://raw.githubusercontent.com/silvan2077/markdown_pic/main/Picgo/image-20221112112359233.png"/></div></div><div class="note info no-icon flat"><p><strong>业务规则：</strong></p><ul><li>可以对状态为“启用” 的员工账号进行“禁用”操作</li><li>可以对状态为“禁用”的员工账号进行“启用”操作</li><li>状态为“禁用”的员工账号不能登录系统</li></ul></div><p><strong>接口设计</strong><br><div class="img-wrap"><div class="img-bg"><img class="img" src="https://raw.githubusercontent.com/silvan2077/markdown_pic/main/Picgo/image-20221112112728333.png"/></div></div><br><div class="img-wrap"><div class="img-bg"><img class="img" src="https://raw.githubusercontent.com/silvan2077/markdown_pic/main/Picgo/image-20221112112739680.png"/></div></div></p><h3 id="代码开发-2"><a href="#代码开发-2" class="headerlink" title="代码开发"></a>代码开发</h3><h4 id="Controller层"><a href="#Controller层" class="headerlink" title="Controller层"></a>Controller层</h4><p>根据接口设计中的请求参数形式对应的在<code>EmployeeController</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></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">@param</span> status</span></span><br><span class="line"><span class="comment">    * <span class="doctag">@param</span> id</span></span><br><span class="line"><span class="comment">    * <span class="doctag">@return</span></span></span><br><span class="line"><span class="comment">    */</span></span><br><span class="line">   <span class="meta">@PostMapping(&quot;/status/&#123;status&#125;&quot;)</span></span><br><span class="line">   <span class="meta">@ApiOperation(&quot;启用禁用员工账号&quot;)</span></span><br><span class="line">   <span class="keyword">public</span> Result <span class="title function_">startOrStop</span><span class="params">(<span class="meta">@PathVariable</span> Integer status,Long id)</span>&#123;</span><br><span class="line">       log.info(<span class="string">&quot;启用禁用员工账号：&#123;&#125;,&#123;&#125;&quot;</span>,status,id);</span><br><span class="line">       employeeService.startOrStop(status,id);<span class="comment">//后绪步骤定义</span></span><br><span class="line">       <span class="keyword">return</span> Result.success();</span><br><span class="line">   &#125;</span><br></pre></td></tr></table></figure><h4 id="Service层接口"><a href="#Service层接口" class="headerlink" title="Service层接口"></a>Service层接口</h4><p>在<code>EmployeeService</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></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">@param</span> status</span></span><br><span class="line"><span class="comment">    * <span class="doctag">@param</span> id</span></span><br><span class="line"><span class="comment">    */</span></span><br><span class="line">   <span class="keyword">void</span> <span class="title function_">startOrStop</span><span class="params">(Integer status, Long id)</span>;</span><br></pre></td></tr></table></figure><h4 id="Service层实现类"><a href="#Service层实现类" class="headerlink" title="Service层实现类"></a>Service层实现类</h4><p>在<code>EmployeeServiceImpl</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></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">@param</span> status</span></span><br><span class="line"><span class="comment">    * <span class="doctag">@param</span> id</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_">startOrStop</span><span class="params">(Integer status, Long id)</span> &#123;</span><br><span class="line">       <span class="type">Employee</span> <span class="variable">employee</span> <span class="operator">=</span> Employee.builder()</span><br><span class="line">               .status(status)</span><br><span class="line">               .id(id)</span><br><span class="line">               .build();</span><br><span class="line"></span><br><span class="line">       employeeMapper.update(employee);</span><br><span class="line">   &#125;</span><br></pre></td></tr></table></figure><h4 id="Mapper层"><a href="#Mapper层" class="headerlink" title="Mapper层"></a>Mapper层</h4><p>在<code>EmployeeMapper</code>接口中声明 update 方法：</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></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">@param</span> employee</span></span><br><span class="line"><span class="comment">    */</span></span><br><span class="line">   <span class="keyword">void</span> <span class="title function_">update</span><span class="params">(Employee employee)</span>;</span><br></pre></td></tr></table></figure><p>在<code>EmployeeMapper.xml</code>中编写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><span class="line">10</span><br><span class="line">11</span><br><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="operator">&lt;</span><span class="keyword">update</span> id<span class="operator">=</span>&quot;update&quot; parameterType<span class="operator">=</span>&quot;Employee&quot;<span class="operator">&gt;</span></span><br><span class="line">    <span class="keyword">update</span> employee</span><br><span class="line">    <span class="operator">&lt;</span><span class="keyword">set</span><span class="operator">&gt;</span></span><br><span class="line">        <span class="operator">&lt;</span>if test<span class="operator">=</span>&quot;name != null&quot;<span class="operator">&gt;</span>name <span class="operator">=</span> #&#123;name&#125;,<span class="operator">&lt;</span><span class="operator">/</span>if<span class="operator">&gt;</span></span><br><span class="line">        <span class="operator">&lt;</span>if test<span class="operator">=</span>&quot;username != null&quot;<span class="operator">&gt;</span>username <span class="operator">=</span> #&#123;username&#125;,<span class="operator">&lt;</span><span class="operator">/</span>if<span class="operator">&gt;</span></span><br><span class="line">        <span class="operator">&lt;</span>if test<span class="operator">=</span>&quot;password != null&quot;<span class="operator">&gt;</span>password <span class="operator">=</span> #&#123;password&#125;,<span class="operator">&lt;</span><span class="operator">/</span>if<span class="operator">&gt;</span></span><br><span class="line">        <span class="operator">&lt;</span>if test<span class="operator">=</span>&quot;phone != null&quot;<span class="operator">&gt;</span>phone <span class="operator">=</span> #&#123;phone&#125;,<span class="operator">&lt;</span><span class="operator">/</span>if<span class="operator">&gt;</span></span><br><span class="line">        <span class="operator">&lt;</span>if test<span class="operator">=</span>&quot;sex != null&quot;<span class="operator">&gt;</span>sex <span class="operator">=</span> #&#123;sex&#125;,<span class="operator">&lt;</span><span class="operator">/</span>if<span class="operator">&gt;</span></span><br><span class="line">        <span class="operator">&lt;</span>if test<span class="operator">=</span>&quot;idNumber != null&quot;<span class="operator">&gt;</span>id_Number <span class="operator">=</span> #&#123;idNumber&#125;,<span class="operator">&lt;</span><span class="operator">/</span>if<span class="operator">&gt;</span></span><br><span class="line">        <span class="operator">&lt;</span>if test<span class="operator">=</span>&quot;updateTime != null&quot;<span class="operator">&gt;</span>update_Time <span class="operator">=</span> #&#123;updateTime&#125;,<span class="operator">&lt;</span><span class="operator">/</span>if<span class="operator">&gt;</span></span><br><span class="line">        <span class="operator">&lt;</span>if test<span class="operator">=</span>&quot;updateUser != null&quot;<span class="operator">&gt;</span>update_User <span class="operator">=</span> #&#123;updateUser&#125;,<span class="operator">&lt;</span><span class="operator">/</span>if<span class="operator">&gt;</span></span><br><span class="line">        <span class="operator">&lt;</span>if test<span class="operator">=</span>&quot;status != null&quot;<span class="operator">&gt;</span>status <span class="operator">=</span> #&#123;status&#125;,<span class="operator">&lt;</span><span class="operator">/</span>if<span class="operator">&gt;</span></span><br><span class="line">    <span class="operator">&lt;</span><span class="operator">/</span><span class="keyword">set</span><span class="operator">&gt;</span></span><br><span class="line">    <span class="keyword">where</span> id <span class="operator">=</span> #&#123;id&#125;</span><br><span class="line"><span class="operator">&lt;</span><span class="operator">/</span><span class="keyword">update</span><span class="operator">&gt;</span></span><br></pre></td></tr></table></figure><h3 id="功能测试-2"><a href="#功能测试-2" class="headerlink" title="功能测试"></a>功能测试</h3><h4 id="接口文档测试-2"><a href="#接口文档测试-2" class="headerlink" title="接口文档测试"></a>接口文档测试</h4><p>在测试前，查询员工id和账号状态，在接口文档中作为相应的参数进行测试，测试完毕后，再次查询员工账号状态，查看是否成功启用/禁用员工账号</p><div class="img-wrap"><div class="img-bg"><img class="img" src="https://raw.githubusercontent.com/silvan2077/markdown_pic/main/Picgo/20251112203047.png"/></div></div><h4 id="前后端联调测试-2"><a href="#前后端联调测试-2" class="headerlink" title="前后端联调测试"></a>前后端联调测试</h4><p>点击相应的按钮，会对员工账号进行启用/禁用，并提示账号状态是否更改成功<br><div class="img-wrap"><div class="img-bg"><img class="img" src="https://raw.githubusercontent.com/silvan2077/markdown_pic/main/Picgo/20251112202543.png"/></div></div></p><h2 id="编辑员工"><a href="#编辑员工" class="headerlink" title="编辑员工"></a>编辑员工</h2><h3 id="需求分析和设计-3"><a href="#需求分析和设计-3" class="headerlink" title="需求分析和设计"></a>需求分析和设计</h3><p><strong>需求分析：</strong> 员工管理界面点击编辑按钮即可跳转到员工编辑页面，页面中显示当前员工信息，用户可以修改员工信息并保存。</p><div class="img-wrap"><div class="img-bg"><img class="img" src="https://raw.githubusercontent.com/silvan2077/markdown_pic/main/Picgo/image-20221112144731759.png"/></div></div><div class="note warning no-icon flat"><p>员工编辑页面应当回显当前员工信息，用户在当前员工信息的基础上进行修改</p></div><p><strong>接口设计：</strong><br>编辑员工共涉及到两个接口：</p><ul><li>根据id查询员工信息</li><li>编辑员工信息</li></ul><ol><li><p><strong>根据id查询员工信息</strong></p><div class="img-wrap"><div class="img-bg"><img class="img" src="https://raw.githubusercontent.com/silvan2077/markdown_pic/main/Picgo/image-20221112145619775.png"/></div></div></li><li><p><strong>编辑员工信息</strong></p><div class="img-wrap"><div class="img-bg"><img class="img" src="https://raw.githubusercontent.com/silvan2077/markdown_pic/main/Picgo/image-20221112145643769.png"/></div></div><div class="img-wrap"><div class="img-bg"><img class="img" src="https://raw.githubusercontent.com/silvan2077/markdown_pic/main/Picgo/image-20221112145659035.png"/></div></div></li></ol><div class="note warning no-icon flat"><p>修改功能，所以请求方式可设置为POST</p></div><h3 id="代码开发-3"><a href="#代码开发-3" class="headerlink" title="代码开发"></a>代码开发</h3><h4 id="回显员工信息"><a href="#回显员工信息" class="headerlink" title="回显员工信息"></a>回显员工信息</h4><ol><li><strong>Controller层</strong><br>在 <code>EmployeeController</code> 中创建 getById 方法：<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><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"> * 根据id查询员工信息</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@param</span> id</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@return</span></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="meta">@ApiOperation(&quot;根据id查询员工信息&quot;)</span></span><br><span class="line"><span class="keyword">public</span> Result&lt;Employee&gt; <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="type">Employee</span> <span class="variable">employee</span> <span class="operator">=</span> employeeService.getById(id);</span><br><span class="line">    <span class="keyword">return</span> Result.success(employee);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></li><li><strong>Service层接口</strong><br>在 EmployeeService 接口中声明 getById 方法：<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><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="comment"> * 根据id查询员工</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@param</span> id</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@return</span></span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line">Employee <span class="title function_">getById</span><span class="params">(Long id)</span>;</span><br></pre></td></tr></table></figure></li><li><strong>Service层实现类</strong><br>在 <code>EmployeeServiceImpl</code> 中实现 getById 方法：<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><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">  * 根据id查询员工</span></span><br><span class="line"><span class="comment">  * <span class="doctag">@param</span> id</span></span><br><span class="line"><span class="comment">  * <span class="doctag">@return</span></span></span><br><span class="line"><span class="comment">  */</span></span><br><span class="line"><span class="keyword">public</span> Employee <span class="title function_">getById</span><span class="params">(Long id)</span> &#123;</span><br><span class="line">     <span class="type">Employee</span> <span class="variable">employee</span> <span class="operator">=</span> employeeMapper.getById(id);</span><br><span class="line">     employee.setPassword(<span class="string">&quot;****&quot;</span>);</span><br><span class="line">     <span class="keyword">return</span> employee;</span><br><span class="line"> &#125;</span><br></pre></td></tr></table></figure></li><li><strong>Mapper层</strong><br>在 EmployeeMapper 接口中声明 getById 方法：<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><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="comment">    * 根据id查询员工信息</span></span><br><span class="line"><span class="comment">    * <span class="doctag">@param</span> id</span></span><br><span class="line"><span class="comment">    * <span class="doctag">@return</span></span></span><br><span class="line"><span class="comment">    */</span></span><br><span class="line">   <span class="meta">@Select(&quot;select * from employee where id = #&#123;id&#125;&quot;)</span></span><br><span class="line">   Employee <span class="title function_">getById</span><span class="params">(Long id)</span>;</span><br></pre></td></tr></table></figure></li></ol><h4 id="修改员工信息功能"><a href="#修改员工信息功能" class="headerlink" title="修改员工信息功能"></a>修改员工信息功能</h4><ol><li><p><strong>Controller层</strong><br>在 <code>EmployeeController</code> 中创建 <code>update</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></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">@param</span> employeeDTO</span></span><br><span class="line"><span class="comment">    * <span class="doctag">@return</span></span></span><br><span class="line"><span class="comment">    */</span></span><br><span class="line">   <span class="meta">@PutMapping</span></span><br><span class="line">   <span class="meta">@ApiOperation(&quot;编辑员工信息&quot;)</span></span><br><span class="line">   <span class="keyword">public</span> Result <span class="title function_">update</span><span class="params">(<span class="meta">@RequestBody</span> EmployeeDTO employeeDTO)</span>&#123;</span><br><span class="line">       log.info(<span class="string">&quot;编辑员工信息：&#123;&#125;&quot;</span>, employeeDTO);</span><br><span class="line">       employeeService.update(employeeDTO);</span><br><span class="line">       <span class="keyword">return</span> Result.success();</span><br><span class="line">   &#125;</span><br></pre></td></tr></table></figure></li><li><p><strong>Service层接口</strong><br>在 <code>EmployeeService</code> 接口中声明 <code>update</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></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">@param</span> employeeDTO</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="keyword">void</span> <span class="title function_">update</span><span class="params">(EmployeeDTO employeeDTO)</span>;</span><br></pre></td></tr></table></figure></li><li><p><strong>Service层实现类</strong><br>在 <code>EmployeeServiceImpl</code> 中实现 <code>update</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></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">@param</span> employeeDTO</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> <span class="keyword">void</span> <span class="title function_">update</span><span class="params">(EmployeeDTO employeeDTO)</span> &#123;</span><br><span class="line">      <span class="type">Employee</span> <span class="variable">employee</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">Employee</span>();</span><br><span class="line">      BeanUtils.copyProperties(employeeDTO, employee);</span><br><span class="line"></span><br><span class="line">      employee.setUpdateTime(LocalDateTime.now());</span><br><span class="line">      employee.setUpdateUser(BaseContext.getCurrentId());</span><br><span class="line"></span><br><span class="line">      employeeMapper.update(employee);</span><br><span class="line">  &#125;</span><br></pre></td></tr></table></figure></li></ol><div class="note info no-icon flat"><p>在实现<strong>启用禁用员工账号</strong>功能时，已实现employeeMapper.update(employee)，在此不需写Mapper层代码,直接调用即可</p></div><h3 id="功能测试-3"><a href="#功能测试-3" class="headerlink" title="功能测试"></a>功能测试</h3><h4 id="接口文档测试-3"><a href="#接口文档测试-3" class="headerlink" title="接口文档测试"></a>接口文档测试</h4><p>分别测试<code>根据id查询员工信息</code>和<code>编辑员工信息接口</code></p><ol><li><p>根据id查询员工信息<br>获得了对应id的相关员工信息</p><div class="img-wrap"><div class="img-bg"><img class="img" src="https://raw.githubusercontent.com/silvan2077/markdown_pic/main/Picgo/20251112205954.png"/></div></div></li><li><p>编辑员工信息<br>修改id=4的员工信息，<strong>name</strong>由<strong>zhangsan</strong>改为<strong>张三丰</strong>，<strong>username</strong>由<strong>张三</strong>改为<strong>zhangsanfeng</strong>。</p></li></ol><div class="img-wrap"><div class="img-bg"><img class="img" src="https://raw.githubusercontent.com/silvan2077/markdown_pic/main/Picgo/image-20221112155001414.png"/></div></div><p>查看employee表数据<br><div class="img-wrap"><div class="img-bg"><img class="img" src="https://raw.githubusercontent.com/silvan2077/markdown_pic/main/Picgo/image-20221112155029547.png"/></div></div></p><h4 id="前后端联调测试-3"><a href="#前后端联调测试-3" class="headerlink" title="前后端联调测试"></a>前后端联调测试</h4><p>进入到员工列表查询<br><div class="img-wrap"><div class="img-bg"><img class="img" src="https://raw.githubusercontent.com/silvan2077/markdown_pic/main/Picgo/image-20221112155430652.png"/></div></div><br>对员工姓名为杰克的员工数据修改，点击修改，数据已回显<br><div class="img-wrap"><div class="img-bg"><img class="img" src="https://raw.githubusercontent.com/silvan2077/markdown_pic/main/Picgo/image-20221112155206712.png"/></div></div><br>修改后，点击保存<br><div class="img-wrap"><div class="img-bg"><img class="img" src="https://raw.githubusercontent.com/silvan2077/markdown_pic/main/Picgo/image-20221112155559298.png"/></div></div></p><h2 id="导入分类模块功能代码"><a href="#导入分类模块功能代码" class="headerlink" title="导入分类模块功能代码"></a>导入分类模块功能代码</h2><h3 id="需求分析和设计-4"><a href="#需求分析和设计-4" class="headerlink" title="需求分析和设计"></a>需求分析和设计</h3><h4 id="需求分析"><a href="#需求分析" class="headerlink" title="需求分析"></a>需求分析</h4><p>后台系统中可以管理分类信息，分类包括两种类型，分别是 <strong>菜品分类</strong> 和 <strong>套餐分类</strong> 。</p><p><strong>分析菜品分类相关功能：</strong></p><ul><li><p><strong>新增菜品分类：</strong>当我们在后台系统中添加菜品时需要选择一个菜品分类，在移动端也会按照菜品分类来展示对应的菜品。</p></li><li><p><strong>菜品分类分页查询：</strong>系统中的分类很多的时候，如果在一个页面中全部展示出来会显得比较乱，不便于查看，所以一般的系统中都会以分页的方式来展示列表数据。</p></li><li><p><strong>根据id删除菜品分类：</strong>在分类管理列表页面，可以对某个分类进行删除操作。需要注意的是当分类关联了菜品或者套餐时，此分类不允许删除。</p></li><li><p><strong>修改菜品分类：</strong>在分类管理列表页面点击修改按钮，弹出修改窗口，在修改窗口回显分类信息并进行修改，最后点击确定按钮完成修改操作。</p></li><li><p><strong>启用禁用菜品分类：</strong>在分类管理列表页面，可以对某个分类进行启用或者禁用操作。</p></li><li><p><strong>分类类型查询：</strong>当点击分类类型下拉框时，从数据库中查询所有的菜品分类数据进行展示。</p></li></ul><p><strong>分类管理原型：</strong><br><div class="img-wrap"><div class="img-bg"><img class="img" src="https://raw.githubusercontent.com/silvan2077/markdown_pic/main/Picgo/image-20221112160907419.png"/></div></div></p><div class="note warning no-icon flat"><p><strong>业务规则：</strong></p><ul><li>分类名称必须是唯一的</li><li>分类按照类型可以分为菜品分类和套餐分类</li><li>新添加的分类状态默认为“禁用”</li></ul></div><h4 id="接口设计"><a href="#接口设计" class="headerlink" title="接口设计"></a>接口设计</h4><p>根据上述分析，菜品分类模块共涉及6个接口：</p><ul><li>新增分类</li><li>分类分页查询</li><li>根据id删除分类</li><li>修改分类</li><li>启用禁用分类</li><li>根据类型查询分类</li></ul><ol><li><strong>新增分类</strong><div class="tabs" id="导入分类新增分类"><ul class="nav-tabs"><li class="tab active"><button type="button" data-href="#导入分类新增分类-1">基本信息</button></li><li class="tab"><button type="button" data-href="#导入分类新增分类-2">返回数据</button></li></ul><div class="tab-contents"><div class="tab-item-content active" id="导入分类新增分类-1"><div class="img-wrap"><div class="img-bg"><img class="img" src="https://raw.githubusercontent.com/silvan2077/markdown_pic1/main/Picgo/image-20221112163011291.png"/></div></div><button type="button" class="tab-to-top" aria-label="scroll to top"><i class="fas fa-arrow-up"></i></button></div><div class="tab-item-content" id="导入分类新增分类-2"><div class="img-wrap"><div class="img-bg"><img class="img" src="https://raw.githubusercontent.com/silvan2077/markdown_pic1/main/Picgo/image-20221112163044547.png"/></div></div><button type="button" class="tab-to-top" aria-label="scroll to top"><i class="fas fa-arrow-up"></i></button></div></div></div></li><li><strong>分类分页查询</strong><div class="tabs" id="导入分类分类分页查询"><ul class="nav-tabs"><li class="tab active"><button type="button" data-href="#导入分类分类分页查询-1">基本信息</button></li><li class="tab"><button type="button" data-href="#导入分类分类分页查询-2">返回数据</button></li></ul><div class="tab-contents"><div class="tab-item-content active" id="导入分类分类分页查询-1"><div class="img-wrap"><div class="img-bg"><img class="img" src="https://raw.githubusercontent.com/silvan2077/markdown_pic1/main/Picgo/image-20221112163209383.png"/></div></div><button type="button" class="tab-to-top" aria-label="scroll to top"><i class="fas fa-arrow-up"></i></button></div><div class="tab-item-content" id="导入分类分类分页查询-2"><div class="img-wrap"><div class="img-bg"><img class="img" src="https://raw.githubusercontent.com/silvan2077/markdown_pic1/main/Picgo/image-20221112163233212.png"/></div></div><button type="button" class="tab-to-top" aria-label="scroll to top"><i class="fas fa-arrow-up"></i></button></div></div></div></li><li><strong>根据id删除分类</strong></li></ol><div class="img-wrap"><div class="img-bg"><img class="img" src="https://raw.githubusercontent.com/silvan2077/markdown_pic1/main/Picgo/image-20221112163323554.png"/></div></div><ol><li><strong>修改分类</strong><div class="tabs" id="导入分类修改分类"><ul class="nav-tabs"><li class="tab active"><button type="button" data-href="#导入分类修改分类-1">基本信息</button></li><li class="tab"><button type="button" data-href="#导入分类修改分类-2">返回数据</button></li></ul><div class="tab-contents"><div class="tab-item-content active" id="导入分类修改分类-1"><div class="img-wrap"><div class="img-bg"><img class="img" src="https://raw.githubusercontent.com/silvan2077/markdown_pic1/main/Picgo/image-20221112163424457.png"/></div></div><button type="button" class="tab-to-top" aria-label="scroll to top"><i class="fas fa-arrow-up"></i></button></div><div class="tab-item-content" id="导入分类修改分类-2"><div class="img-wrap"><div class="img-bg"><img class="img" src="https://raw.githubusercontent.com/silvan2077/markdown_pic1/main/Picgo/image-20221112163445296.png"/></div></div><button type="button" class="tab-to-top" aria-label="scroll to top"><i class="fas fa-arrow-up"></i></button></div></div></div></li><li><strong>启用禁用分类</strong><div class="tabs" id="导入分类启用禁用分类"><ul class="nav-tabs"><li class="tab active"><button type="button" data-href="#导入分类启用禁用分类-1">基本信息</button></li><li class="tab"><button type="button" data-href="#导入分类启用禁用分类-2">返回数据</button></li></ul><div class="tab-contents"><div class="tab-item-content active" id="导入分类启用禁用分类-1"><div class="img-wrap"><div class="img-bg"><img class="img" src="https://raw.githubusercontent.com/silvan2077/markdown_pic1/main/Picgo/image-20221112163557247.png"/></div></div><button type="button" class="tab-to-top" aria-label="scroll to top"><i class="fas fa-arrow-up"></i></button></div><div class="tab-item-content" id="导入分类启用禁用分类-2"><div class="img-wrap"><div class="img-bg"><img class="img" src="https://raw.githubusercontent.com/silvan2077/markdown_pic1/main/Picgo/image-20221112163622896.png"/></div></div><button type="button" class="tab-to-top" aria-label="scroll to top"><i class="fas fa-arrow-up"></i></button></div></div></div></li><li><strong>根据类型查询分类</strong><div class="tabs" id="导入分类根据类型查询分类"><ul class="nav-tabs"><li class="tab active"><button type="button" data-href="#导入分类根据类型查询分类-1">基本信息</button></li><li class="tab"><button type="button" data-href="#导入分类根据类型查询分类-2">返回数据</button></li></ul><div class="tab-contents"><div class="tab-item-content active" id="导入分类根据类型查询分类-1"><div class="img-wrap"><div class="img-bg"><img class="img" src="https://raw.githubusercontent.com/silvan2077/markdown_pic1/main/Picgo/image-20221112163806318.png"/></div></div><button type="button" class="tab-to-top" aria-label="scroll to top"><i class="fas fa-arrow-up"></i></button></div><div class="tab-item-content" id="导入分类根据类型查询分类-2"><div class="img-wrap"><div class="img-bg"><img class="img" src="https://raw.githubusercontent.com/silvan2077/markdown_pic1/main/Picgo/image-20221112163839168.png"/></div></div><button type="button" class="tab-to-top" aria-label="scroll to top"><i class="fas fa-arrow-up"></i></button></div></div></div><h4 id="表设计"><a href="#表设计" class="headerlink" title="表设计"></a>表设计</h4><strong>category表结构：</strong></li></ol><div class="table-container"><table><thead><tr><th><strong>字段名</strong></th><th><strong>数据类型</strong></th><th><strong>说明</strong></th><th><strong>备注</strong></th></tr></thead><tbody><tr><td>id</td><td>bigint</td><td>主键</td><td>自增</td></tr><tr><td>name</td><td>varchar(32)</td><td>分类名称</td><td>唯一</td></tr><tr><td>type</td><td>int</td><td>分类类型</td><td>1菜品分类 2套餐分类</td></tr><tr><td>sort</td><td>int</td><td>排序字段</td><td>用于分类数据的排序</td></tr><tr><td>status</td><td>int</td><td>状态</td><td>1启用 0禁用</td></tr><tr><td>create_time</td><td>datetime</td><td>创建时间</td><td></td></tr><tr><td>update_time</td><td>datetime</td><td>最后修改时间</td><td></td></tr><tr><td>create_user</td><td>bigint</td><td>创建人id</td><td></td></tr><tr><td>update_user</td><td>bigint</td><td>最后修改人id</td></tr></tbody></table></div><h3 id="代码导入"><a href="#代码导入" class="headerlink" title="代码导入"></a>代码导入</h3><div class="note info no-icon flat"><p>直接将资料中的分类管理模块功能代码导入即可</p></div><h4 id="Mapper层-1"><a href="#Mapper层-1" class="headerlink" title="Mapper层"></a>Mapper层</h4><div class="tabs" id="导入分类mapper层"><ul class="nav-tabs"><li class="tab active"><button type="button" data-href="#导入分类mapper层-1">CategoryMapper.java</button></li><li class="tab"><button type="button" data-href="#导入分类mapper层-2">DishMapper.java</button></li><li class="tab"><button type="button" data-href="#导入分类mapper层-3">SetmealMapper.java</button></li></ul><div class="tab-contents"><div class="tab-item-content active" id="导入分类mapper层-1"><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><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">@Mapper</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">interface</span> <span class="title class_">CategoryMapper</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 class="doctag">@param</span> category</span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    <span class="meta">@Insert(&quot;insert into category(type, name, sort, status, create_time, update_time, create_user, update_user)&quot; +</span></span><br><span class="line"><span class="meta">            &quot; VALUES&quot; +</span></span><br><span class="line"><span class="meta">            &quot; (#&#123;type&#125;, #&#123;name&#125;, #&#123;sort&#125;, #&#123;status&#125;, #&#123;createTime&#125;, #&#123;updateTime&#125;, #&#123;createUser&#125;, #&#123;updateUser&#125;)&quot;)</span></span><br><span class="line">    <span class="keyword">void</span> <span class="title function_">insert</span><span class="params">(Category category)</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 class="doctag">@param</span> categoryPageQueryDTO</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@return</span></span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    Page&lt;Category&gt; <span class="title function_">pageQuery</span><span class="params">(CategoryPageQueryDTO categoryPageQueryDTO)</span>;</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 class="doctag">@param</span> id</span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    <span class="meta">@Delete(&quot;delete from category where id = #&#123;id&#125;&quot;)</span></span><br><span class="line">    <span class="keyword">void</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">     * 根据id修改分类</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@param</span> category</span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    <span class="keyword">void</span> <span class="title function_">update</span><span class="params">(Category category)</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 class="doctag">@param</span> type</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@return</span></span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    List&lt;Category&gt; <span class="title function_">list</span><span class="params">(Integer type)</span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><button type="button" class="tab-to-top" aria-label="scroll to top"><i class="fas fa-arrow-up"></i></button></div><div class="tab-item-content" id="导入分类mapper层-2"><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><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><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_">DishMapper</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 class="doctag">@param</span> categoryId</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@return</span></span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    <span class="meta">@Select(&quot;select count(id) from dish where category_id = #&#123;categoryId&#125;&quot;)</span></span><br><span class="line">    Integer <span class="title function_">countByCategoryId</span><span class="params">(Long categoryId)</span>;</span><br><span class="line"></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><button type="button" class="tab-to-top" aria-label="scroll to top"><i class="fas fa-arrow-up"></i></button></div><div class="tab-item-content" id="导入分类mapper层-3"><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><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">@Mapper</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">interface</span> <span class="title class_">SetmealMapper</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 class="doctag">@param</span> id</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@return</span></span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    <span class="meta">@Select(&quot;select count(id) from setmeal where category_id = #&#123;categoryId&#125;&quot;)</span></span><br><span class="line">    Integer <span class="title function_">countByCategoryId</span><span class="params">(Long id)</span>;</span><br><span class="line"></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><button type="button" class="tab-to-top" aria-label="scroll to top"><i class="fas fa-arrow-up"></i></button></div></div></div><h4 id="Service层"><a href="#Service层" class="headerlink" title="Service层"></a>Service层</h4><p><strong>接口类</strong><br><div class="tabs" id="导入分类service层"><ul class="nav-tabs"><li class="tab active"><button type="button" data-href="#导入分类service层-1">CategoryService.java</button></li><li class="tab"><button type="button" data-href="#导入分类service层-2">EmployeeService.java</button></li></ul><div class="tab-contents"><div class="tab-item-content active" id="导入分类service层-1"><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><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">interface</span> <span class="title class_">CategoryService</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 class="doctag">@param</span> categoryDTO</span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    <span class="keyword">void</span> <span class="title function_">save</span><span class="params">(CategoryDTO categoryDTO)</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 class="doctag">@param</span> categoryPageQueryDTO</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@return</span></span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    PageResult <span class="title function_">pageQuery</span><span class="params">(CategoryPageQueryDTO categoryPageQueryDTO)</span>;</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 class="doctag">@param</span> id</span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    <span class="keyword">void</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 class="doctag">@param</span> categoryDTO</span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    <span class="keyword">void</span> <span class="title function_">update</span><span class="params">(CategoryDTO categoryDTO)</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 class="doctag">@param</span> status</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@param</span> id</span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    <span class="keyword">void</span> <span class="title function_">startOrStop</span><span class="params">(Integer status, 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 class="doctag">@param</span> type</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@return</span></span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    List&lt;Category&gt; <span class="title function_">list</span><span class="params">(Integer type)</span>;</span><br><span class="line">&#125;</span><br><span class="line"></span><br></pre></td></tr></table></figure><button type="button" class="tab-to-top" aria-label="scroll to top"><i class="fas fa-arrow-up"></i></button></div><div class="tab-item-content" id="导入分类service层-2"><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><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="keyword">public</span> <span class="keyword">interface</span> <span class="title class_">EmployeeService</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 class="doctag">@param</span> employeeLoginDTO</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@return</span></span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    Employee <span class="title function_">login</span><span class="params">(EmployeeLoginDTO employeeLoginDTO)</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 class="doctag">@param</span> employeeDTO</span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    <span class="keyword">void</span> <span class="title function_">save</span><span class="params">(EmployeeDTO employeeDTO)</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 class="doctag">@param</span> employeePageQueryDTO</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@return</span></span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    PageResult <span class="title function_">pageQuery</span><span class="params">(EmployeePageQueryDTO employeePageQueryDTO)</span>;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">void</span> <span class="title function_">startOrStop</span><span class="params">(Integer status, Long id)</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"><span class="comment">     * 编辑员工信息</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@param</span> employeeDTO</span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    <span class="keyword">void</span> <span class="title function_">update</span><span class="params">(EmployeeDTO employeeDTO)</span>;</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 class="doctag">@param</span> id</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@return</span></span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    Employee <span class="title function_">getById</span><span class="params">(Long id)</span>;</span><br><span class="line">&#125;</span><br><span class="line"></span><br></pre></td></tr></table></figure><button type="button" class="tab-to-top" aria-label="scroll to top"><i class="fas fa-arrow-up"></i></button></div></div></div><br><strong>实现类</strong><br><div class="tabs" id="导入分类service层实现类"><ul class="nav-tabs"><li class="tab active"><button type="button" data-href="#导入分类service层实现类-1">CategoryServiceImpl.java</button></li><li class="tab"><button type="button" data-href="#导入分类service层实现类-2">EmployeeServiceImpl.java</button></li></ul><div class="tab-contents"><div class="tab-item-content active" id="导入分类service层实现类-1"><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><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="meta">@Service</span></span><br><span class="line"><span class="meta">@Slf4j</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">CategoryServiceImpl</span> <span class="keyword">implements</span> <span class="title class_">CategoryService</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> CategoryMapper categoryMapper;</span><br><span class="line">    <span class="meta">@Autowired</span></span><br><span class="line">    <span class="keyword">private</span> DishMapper dishMapper;</span><br><span class="line">    <span class="meta">@Autowired</span></span><br><span class="line">    <span class="keyword">private</span> SetmealMapper setmealMapper;</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 class="doctag">@param</span> categoryDTO</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> <span class="keyword">void</span> <span class="title function_">save</span><span class="params">(CategoryDTO categoryDTO)</span> &#123;</span><br><span class="line">        <span class="type">Category</span> <span class="variable">category</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">Category</span>();</span><br><span class="line">        <span class="comment">//属性拷贝</span></span><br><span class="line">        BeanUtils.copyProperties(categoryDTO, category);</span><br><span class="line"></span><br><span class="line">        <span class="comment">//分类状态默认为禁用状态0</span></span><br><span class="line">        category.setStatus(StatusConstant.DISABLE);</span><br><span class="line"></span><br><span class="line">        <span class="comment">//设置创建时间、修改时间、创建人、修改人</span></span><br><span class="line">        category.setCreateTime(LocalDateTime.now());</span><br><span class="line">        category.setUpdateTime(LocalDateTime.now());</span><br><span class="line">        category.setCreateUser(BaseContext.getCurrentId());</span><br><span class="line">        category.setUpdateUser(BaseContext.getCurrentId());</span><br><span class="line"></span><br><span class="line">        categoryMapper.insert(category);</span><br><span 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 class="doctag">@param</span> categoryPageQueryDTO</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@return</span></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> PageResult <span class="title function_">pageQuery</span><span class="params">(CategoryPageQueryDTO categoryPageQueryDTO)</span> &#123;</span><br><span class="line">        PageHelper.startPage(categoryPageQueryDTO.getPage(),categoryPageQueryDTO.getPageSize());</span><br><span class="line">        <span class="comment">//下一条sql进行分页，自动加入limit关键字分页</span></span><br><span class="line">        Page&lt;Category&gt; page = categoryMapper.pageQuery(categoryPageQueryDTO);</span><br><span class="line">        <span class="keyword">return</span> <span class="keyword">new</span> <span class="title class_">PageResult</span>(page.getTotal(), page.getResult());</span><br><span 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 class="doctag">@param</span> id</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> <span class="keyword">void</span> <span class="title function_">deleteById</span><span class="params">(Long id)</span> &#123;</span><br><span class="line">        <span class="comment">//查询当前分类是否关联了菜品，如果关联了就抛出业务异常</span></span><br><span class="line">        <span class="type">Integer</span> <span class="variable">count</span> <span class="operator">=</span> dishMapper.countByCategoryId(id);</span><br><span class="line">        <span class="keyword">if</span>(count &gt; <span class="number">0</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_">DeletionNotAllowedException</span>(MessageConstant.CATEGORY_BE_RELATED_BY_DISH);</span><br><span class="line">        &#125;</span><br><span class="line"></span><br><span class="line">        <span class="comment">//查询当前分类是否关联了套餐，如果关联了就抛出业务异常</span></span><br><span class="line">        count = setmealMapper.countByCategoryId(id);</span><br><span class="line">        <span class="keyword">if</span>(count &gt; <span class="number">0</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_">DeletionNotAllowedException</span>(MessageConstant.CATEGORY_BE_RELATED_BY_SETMEAL);</span><br><span class="line">        &#125;</span><br><span class="line"></span><br><span class="line">        <span class="comment">//删除分类数据</span></span><br><span class="line">        categoryMapper.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="comment">     * 修改分类</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@param</span> categoryDTO</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> <span class="keyword">void</span> <span class="title function_">update</span><span class="params">(CategoryDTO categoryDTO)</span> &#123;</span><br><span class="line">        <span class="type">Category</span> <span class="variable">category</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">Category</span>();</span><br><span class="line">        BeanUtils.copyProperties(categoryDTO,category);</span><br><span class="line"></span><br><span class="line">        <span class="comment">//设置修改时间、修改人</span></span><br><span class="line">        category.setUpdateTime(LocalDateTime.now());</span><br><span class="line">        category.setUpdateUser(BaseContext.getCurrentId());</span><br><span class="line"></span><br><span class="line">        categoryMapper.update(category);</span><br><span 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 class="doctag">@param</span> status</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@param</span> id</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> <span class="keyword">void</span> <span class="title function_">startOrStop</span><span class="params">(Integer status, Long id)</span> &#123;</span><br><span class="line">        <span class="type">Category</span> <span class="variable">category</span> <span class="operator">=</span> Category.builder()</span><br><span class="line">                .id(id)</span><br><span class="line">                .status(status)</span><br><span class="line">                .updateTime(LocalDateTime.now())</span><br><span class="line">                .updateUser(BaseContext.getCurrentId())</span><br><span class="line">                .build();</span><br><span class="line">        categoryMapper.update(category);</span><br><span 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 class="doctag">@param</span> type</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@return</span></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> List&lt;Category&gt; <span class="title function_">list</span><span class="params">(Integer type)</span> &#123;</span><br><span class="line">        <span class="keyword">return</span> categoryMapper.list(type);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><button type="button" class="tab-to-top" aria-label="scroll to top"><i class="fas fa-arrow-up"></i></button></div><div class="tab-item-content" id="导入分类service层实现类-2"><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><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></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_">EmployeeServiceImpl</span> <span class="keyword">implements</span> <span class="title class_">EmployeeService</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> EmployeeMapper employeeMapper;</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">@param</span> employeeLoginDTO</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@return</span></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> Employee <span class="title function_">login</span><span class="params">(EmployeeLoginDTO employeeLoginDTO)</span> &#123;</span><br><span class="line">        <span class="type">String</span> <span class="variable">username</span> <span class="operator">=</span> employeeLoginDTO.getUsername();</span><br><span class="line">        <span class="type">String</span> <span class="variable">password</span> <span class="operator">=</span> employeeLoginDTO.getPassword();</span><br><span class="line"></span><br><span class="line">        <span class="comment">//1、根据用户名查询数据库中的数据</span></span><br><span class="line">        <span class="type">Employee</span> <span class="variable">employee</span> <span class="operator">=</span> employeeMapper.getByUsername(username);</span><br><span class="line"></span><br><span class="line">        <span class="comment">//2、处理各种异常情况（用户名不存在、密码不对、账号被锁定）</span></span><br><span class="line">        <span class="keyword">if</span> (employee == <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_">AccountNotFoundException</span>(MessageConstant.ACCOUNT_NOT_FOUND);</span><br><span 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">// TODO 后期需要进行md5加密，然后再进行比对</span></span><br><span class="line">        password = DigestUtils.md5DigestAsHex(password.getBytes());</span><br><span class="line">        <span class="keyword">if</span> (!password.equals(employee.getPassword())) &#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_">PasswordErrorException</span>(MessageConstant.PASSWORD_ERROR);</span><br><span class="line">        &#125;</span><br><span class="line"></span><br><span class="line">        <span class="keyword">if</span> (employee.getStatus().equals(StatusConstant.DISABLE)) &#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_">AccountLockedException</span>(MessageConstant.ACCOUNT_LOCKED);</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">return</span> employee;</span><br><span 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 class="doctag">@param</span> employeeDTO</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> <span class="keyword">void</span> <span class="title function_">save</span><span class="params">(EmployeeDTO employeeDTO)</span> &#123;</span><br><span class="line">        <span class="type">Employee</span> <span class="variable">employee</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">Employee</span>();</span><br><span class="line"></span><br><span class="line">        <span class="comment">//对象属性拷贝</span></span><br><span class="line">        BeanUtils.copyProperties(employeeDTO, employee);</span><br><span class="line"></span><br><span class="line">        <span class="comment">//设置账号的状态，默认正常状态 1表示正常 0表示锁定</span></span><br><span class="line">        employee.setStatus(StatusConstant.ENABLE);</span><br><span class="line"></span><br><span class="line">        <span class="comment">//设置密码，默认密码123456</span></span><br><span class="line">        employee.setPassword(DigestUtils.md5DigestAsHex(PasswordConstant.DEFAULT_PASSWORD.getBytes()));</span><br><span class="line"></span><br><span class="line">        <span class="comment">//设置当前记录的创建时间和修改时间</span></span><br><span class="line">        employee.setCreateTime(LocalDateTime.now());</span><br><span class="line">        employee.setUpdateTime(LocalDateTime.now());</span><br><span class="line"></span><br><span class="line">        <span class="comment">//设置当前记录创建人id和修改人id</span></span><br><span class="line">        employee.setCreateUser(BaseContext.getCurrentId());<span class="comment">//目前写个假数据，后期修改</span></span><br><span class="line">        employee.setUpdateUser(BaseContext.getCurrentId());</span><br><span class="line"></span><br><span class="line">        employeeMapper.insert(employee);</span><br><span 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 class="doctag">@param</span> employeePageQueryDTO</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@return</span></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> PageResult <span class="title function_">pageQuery</span><span class="params">(EmployeePageQueryDTO employeePageQueryDTO)</span> &#123;</span><br><span class="line">        <span class="comment">// select * from employee limit 0,10</span></span><br><span class="line">        <span class="comment">//开始分页查询</span></span><br><span class="line">        PageHelper.startPage(employeePageQueryDTO.getPage(), employeePageQueryDTO.getPageSize());</span><br><span class="line"></span><br><span class="line">        Page&lt;Employee&gt; page = employeeMapper.pageQuery(employeePageQueryDTO);</span><br><span class="line"></span><br><span class="line">        <span class="type">long</span> <span class="variable">total</span> <span class="operator">=</span> page.getTotal();</span><br><span class="line">        List&lt;Employee&gt; records = page.getResult();</span><br><span class="line"></span><br><span class="line">        <span class="keyword">return</span> <span class="keyword">new</span> <span class="title class_">PageResult</span>(total, records);</span><br><span 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 class="doctag">@param</span> status</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@param</span> id</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> <span class="keyword">void</span> <span class="title function_">startOrStop</span><span class="params">(Integer status, Long id)</span> &#123;</span><br><span class="line">        <span class="type">Employee</span> <span class="variable">employee</span> <span class="operator">=</span> Employee.builder()</span><br><span class="line">                .status(status)</span><br><span class="line">                .id(id)</span><br><span class="line">                .build();</span><br><span class="line"></span><br><span class="line">        employeeMapper.update(employee);</span><br><span 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="comment">     * <span class="doctag">@param</span> id</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@return</span></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> Employee <span class="title function_">getById</span><span class="params">(Long id)</span> &#123;</span><br><span class="line">        <span class="type">Employee</span> <span class="variable">employee</span> <span class="operator">=</span> employeeMapper.getById(id);</span><br><span class="line">        employee.setPassword(<span class="string">&quot;****&quot;</span>);</span><br><span class="line">        <span class="keyword">return</span> employee;</span><br><span 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 class="doctag">@param</span> employeeDTO</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> <span class="keyword">void</span> <span class="title function_">update</span><span class="params">(EmployeeDTO employeeDTO)</span> &#123;</span><br><span class="line">        <span class="type">Employee</span> <span class="variable">employee</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">Employee</span>();</span><br><span class="line">        BeanUtils.copyProperties(employeeDTO, employee);</span><br><span class="line"></span><br><span class="line">        employee.setUpdateTime(LocalDateTime.now());</span><br><span class="line">        employee.setUpdateUser(BaseContext.getCurrentId());</span><br><span class="line"></span><br><span class="line">        employeeMapper.update(employee);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><button type="button" class="tab-to-top" aria-label="scroll to top"><i class="fas fa-arrow-up"></i></button></div></div></div></p><h4 id="Controller层-1"><a href="#Controller层-1" class="headerlink" title="Controller层"></a>Controller层</h4><p><strong>CategoryController.java</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></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;/admin/category&quot;)</span></span><br><span class="line"><span class="meta">@Api(tags = &quot;分类相关接口&quot;)</span></span><br><span class="line"><span class="meta">@Slf4j</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">CategoryController</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> CategoryService categoryService;</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 class="doctag">@param</span> categoryDTO</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@return</span></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="meta">@ApiOperation(&quot;新增分类&quot;)</span></span><br><span class="line">    <span class="keyword">public</span> Result&lt;String&gt; <span class="title function_">save</span><span class="params">(<span class="meta">@RequestBody</span> CategoryDTO categoryDTO)</span>&#123;</span><br><span class="line">        log.info(<span class="string">&quot;新增分类：&#123;&#125;&quot;</span>, categoryDTO);</span><br><span class="line">        categoryService.save(categoryDTO);</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">     * 分类分页查询</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@param</span> categoryPageQueryDTO</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@return</span></span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    <span class="meta">@GetMapping(&quot;/page&quot;)</span></span><br><span class="line">    <span class="meta">@ApiOperation(&quot;分类分页查询&quot;)</span></span><br><span class="line">    <span class="keyword">public</span> Result&lt;PageResult&gt; <span class="title function_">page</span><span class="params">(CategoryPageQueryDTO categoryPageQueryDTO)</span>&#123;</span><br><span class="line">        log.info(<span class="string">&quot;分页查询：&#123;&#125;&quot;</span>, categoryPageQueryDTO);</span><br><span class="line">        <span class="type">PageResult</span> <span class="variable">pageResult</span> <span class="operator">=</span> categoryService.pageQuery(categoryPageQueryDTO);</span><br><span class="line">        <span class="keyword">return</span> Result.success(pageResult);</span><br><span 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 class="doctag">@param</span> id</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@return</span></span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    <span class="meta">@DeleteMapping</span></span><br><span class="line">    <span class="meta">@ApiOperation(&quot;删除分类&quot;)</span></span><br><span class="line">    <span class="keyword">public</span> Result&lt;String&gt; <span class="title function_">deleteById</span><span class="params">(Long id)</span>&#123;</span><br><span class="line">        log.info(<span class="string">&quot;删除分类：&#123;&#125;&quot;</span>, id);</span><br><span class="line">        categoryService.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"></span><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">@param</span> categoryDTO</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@return</span></span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    <span class="meta">@PutMapping</span></span><br><span class="line">    <span class="meta">@ApiOperation(&quot;修改分类&quot;)</span></span><br><span class="line">    <span class="keyword">public</span> Result&lt;String&gt; <span class="title function_">update</span><span class="params">(<span class="meta">@RequestBody</span> CategoryDTO categoryDTO)</span>&#123;</span><br><span class="line">        categoryService.update(categoryDTO);</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">     * 启用、禁用分类</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@param</span> status</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@param</span> id</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@return</span></span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    <span class="meta">@PostMapping(&quot;/status/&#123;status&#125;&quot;)</span></span><br><span class="line">    <span class="meta">@ApiOperation(&quot;启用禁用分类&quot;)</span></span><br><span class="line">    <span class="keyword">public</span> Result&lt;String&gt; <span class="title function_">startOrStop</span><span class="params">(<span class="meta">@PathVariable(&quot;status&quot;)</span> Integer status, Long id)</span>&#123;</span><br><span class="line">        categoryService.startOrStop(status,id);</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">     * 根据类型查询分类</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@param</span> type</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@return</span></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="meta">@ApiOperation(&quot;根据类型查询分类&quot;)</span></span><br><span class="line">    <span class="keyword">public</span> Result&lt;List&lt;Category&gt;&gt; <span class="title function_">list</span><span class="params">(Integer type)</span>&#123;</span><br><span class="line">        List&lt;Category&gt; list = categoryService.list(type);</span><br><span class="line">        <span class="keyword">return</span> Result.success(list);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h3 id="功能测试-4"><a href="#功能测试-4" class="headerlink" title="功能测试"></a>功能测试</h3><p><strong>分页查询和分类类型</strong><br><div class="img-wrap"><div class="img-bg"><img class="img" src="https://raw.githubusercontent.com/silvan2077/markdown_pic1/main/Picgo/20251112225311.png"/></div></div><br><strong>启用禁用</strong><br><div class="img-wrap"><div class="img-bg"><img class="img" src="https://raw.githubusercontent.com/silvan2077/markdown_pic1/main/Picgo/20251112225433.png"/></div></div></p><p><strong>修改</strong><br>回显<br><div class="img-wrap"><div class="img-bg"><img class="img" src="https://raw.githubusercontent.com/silvan2077/markdown_pic1/main/Picgo/20251112225716.png"/></div></div></p><p>修改后</p><div class="img-wrap"><div class="img-bg"><img class="img" src="https://raw.githubusercontent.com/silvan2077/markdown_pic1/main/Picgo/20251112225813.png"/></div></div><p><strong>新增</strong></p><div class="img-wrap"><div class="img-bg"><img class="img" src="https://raw.githubusercontent.com/silvan2077/markdown_pic1/main/Picgo/image-20221112172356093.png"/></div></div><p><strong>删除</strong></p><div class="img-wrap"><div class="img-bg"><img class="img" src="https://raw.githubusercontent.com/silvan2077/markdown_pic1/main/Picgo/image-20221112172525216.png"/></div></div><h1 id="day03"><a href="#day03" class="headerlink" title="day03"></a>day03</h1><h2 id="课程内容-1"><a href="#课程内容-1" class="headerlink" title="课程内容"></a>课程内容</h2><ul><li>公共字段自动填充</li><li>新增菜品</li><li>菜品分页查询</li><li>删除菜品</li><li>修改菜品</li></ul><p><strong>功能实现：</strong>菜品管理</p><p><strong>菜品管理效果图：</strong></p><div class="img-wrap"><div class="img-bg"><img class="img" src="https://raw.githubusercontent.com/silvan2077/markdown_pic1/main/Picgo/image-20221121142133307.png"/></div></div><h2 id="公共字段自动填充"><a href="#公共字段自动填充" class="headerlink" title="公共字段自动填充"></a>公共字段自动填充</h2><h3 id="需求分析-1"><a href="#需求分析-1" class="headerlink" title="需求分析"></a>需求分析</h3><p>在前面学习的<code>员工管理功能</code>和<code>菜品分类功能</code>，在 <strong>添加员工</strong> 或者 <strong>添加菜品分类</strong> 时都需要设置创建时间、创建人、修改时间、修改人等字段，在 <strong>编辑员工</strong> 或 <strong>编辑菜品</strong> 分类时需要设置修改时间、修改人等字段。这些字段属于公共字段也就是在系统中很多表中都会有这些字段，如下：</p><div class="table-container"><table><thead><tr><th><strong>序号</strong></th><th><strong>字段名</strong></th><th><strong>含义</strong></th><th><strong>数据类型</strong></th></tr></thead><tbody><tr><td>1</td><td>create_time</td><td>创建时间</td><td>datetime</td></tr><tr><td>2</td><td>create_user</td><td>创建人id</td><td>bigint</td></tr><tr><td>3</td><td>update_time</td><td>修改时间</td><td>datetime</td></tr><tr><td>4</td><td>update_user</td><td>修改人id</td><td>bigint</td></tr></tbody></table></div><p>针对这些字段的赋值方式通常为：</p><ol><li>新增数据时, 将<code>createTime</code>、<code>updateTime</code> 设置为当前时间, <code>createUser</code>、<code>updateUser</code>设置为当前登录用户ID。</li><li>更新数据时, 将<code>updateTime</code>设置为当前时间, <code>updateUser</code>设置为当前登录用户ID。</li></ol><div class="note info no-icon flat"><p>目前处理这些字段都是通过在对应的添加、修改方法中手动设置这些字段的值来实现，但是这样存在问题，比如新增、修改方法中如果忘记设置这些字段的值，那么这些字段的值就会丢失，且处理方法相对冗余、繁琐。因此，我们可以通过AOP切面编程来实现这些公共字段的自动填充。</p></div><h3 id="实现思路"><a href="#实现思路" class="headerlink" title="实现思路"></a>实现思路</h3><p>公共字段自动填充，也就是在插入或者更新的时候为指定字段赋予指定的值，使用它的好处就是可以统一对这些字段进行处理，避免了重复代码,前面提到的共有四个公共字段需要进行自动填充，分别是：<code>createTime</code>、<code>updateTime</code>、<code>createUser</code>、<code>updateUser</code>。</p><div class="table-container"><table><thead><tr><th><strong>序号</strong></th><th><strong>字段名</strong></th><th><strong>含义</strong></th><th><strong>数据类型</strong></th><th><strong>操作类型</strong></th></tr></thead><tbody><tr><td>1</td><td>create_time</td><td>创建时间</td><td>datetime</td><td>insert</td></tr><tr><td>2</td><td>create_user</td><td>创建人id</td><td>bigint</td><td>insert</td></tr><tr><td>3</td><td>update_time</td><td>修改时间</td><td>datetime</td><td>insert、update</td></tr><tr><td>4</td><td>update_user</td><td>修改人id</td><td>bigint</td><td>insert、update</td></tr></tbody></table></div><p><strong>实现步骤：</strong></p><ol><li>自定义注解<code>AutoFill</code>，用于标识需要进行公共字段自动填充的方法。</li><li>自定义切面类<code>AutoFillAspect</code>，统一拦截加入了<code>AutoFill</code>注解的方法，通过反射为公共字段赋值。</li><li>在Mapper的方法上加入<code>AutoFill</code>注解</li></ol><div class="note info no-icon flat"><p><strong>技术点：</strong> 枚举、注解、AOP、反射</p></div><h3 id="代码开发-4"><a href="#代码开发-4" class="headerlink" title="代码开发"></a>代码开发</h3><h4 id="自定义注解"><a href="#自定义注解" class="headerlink" title="自定义注解"></a>自定义注解</h4><p>进入sky-server模块，创建com.sky.annotation包<br><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><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"> * 自定义注解，用于标识某个方法需要进行功能字段自动填充处理</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="meta">@Target(ElementType.METHOD)</span></span><br><span class="line"><span class="meta">@Retention(RetentionPolicy.RUNTIME)</span></span><br><span class="line"><span class="keyword">public</span> <span class="meta">@interface</span> AutoFill &#123;</span><br><span class="line">    <span class="comment">//数据库操作类型：UPDATE INSERT</span></span><br><span class="line">    OperationType <span class="title function_">value</span><span class="params">()</span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><br>其中OperationType已在sky-common模块中定义<br><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><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="comment"> * 数据库操作类型</span></span><br><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_">OperationType</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"><span class="comment">     */</span></span><br><span class="line">    UPDATE,</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">    INSERT</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></p><h4 id="自定义切面"><a href="#自定义切面" class="headerlink" title="自定义切面"></a>自定义切面</h4><p>在sky-server模块，创建com.sky.aspect包<br><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><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="meta">@Aspect</span></span><br><span class="line"><span class="meta">@Component</span></span><br><span class="line"><span class="meta">@Slf4j</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">AutoFillAspect</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">@Pointcut(&quot;execution(* com.sky.mapper.*.*(..)) &amp;&amp; @annotation(com.sky.annotation.AutoFill)&quot;)</span></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">autoFillPointCut</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 class="meta">@Before(&quot;autoFillPointCut()&quot;)</span></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">autoFill</span><span class="params">(JoinPoint joinPoint)</span>&#123;</span><br><span class="line">        <span class="comment">/////////////////////重要////////////////////////////////////</span></span><br><span class="line">        <span class="comment">//可先进行调试，是否能进入该方法 提前在mapper方法添加AutoFill注解</span></span><br><span class="line">        log.info(<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><br><strong>完善自定义切面 <code>AutoFillAspect</code> 的 <code>autoFill</code> 方法</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></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">@Aspect</span></span><br><span class="line"><span class="meta">@Component</span></span><br><span class="line"><span class="meta">@Slf4j</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">AutoFillAspect</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">@Pointcut(&quot;execution(* com.sky.mapper.*.*(..)) &amp;&amp; @annotation(com.sky.annotation.AutoFill)&quot;)</span></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">autoFillPointCut</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 class="meta">@Before(&quot;autoFillPointCut()&quot;)</span></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">autoFill</span><span class="params">(JoinPoint joinPoint)</span>&#123;</span><br><span class="line">        log.info(<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">MethodSignature</span> <span class="variable">signature</span> <span class="operator">=</span> (MethodSignature) joinPoint.getSignature();<span class="comment">//方法签名对象</span></span><br><span class="line">        <span class="type">AutoFill</span> <span class="variable">autoFill</span> <span class="operator">=</span> signature.getMethod().getAnnotation(AutoFill.class);<span class="comment">//获得方法上的注解对象</span></span><br><span class="line">        <span class="type">OperationType</span> <span class="variable">operationType</span> <span class="operator">=</span> autoFill.value();<span class="comment">//获得数据库操作类型</span></span><br><span class="line"></span><br><span class="line">        <span class="comment">//获取到当前被拦截的方法的参数--实体对象</span></span><br><span class="line">        Object[] args = joinPoint.getArgs();</span><br><span class="line">        <span class="keyword">if</span>(args == <span class="literal">null</span> || args.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="type">Object</span> <span class="variable">entity</span> <span class="operator">=</span> args[<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="type">LocalDateTime</span> <span class="variable">now</span> <span class="operator">=</span> LocalDateTime.now();</span><br><span class="line">        <span class="type">Long</span> <span class="variable">currentId</span> <span class="operator">=</span> BaseContext.getCurrentId();</span><br><span class="line"></span><br><span class="line">        <span class="comment">//根据当前不同的操作类型，为对应的属性通过反射来赋值</span></span><br><span class="line">        <span class="keyword">if</span>(operationType == OperationType.INSERT)&#123;</span><br><span class="line">            <span class="comment">//为4个公共字段赋值</span></span><br><span class="line">            <span class="keyword">try</span> &#123;</span><br><span class="line">                <span class="type">Method</span> <span class="variable">setCreateTime</span> <span class="operator">=</span> entity.getClass().getDeclaredMethod(AutoFillConstant.SET_CREATE_TIME, LocalDateTime.class);</span><br><span class="line">                <span class="type">Method</span> <span class="variable">setCreateUser</span> <span class="operator">=</span> entity.getClass().getDeclaredMethod(AutoFillConstant.SET_CREATE_USER, Long.class);</span><br><span class="line">                <span class="type">Method</span> <span class="variable">setUpdateTime</span> <span class="operator">=</span> entity.getClass().getDeclaredMethod(AutoFillConstant.SET_UPDATE_TIME, LocalDateTime.class);</span><br><span class="line">                <span class="type">Method</span> <span class="variable">setUpdateUser</span> <span class="operator">=</span> entity.getClass().getDeclaredMethod(AutoFillConstant.SET_UPDATE_USER, Long.class);</span><br><span class="line"></span><br><span class="line">                <span class="comment">//通过反射为对象属性赋值</span></span><br><span class="line">                setCreateTime.invoke(entity,now);</span><br><span class="line">                setCreateUser.invoke(entity,currentId);</span><br><span class="line">                setUpdateTime.invoke(entity,now);</span><br><span class="line">                setUpdateUser.invoke(entity,currentId);</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 class="keyword">else</span> <span class="keyword">if</span>(operationType == OperationType.UPDATE)&#123;</span><br><span class="line">            <span class="comment">//为2个公共字段赋值</span></span><br><span class="line">            <span class="keyword">try</span> &#123;</span><br><span class="line">                <span class="type">Method</span> <span class="variable">setUpdateTime</span> <span class="operator">=</span> entity.getClass().getDeclaredMethod(AutoFillConstant.SET_UPDATE_TIME, LocalDateTime.class);</span><br><span class="line">                <span class="type">Method</span> <span class="variable">setUpdateUser</span> <span class="operator">=</span> entity.getClass().getDeclaredMethod(AutoFillConstant.SET_UPDATE_USER, Long.class);</span><br><span class="line"></span><br><span class="line">                <span class="comment">//通过反射为对象属性赋值</span></span><br><span class="line">                setUpdateTime.invoke(entity,now);</span><br><span class="line">                setUpdateUser.invoke(entity,currentId);</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><h4 id="添加注解"><a href="#添加注解" class="headerlink" title="添加注解"></a>添加注解</h4><p>以<strong>CategoryMapper</strong>为例，分别在新增和修改方法添加<code>@AutoFill()</code>注解，也需要<strong>EmployeeMapper</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></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_">CategoryMapper</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"><span class="comment">     * <span class="doctag">@param</span> category</span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    <span class="meta">@Insert(&quot;insert into category(type, name, sort, status, create_time, update_time, create_user, update_user)&quot; +</span></span><br><span class="line"><span class="meta">            &quot; VALUES&quot; +</span></span><br><span class="line"><span class="meta">            &quot; (#&#123;type&#125;, #&#123;name&#125;, #&#123;sort&#125;, #&#123;status&#125;, #&#123;createTime&#125;, #&#123;updateTime&#125;, #&#123;createUser&#125;, #&#123;updateUser&#125;)&quot;)</span></span><br><span class="line">    <span class="meta">@AutoFill(value = OperationType.INSERT)</span></span><br><span class="line">    <span class="keyword">void</span> <span class="title function_">insert</span><span class="params">(Category category)</span>;</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 class="doctag">@param</span> category</span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    <span class="meta">@AutoFill(value = OperationType.UPDATE)</span></span><br><span class="line">    <span class="keyword">void</span> <span class="title function_">update</span><span class="params">(Category category)</span>;</span><br><span class="line"></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><div class="note info no-icon flat"><p>执行完这三步操作以后，就可以将业务层中为公共字段赋值的代码注释掉了</p></div><h3 id="功能测试-5"><a href="#功能测试-5" class="headerlink" title="功能测试"></a>功能测试</h3><p>重启项目并新增一个菜品分类</p><div class="img-wrap"><div class="img-bg"><img class="img" src="https://raw.githubusercontent.com/silvan2077/markdown_pic1/main/Picgo/20251113151602.png"/></div></div><p>新增之后，可以去数据库查看category表，新增的分类数据已经包含了公共字段的赋值:<br><div class="img-wrap"><div class="img-bg"><img class="img" src="https://raw.githubusercontent.com/silvan2077/markdown_pic1/main/Picgo/20251113151602.png"/></div></div></p><h2 id="新增菜品"><a href="#新增菜品" class="headerlink" title="新增菜品"></a>新增菜品</h2><h3 id="需求分析和设计-5"><a href="#需求分析和设计-5" class="headerlink" title="需求分析和设计"></a>需求分析和设计</h3><h4 id="需求分析-2"><a href="#需求分析-2" class="headerlink" title="需求分析"></a>需求分析</h4><p>在后台系统可以管理菜品的信息，通过新增功能来添加新的菜品，在添加时需要选择菜品所属分类并且上传菜品图片<br><div class="img-wrap"><div class="img-bg"><img class="img" src="https://raw.githubusercontent.com/silvan2077/markdown_pic1/main/Picgo/image-20221121164131337.png"/></div></div></p><div class="note info no-icon flat"><p><strong>业务规则：</strong></p><ul><li>菜品名称必须是唯一的</li><li>菜品必须属于某个分类下，不能单独存在</li><li>新增菜品时可以根据情况选择菜品的口味</li><li>每个菜品必须对应一张图片</li></ul></div><h4 id="接口设计-1"><a href="#接口设计-1" class="headerlink" title="接口设计"></a>接口设计</h4><ul><li>根据类型查询分类（已完成）</li><li>文件上传</li><li>新增菜品</li></ul><div class="tabs" id="商家端新增菜品接口设计"><ul class="nav-tabs"><li class="tab active"><button type="button" data-href="#商家端新增菜品接口设计-1">根据类型查询分类</button></li><li class="tab"><button type="button" data-href="#商家端新增菜品接口设计-2">文件上传</button></li><li class="tab"><button type="button" data-href="#商家端新增菜品接口设计-3">新增菜品</button></li></ul><div class="tab-contents"><div class="tab-item-content active" id="商家端新增菜品接口设计-1"><div class="img-wrap"><div class="img-bg"><img class="img" src="https://raw.githubusercontent.com/silvan2077/markdown_pic1/main/Picgo/image-20221121165033612.png"/></div></div><div class="img-wrap"><div class="img-bg"><img class="img" src="https://raw.githubusercontent.com/silvan2077/markdown_pic1/main/Picgo/image-20221121165043619.png"/></div></div><button type="button" class="tab-to-top" aria-label="scroll to top"><i class="fas fa-arrow-up"></i></button></div><div class="tab-item-content" id="商家端新增菜品接口设计-2"><div class="img-wrap"><div class="img-bg"><img class="img" src="https://raw.githubusercontent.com/silvan2077/markdown_pic1/main/Picgo/image-20221121165033612.png"/></div></div><div class="img-wrap"><div class="img-bg"><img class="img" src="https://raw.githubusercontent.com/silvan2077/markdown_pic1/main/Picgo/image-20221121165215634.png"/></div></div><button type="button" class="tab-to-top" aria-label="scroll to top"><i class="fas fa-arrow-up"></i></button></div><div class="tab-item-content" id="商家端新增菜品接口设计-3"><div class="img-wrap"><div class="img-bg"><img class="img" src="https://raw.githubusercontent.com/silvan2077/markdown_pic1/main/Picgo/image-20221121165254961.png"/></div></div><div class="img-wrap"><div class="img-bg"><img class="img" src="https://raw.githubusercontent.com/silvan2077/markdown_pic1/main/Picgo/image-20221121165322687.png"/></div></div><button type="button" class="tab-to-top" aria-label="scroll to top"><i class="fas fa-arrow-up"></i></button></div></div></div><h4 id="表设计-1"><a href="#表设计-1" class="headerlink" title="表设计"></a>表设计</h4><div class="img-wrap"><div class="img-bg"><img class="img" src="https://raw.githubusercontent.com/silvan2077/markdown_pic1/main/Picgo/image-20221121165917874.png" style="width:600px;"/></div></div><p>新增菜品，其实就是将新增页面录入的菜品信息插入到dish表，如果添加了口味做法，还需要向dish_flavor表插入数据。所以在新增菜品时，涉及到两个表：</p><div class="table-container"><table><thead><tr><th>表名</th><th>说明</th></tr></thead><tbody><tr><td>dish</td><td>菜品表</td></tr><tr><td>dish_flavor</td><td>菜品口味表</td></tr></tbody></table></div><div class="tabs" id="新增菜品表设计"><ul class="nav-tabs"><li class="tab active"><button type="button" data-href="#新增菜品表设计-1">dish表</button></li><li class="tab"><button type="button" data-href="#新增菜品表设计-2">dish_flavor</button></li></ul><div class="tab-contents"><div class="tab-item-content active" id="新增菜品表设计-1"><div class="table-container"><table><thead><tr><th><strong>字段名</strong></th><th><strong>数据类型</strong></th><th><strong>说明</strong></th><th><strong>备注</strong></th></tr></thead><tbody><tr><td>id</td><td>bigint</td><td>主键</td><td>自增</td></tr><tr><td>name</td><td>varchar(32)</td><td>菜品名称</td><td>唯一</td></tr><tr><td>category_id</td><td>bigint</td><td>分类id</td><td>逻辑外键</td></tr><tr><td>price</td><td>decimal(10,2)</td><td>菜品价格</td><td></td></tr><tr><td>image</td><td>varchar(255)</td><td>图片路径</td><td></td></tr><tr><td>description</td><td>varchar(255)</td><td>菜品描述</td><td></td></tr><tr><td>status</td><td>int</td><td>售卖状态</td><td>1起售 0停售</td></tr><tr><td>create_time</td><td>datetime</td><td>创建时间</td><td></td></tr><tr><td>update_time</td><td>datetime</td><td>最后修改时间</td><td></td></tr><tr><td>create_user</td><td>bigint</td><td>创建人id</td><td></td></tr><tr><td>update_user</td><td>bigint</td><td>最后修改人id</td></tr></tbody></table></div><button type="button" class="tab-to-top" aria-label="scroll to top"><i class="fas fa-arrow-up"></i></button></div><div class="tab-item-content" id="新增菜品表设计-2"><div class="table-container"><table><thead><tr><th><strong>字段名</strong></th><th><strong>数据类型</strong></th><th><strong>说明</strong></th><th><strong>备注</strong></th></tr></thead><tbody><tr><td>id</td><td>bigint</td><td>主键</td><td>自增</td></tr><tr><td>dish_id</td><td>bigint</td><td>菜品id</td><td>逻辑外键</td></tr><tr><td>name</td><td>varchar(32)</td><td>口味名称</td><td></td></tr><tr><td>value</td><td>varchar(255)</td><td>口味值</td></tr></tbody></table></div><button type="button" class="tab-to-top" aria-label="scroll to top"><i class="fas fa-arrow-up"></i></button></div></div></div><h3 id="代码开发-5"><a href="#代码开发-5" class="headerlink" title="代码开发"></a>代码开发</h3><h4 id="文件上传功能"><a href="#文件上传功能" class="headerlink" title="文件上传功能"></a>文件上传功能</h4><p>新增菜品时，需要上传菜品的图片，因此需要使用到文件上传功能。<br>文件上传是将本地图片、视频、音频等文件上传到服务器，来供其他用户浏览或下载的过程。实现文件上传服务分为对文件进行存储和取出存储的文件两个部分，对于文件的存储解决方案通常有以下几种：</p><ol><li>直接将图片保存到服务的硬盘（springmvc中的文件上传）<ul><li>优点：开发便捷，成本低</li><li>缺点：扩容困难</li></ul></li><li>使用分布式文件系统进行存储<ul><li>优点：容易实现扩容</li><li>缺点：开发复杂度稍大（有成熟的产品可以使用，比如：FastDFS,MinIO）</li></ul></li><li>使用第三方的存储服务（例如OSS）<ul><li>优点：开发简单，拥有强大功能，免维护</li><li>缺点：付费(个人使用的话费用很低)</li></ul></li></ol><div class="note info no-icon flat"><p>此处使用的是阿里云的OSS服务进行文件存储，具体的阿里云OSS内容在最新版的Web课程中有过介绍，这里就不重复介绍了。<br><a href="https://heuqqdmbyk.feishu.cn/wiki/PHp6wXaNUij3bokCXWrclz5anWh">Web在线笔记</a></p></div><p><strong><em>实现步骤：</em></strong></p><ol><li>定义OSS相关配置<br>在sky-server模块的<code>application-dev.yml</code>中添加以下配置,其中<code>access-key-id</code>和<code>access-key-secret</code>需要从阿里云OSS控制台获取<figure class="highlight yml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><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="attr">sky:</span></span><br><span class="line">  <span class="attr">alioss:</span></span><br><span class="line">    <span class="attr">endpoint:</span> <span class="string">oss-cn-beijing.aliyuncs.com</span></span><br><span class="line">    <span class="attr">access-key-id:</span> <span class="string">id</span></span><br><span class="line">    <span class="attr">access-key-secret:</span> <span class="string">secret</span></span><br><span class="line">    <span class="attr">bucket-name:</span> <span class="string">sky-take-out-silvan</span></span><br></pre></td></tr></table></figure><div class="note warning no-icon flat"><ul><li>此处的<code>access-key-id</code>和<code>access-key-secret</code>需要从阿里云OSS控制台获取，请勿泄露</li><li><code>endpoint</code>的区域也需要根据bucket所在地区进行选择，如在杭州，则<code>endpoint</code>为<code>oss-cn-hangzhou.aliyuncs.com</code></li></ul></div></li><li>读取OSS配置<br>在sky-common模块中，基础工程已定义<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><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="meta">@Component</span></span><br><span class="line"><span class="meta">@ConfigurationProperties(prefix = &quot;sky.alioss&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_">AliOssProperties</span> &#123;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">private</span> String endpoint;</span><br><span class="line">    <span class="keyword">private</span> String accessKeyId;</span><br><span class="line">    <span class="keyword">private</span> String accessKeySecret;</span><br><span class="line">    <span class="keyword">private</span> String bucketName;</span><br><span class="line"></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></li><li>生成OSS工具类对象<br>在sky-server模块的config包下新建OSS配置类<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><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"> * 配置类，用于创建AliOssUtil对象</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">@Slf4j</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">OssConfiguration</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">@ConditionalOnMissingBean</span></span><br><span class="line">    <span class="keyword">public</span> AliOssUtil <span class="title function_">aliOssUtil</span><span class="params">(AliOssProperties aliOssProperties)</span>&#123;</span><br><span class="line">        log.info(<span class="string">&quot;开始创建阿里云文件上传工具类对象：&#123;&#125;&quot;</span>,aliOssProperties);</span><br><span class="line">        <span class="keyword">return</span> <span class="keyword">new</span> <span class="title class_">AliOssUtil</span>(aliOssProperties.getEndpoint(),</span><br><span class="line">                aliOssProperties.getAccessKeyId(),</span><br><span class="line">                aliOssProperties.getAccessKeySecret(),</span><br><span class="line">                aliOssProperties.getBucketName());</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>其中的<code>AliOssUtil.java</code>已经在sky-common模块中定义了<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><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="meta">@Data</span></span><br><span class="line"><span class="meta">@AllArgsConstructor</span></span><br><span class="line"><span class="meta">@Slf4j</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">AliOssUtil</span> &#123;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">private</span> String endpoint;</span><br><span class="line">    <span class="keyword">private</span> String accessKeyId;</span><br><span class="line">    <span class="keyword">private</span> String accessKeySecret;</span><br><span class="line">    <span class="keyword">private</span> String bucketName;</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">@param</span> bytes</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@param</span> objectName</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@return</span></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_">upload</span><span class="params">(<span class="type">byte</span>[] bytes, String objectName)</span> &#123;</span><br><span class="line"></span><br><span class="line">        <span class="comment">// 创建OSSClient实例。</span></span><br><span class="line">        <span class="type">OSS</span> <span class="variable">ossClient</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">OSSClientBuilder</span>().build(endpoint, accessKeyId, accessKeySecret);</span><br><span class="line"></span><br><span class="line">        <span class="keyword">try</span> &#123;</span><br><span class="line">            <span class="comment">// 创建PutObject请求。</span></span><br><span class="line">            ossClient.putObject(bucketName, objectName, <span class="keyword">new</span> <span class="title class_">ByteArrayInputStream</span>(bytes));</span><br><span class="line">        &#125; <span class="keyword">catch</span> (OSSException oe) &#123;</span><br><span class="line">            System.out.println(<span class="string">&quot;Caught an OSSException, which means your request made it to OSS, &quot;</span></span><br><span class="line">                    + <span class="string">&quot;but was rejected with an error response for some reason.&quot;</span>);</span><br><span class="line">            System.out.println(<span class="string">&quot;Error Message:&quot;</span> + oe.getErrorMessage());</span><br><span class="line">            System.out.println(<span class="string">&quot;Error Code:&quot;</span> + oe.getErrorCode());</span><br><span class="line">            System.out.println(<span class="string">&quot;Request ID:&quot;</span> + oe.getRequestId());</span><br><span class="line">            System.out.println(<span class="string">&quot;Host ID:&quot;</span> + oe.getHostId());</span><br><span class="line">        &#125; <span class="keyword">catch</span> (ClientException ce) &#123;</span><br><span class="line">            System.out.println(<span class="string">&quot;Caught an ClientException, which means the client encountered &quot;</span></span><br><span class="line">                    + <span class="string">&quot;a serious internal problem while trying to communicate with OSS, &quot;</span></span><br><span class="line">                    + <span class="string">&quot;such as not being able to access the network.&quot;</span>);</span><br><span class="line">            System.out.println(<span class="string">&quot;Error Message:&quot;</span> + ce.getMessage());</span><br><span class="line">        &#125; <span class="keyword">finally</span> &#123;</span><br><span class="line">            <span class="keyword">if</span> (ossClient != <span class="literal">null</span>) &#123;</span><br><span class="line">                ossClient.shutdown();</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">//文件访问路径规则 https://BucketName.Endpoint/ObjectName</span></span><br><span class="line">        <span class="type">StringBuilder</span> <span class="variable">stringBuilder</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">StringBuilder</span>(<span class="string">&quot;https://&quot;</span>);</span><br><span class="line">        stringBuilder</span><br><span class="line">                .append(bucketName)</span><br><span class="line">                .append(<span class="string">&quot;.&quot;</span>)</span><br><span class="line">                .append(endpoint)</span><br><span class="line">                .append(<span class="string">&quot;/&quot;</span>)</span><br><span class="line">                .append(objectName);</span><br><span class="line"></span><br><span class="line">        log.info(<span class="string">&quot;文件上传到:&#123;&#125;&quot;</span>, stringBuilder.toString());</span><br><span class="line"></span><br><span class="line">        <span class="keyword">return</span> stringBuilder.toString();</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></li><li>定义文件上传接口<br>在Controller层中定义接口<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><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="meta">@RestController</span></span><br><span class="line"><span class="meta">@RequestMapping(&quot;/admin/common&quot;)</span></span><br><span class="line"><span class="meta">@Api(tags = &quot;通用接口&quot;)</span></span><br><span class="line"><span class="meta">@Slf4j</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">CommonController</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> AliOssUtil aliOssUtil;</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 class="doctag">@param</span> file</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@return</span></span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    <span class="meta">@PostMapping(&quot;/upload&quot;)</span></span><br><span class="line">    <span class="meta">@ApiOperation(&quot;文件上传&quot;)</span></span><br><span class="line">    <span class="keyword">public</span> Result&lt;String&gt; <span class="title function_">upload</span><span class="params">(MultipartFile file)</span>&#123;</span><br><span class="line">        log.info(<span class="string">&quot;文件上传：&#123;&#125;&quot;</span>,file);</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">            <span class="type">String</span> <span class="variable">originalFilename</span> <span class="operator">=</span> file.getOriginalFilename();</span><br><span class="line">            <span class="comment">//截取原始文件名的后缀   dfdfdf.png</span></span><br><span class="line">            <span class="type">String</span> <span class="variable">extension</span> <span class="operator">=</span> originalFilename.substring(originalFilename.lastIndexOf(<span class="string">&quot;.&quot;</span>));</span><br><span class="line">            <span class="comment">//构造新文件名称</span></span><br><span class="line">            <span class="type">String</span> <span class="variable">objectName</span> <span class="operator">=</span> UUID.randomUUID().toString() + extension;</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">filePath</span> <span class="operator">=</span> aliOssUtil.upload(file.getBytes(), objectName);</span><br><span class="line">            <span class="keyword">return</span> Result.success(filePath);</span><br><span class="line">        &#125; <span class="keyword">catch</span> (IOException e) &#123;</span><br><span class="line">            log.error(<span class="string">&quot;文件上传失败：&#123;&#125;&quot;</span>, e);</span><br><span class="line">        &#125;</span><br><span class="line"></span><br><span class="line">        <span class="keyword">return</span> Result.error(MessageConstant.UPLOAD_FAILED);</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></li><li>设计DTO类<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><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="meta">@Data</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">DishDTO</span> <span class="keyword">implements</span> <span class="title class_">Serializable</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="comment">//菜品名称</span></span><br><span class="line">    <span class="keyword">private</span> String name;</span><br><span class="line">    <span class="comment">//菜品分类id</span></span><br><span class="line">    <span class="keyword">private</span> Long categoryId;</span><br><span class="line">    <span class="comment">//菜品价格</span></span><br><span class="line">    <span class="keyword">private</span> BigDecimal price;</span><br><span class="line">    <span class="comment">//图片</span></span><br><span class="line">    <span class="keyword">private</span> String image;</span><br><span class="line">    <span class="comment">//描述信息</span></span><br><span class="line">    <span class="keyword">private</span> String description;</span><br><span class="line">    <span class="comment">//0 停售 1 起售</span></span><br><span class="line">    <span class="keyword">private</span> Integer status;</span><br><span class="line">    <span class="comment">//口味</span></span><br><span class="line">    <span class="keyword">private</span> List&lt;DishFlavor&gt; flavors = <span class="keyword">new</span> <span class="title class_">ArrayList</span>&lt;&gt;();</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></li><li>Controller层<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><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"> * 菜品管理</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;/admin/dish&quot;)</span></span><br><span class="line"><span class="meta">@Api(tags = &quot;菜品相关接口&quot;)</span></span><br><span class="line"><span class="meta">@Slf4j</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">DishController</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> DishService dishService;</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">@param</span> dishDTO</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@return</span></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="meta">@ApiOperation(&quot;新增菜品&quot;)</span></span><br><span class="line">    <span class="keyword">public</span> Result <span class="title function_">save</span><span class="params">(<span class="meta">@RequestBody</span> DishDTO dishDTO)</span> &#123;</span><br><span class="line">        log.info(<span class="string">&quot;新增菜品：&#123;&#125;&quot;</span>, dishDTO);</span><br><span class="line">        dishService.saveWithFlavor(dishDTO);<span class="comment">//后绪步骤开发</span></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></li><li>Service层<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><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">interface</span> <span class="title class_">DishService</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">@param</span> dishDTO</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_">saveWithFlavor</span><span class="params">(DishDTO dishDTO)</span>;</span><br><span class="line"></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></li><li>ServiceImpl层<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><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="meta">@Service</span></span><br><span class="line"><span class="meta">@Slf4j</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">DishServiceImpl</span> <span class="keyword">implements</span> <span class="title class_">DishService</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> DishMapper dishMapper;</span><br><span class="line">    <span class="meta">@Autowired</span></span><br><span class="line">    <span class="keyword">private</span> DishFlavorMapper dishFlavorMapper;</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">@param</span> dishDTO</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_">saveWithFlavor</span><span class="params">(DishDTO dishDTO)</span> &#123;</span><br><span class="line"></span><br><span class="line">        <span class="type">Dish</span> <span class="variable">dish</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">Dish</span>();</span><br><span class="line">        BeanUtils.copyProperties(dishDTO, dish);</span><br><span class="line"></span><br><span class="line">        <span class="comment">//向菜品表插入1条数据</span></span><br><span class="line">        dishMapper.insert(dish);<span class="comment">//后绪步骤实现</span></span><br><span class="line"></span><br><span class="line">        <span class="comment">//获取insert语句生成的主键值</span></span><br><span class="line">        <span class="type">Long</span> <span class="variable">dishId</span> <span class="operator">=</span> dish.getId();</span><br><span class="line"></span><br><span class="line">        List&lt;DishFlavor&gt; flavors = dishDTO.getFlavors();</span><br><span class="line">        <span class="keyword">if</span> (flavors != <span class="literal">null</span> &amp;&amp; flavors.size() &gt; <span class="number">0</span>) &#123;</span><br><span class="line">            flavors.forEach(dishFlavor -&gt; &#123;</span><br><span class="line">                dishFlavor.setDishId(dishId);</span><br><span class="line">            &#125;);</span><br><span class="line">            <span class="comment">//向口味表插入n条数据</span></span><br><span class="line">            dishFlavorMapper.insertBatch(flavors);<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">&#125;</span><br></pre></td></tr></table></figure></li><li>Mapper层</li><li><div class="tabs" id="新增菜品实现dishmapper"><ul class="nav-tabs"><li class="tab active"><button type="button" data-href="#新增菜品实现dishmapper-1">DishMapper.java</button></li><li class="tab"><button type="button" data-href="#新增菜品实现dishmapper-2">DishMapper.xml</button></li></ul><div class="tab-contents"><div class="tab-item-content active" id="新增菜品实现dishmapper-1"><p>在DishMapper中添加<br><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><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="comment"> * 插入菜品数据</span></span><br><span class="line"><span class="comment"> *</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@param</span> dish</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="meta">@AutoFill(value = OperationType.INSERT)</span></span><br><span class="line"><span class="keyword">void</span> <span class="title function_">insert</span><span class="params">(Dish dish)</span>;</span><br></pre></td></tr></table></figure></p><button type="button" class="tab-to-top" aria-label="scroll to top"><i class="fas fa-arrow-up"></i></button></div><div class="tab-item-content" id="新增菜品实现dishmapper-2"><p>在/resources/mapper中新建DishMapper.xml<br><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="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 class="tag">&lt;<span class="name">mapper</span> <span class="attr">namespace</span>=<span class="string">&quot;com.sky.mapper.DishMapper&quot;</span>&gt;</span></span><br><span class="line"></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">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 dish (name, category_id, price, image, description, create_time, update_time, create_user,update_user, status)</span><br><span class="line">        values (#&#123;name&#125;, #&#123;categoryId&#125;, #&#123;price&#125;, #&#123;image&#125;, #&#123;description&#125;, #&#123;createTime&#125;, #&#123;updateTime&#125;, #&#123;createUser&#125;, #&#123;updateUser&#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 class="tag">&lt;/<span class="name">mapper</span>&gt;</span></span><br></pre></td></tr></table></figure></p><button type="button" class="tab-to-top" aria-label="scroll to top"><i class="fas fa-arrow-up"></i></button></div></div></div>DishFlavorMapper<div class="tabs" id="新增菜品实现dishflavormapper"><ul class="nav-tabs"><li class="tab active"><button type="button" data-href="#新增菜品实现dishflavormapper-1">DishFlavorMapper.java</button></li><li class="tab"><button type="button" data-href="#新增菜品实现dishflavormapper-2">DishFlavorMapper.xml</button></li></ul><div class="tab-contents"><div class="tab-item-content active" id="新增菜品实现dishflavormapper-1"><p>在DishFlavorMapper中添加<br><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><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">@Mapper</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">interface</span> <span class="title class_">DishFlavorMapper</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"><span class="comment">     * <span class="doctag">@param</span> flavors</span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    <span class="keyword">void</span> <span class="title function_">insertBatch</span><span class="params">(List&lt;DishFlavor&gt; flavors)</span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></p><button type="button" class="tab-to-top" aria-label="scroll to top"><i class="fas fa-arrow-up"></i></button></div><div class="tab-item-content" id="新增菜品实现dishflavormapper-2"><p>在/resources/mapper中新建DishFlavorMapper.xml<br><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></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 class="tag">&lt;<span class="name">mapper</span> <span class="attr">namespace</span>=<span class="string">&quot;com.sky.mapper.DishFlavorMapper&quot;</span>&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;insertBatch&quot;</span>&gt;</span></span><br><span class="line">        insert into dish_flavor (dish_id, name, value) VALUES</span><br><span class="line">        <span class="tag">&lt;<span class="name">foreach</span> <span class="attr">collection</span>=<span class="string">&quot;flavors&quot;</span> <span class="attr">item</span>=<span class="string">&quot;df&quot;</span> <span class="attr">separator</span>=<span class="string">&quot;,&quot;</span>&gt;</span></span><br><span class="line">            (#&#123;df.dishId&#125;,#&#123;df.name&#125;,#&#123;df.value&#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 class="tag">&lt;/<span class="name">mapper</span>&gt;</span></span><br></pre></td></tr></table></figure></p><button type="button" class="tab-to-top" aria-label="scroll to top"><i class="fas fa-arrow-up"></i></button></div></div></div></li></ol><h3 id="功能测试-6"><a href="#功能测试-6" class="headerlink" title="功能测试"></a>功能测试</h3><p>登录 -&gt; 菜品管理 -&gt; 新增菜品 -&gt; 输入菜品信息 -&gt; 保存<br><div class="img-wrap"><div class="img-bg"><img class="img" src="https://raw.githubusercontent.com/silvan2077/markdown_pic1/main/Picgo/20251113211125.png"/></div></div><br>暂时还没有实现菜品查询功能，所以保存后通过数据库查看添加的数据</p><h2 id="菜品分页查询"><a href="#菜品分页查询" class="headerlink" title="菜品分页查询"></a>菜品分页查询</h2><h3 id="需求分析和设计-6"><a href="#需求分析和设计-6" class="headerlink" title="需求分析和设计"></a>需求分析和设计</h3><p><strong>需求分析</strong><br>系统中的菜品数据很多的时候，如果在一个页面中全部展示出来会显得比较乱，不便于查看，所以一般的系统中都会以分页的方式来展示列表数据。<br><div class="img-wrap"><div class="img-bg"><img class="img" src="https://raw.githubusercontent.com/silvan2077/markdown_pic1/main/Picgo/image-20221121201552489.png"/></div></div><br><div class="note info no-icon flat"><p>菜品信息展示时，菜品的图片字段和菜品分类字段较特殊</p><ul><li>图片字段在数据库中查询到的仅仅是图片名称，想要回显到页面则需要下载该图片</li><li>分类名称存储形式是分类id，需要根据id来查询分类名称并展示</li></ul></div></p><p><strong>接口设计</strong><br><div class="img-wrap"><div class="img-bg"><img class="img" src="https://raw.githubusercontent.com/silvan2077/markdown_pic1/main/Picgo/image-20221121202019258.png"/></div></div></p><div class="img-wrap"><div class="img-bg"><img class="img" src="https://raw.githubusercontent.com/silvan2077/markdown_pic1/main/Picgo/image-20221121202033284.png"/></div></div><h3 id="代码开发-6"><a href="#代码开发-6" class="headerlink" title="代码开发"></a>代码开发</h3><h4 id="设计DTO类"><a href="#设计DTO类" class="headerlink" title="设计DTO类"></a>设计DTO类</h4><p><strong>根据菜品分页查询接口定义设计对应的DTO：</strong></p><p>在sky-pojo模块中提供了<code>DishPageQueryDTO</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></pre></td><td class="code"><pre><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">class</span> <span class="title class_">DishPageQueryDTO</span> <span class="keyword">implements</span> <span class="title class_">Serializable</span> &#123;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">private</span> <span class="type">int</span> page;</span><br><span class="line">    <span class="keyword">private</span> <span class="type">int</span> pageSize;</span><br><span class="line">    <span class="keyword">private</span> String name;</span><br><span class="line">    <span class="keyword">private</span> Integer categoryId; <span class="comment">//分类id</span></span><br><span class="line">    <span class="keyword">private</span> Integer status; <span class="comment">//状态 0表示禁用 1表示启用</span></span><br><span class="line"></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h4 id="设计VO类"><a href="#设计VO类" class="headerlink" title="设计VO类"></a>设计VO类</h4><p><strong>根据菜品分页查询接口定义设计对应的VO：</strong></p><p>在sky-pojo模块中提供了<code>DishVO</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></pre></td><td class="code"><pre><span class="line"><span class="meta">@Data</span></span><br><span class="line"><span class="meta">@Builder</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="keyword">public</span> <span class="keyword">class</span> <span class="title class_">DishVO</span> <span class="keyword">implements</span> <span class="title class_">Serializable</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="comment">//菜品名称</span></span><br><span class="line">    <span class="keyword">private</span> String name;</span><br><span class="line">    <span class="comment">//菜品分类id</span></span><br><span class="line">    <span class="keyword">private</span> Long categoryId;</span><br><span class="line">    <span class="comment">//菜品价格</span></span><br><span class="line">    <span class="keyword">private</span> BigDecimal price;</span><br><span class="line">    <span class="comment">//图片</span></span><br><span class="line">    <span class="keyword">private</span> String image;</span><br><span class="line">    <span class="comment">//描述信息</span></span><br><span class="line">    <span class="keyword">private</span> String description;</span><br><span class="line">    <span class="comment">//0 停售 1 起售</span></span><br><span class="line">    <span class="keyword">private</span> Integer status;</span><br><span class="line">    <span class="comment">//更新时间</span></span><br><span class="line">    <span class="keyword">private</span> LocalDateTime updateTime;</span><br><span class="line">    <span class="comment">//分类名称</span></span><br><span class="line">    <span class="keyword">private</span> String categoryName;</span><br><span class="line">    <span class="comment">//菜品关联的口味</span></span><br><span class="line">    <span class="keyword">private</span> List&lt;DishFlavor&gt; flavors = <span class="keyword">new</span> <span class="title class_">ArrayList</span>&lt;&gt;();</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h4 id="Controller层-2"><a href="#Controller层-2" class="headerlink" title="Controller层"></a>Controller层</h4><p><strong>根据接口定义添加DishController的page分页查询方法：</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></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">@param</span> dishPageQueryDTO</span></span><br><span class="line"><span class="comment">    * <span class="doctag">@return</span></span></span><br><span class="line"><span class="comment">    */</span></span><br><span class="line">   <span class="meta">@GetMapping(&quot;/page&quot;)</span></span><br><span class="line">   <span class="meta">@ApiOperation(&quot;菜品分页查询&quot;)</span></span><br><span class="line">   <span class="keyword">public</span> Result&lt;PageResult&gt; <span class="title function_">page</span><span class="params">(DishPageQueryDTO dishPageQueryDTO)</span> &#123;</span><br><span class="line">       log.info(<span class="string">&quot;菜品分页查询:&#123;&#125;&quot;</span>, dishPageQueryDTO);</span><br><span class="line">       <span class="type">PageResult</span> <span class="variable">pageResult</span> <span class="operator">=</span> dishService.pageQuery(dishPageQueryDTO);<span class="comment">//后绪步骤定义</span></span><br><span class="line">       <span class="keyword">return</span> Result.success(pageResult);</span><br><span class="line">   &#125;</span><br></pre></td></tr></table></figure><h4 id="Service层接口-1"><a href="#Service层接口-1" class="headerlink" title="Service层接口"></a>Service层接口</h4><p><strong>在 DishService中添加分页查询方法：</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">/**</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">@param</span> dishPageQueryDTO</span></span><br><span class="line"><span class="comment">    * <span class="doctag">@return</span></span></span><br><span class="line"><span class="comment">    */</span></span><br><span class="line">   PageResult <span class="title function_">pageQuery</span><span class="params">(DishPageQueryDTO dishPageQueryDTO)</span>;</span><br></pre></td></tr></table></figure><h4 id="Service层实现类-1"><a href="#Service层实现类-1" class="headerlink" title="Service层实现类"></a>Service层实现类</h4><p><strong>在 DishServiceImpl 中实现分页查询方法：</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></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">@param</span> dishPageQueryDTO</span></span><br><span class="line"><span class="comment">    * <span class="doctag">@return</span></span></span><br><span class="line"><span class="comment">    */</span></span><br><span class="line">   <span class="keyword">public</span> PageResult <span class="title function_">pageQuery</span><span class="params">(DishPageQueryDTO dishPageQueryDTO)</span> &#123;</span><br><span class="line">       PageHelper.startPage(dishPageQueryDTO.getPage(), dishPageQueryDTO.getPageSize());</span><br><span class="line">       Page&lt;DishVO&gt; page = dishMapper.pageQuery(dishPageQueryDTO);<span class="comment">//后绪步骤实现</span></span><br><span class="line">       <span class="keyword">return</span> <span class="keyword">new</span> <span class="title class_">PageResult</span>(page.getTotal(), page.getResult());</span><br><span class="line">   &#125;</span><br></pre></td></tr></table></figure><h4 id="Mapper层-2"><a href="#Mapper层-2" class="headerlink" title="Mapper层"></a>Mapper层</h4><p><strong>在 DishMapper 接口中声明 pageQuery 方法：</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">/**</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">@param</span> dishPageQueryDTO</span></span><br><span class="line"><span class="comment">    * <span class="doctag">@return</span></span></span><br><span class="line"><span class="comment">    */</span></span><br><span class="line">   Page&lt;DishVO&gt; <span class="title function_">pageQuery</span><span class="params">(DishPageQueryDTO dishPageQueryDTO)</span>;</span><br></pre></td></tr></table></figure><p><strong>在<code>DishMapper.xml</code>中编写SQL：</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><span class="line">11</span><br><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="tag">&lt;<span class="name">select</span> <span class="attr">id</span>=<span class="string">&quot;pageQuery&quot;</span> <span class="attr">resultType</span>=<span class="string">&quot;com.sky.vo.DishVO&quot;</span>&gt;</span></span><br><span class="line">        select d.* , c.name as categoryName from dish d left outer join category c on d.category_id = c.id</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;name != null&quot;</span>&gt;</span></span><br><span class="line">                and d.name like concat(&#x27;%&#x27;,#&#123;name&#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;categoryId != null&quot;</span>&gt;</span></span><br><span class="line">                and d.category_id = #&#123;categoryId&#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">                and d.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 d.create_time 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="功能测试-7"><a href="#功能测试-7" class="headerlink" title="功能测试"></a>功能测试</h3><h2 id="删除菜品"><a href="#删除菜品" class="headerlink" title="删除菜品"></a>删除菜品</h2><h3 id="需求分析和设计-7"><a href="#需求分析和设计-7" class="headerlink" title="需求分析和设计"></a>需求分析和设计</h3><p>菜品列表页面中每个菜品后面对应的操作分别为<strong>修改</strong>、<strong>删除</strong>、<strong>停售</strong>，可通过删除功能完成对菜品及相关的数据进行删除。</p><div class="img-wrap"><div class="img-bg"><img class="img" src="https://raw.githubusercontent.com/silvan2077/markdown_pic1/main/Picgo/image-20221121211236356.png"/></div></div><div class="note info no-icon flat"><p><strong>业务规则：</strong></p><ul><li>可以一次删除一个菜品，也可以批量删除菜品</li><li>起售中的菜品不能删除</li><li>被套餐关联的菜品不能删除</li><li>删除菜品后，关联的口味数据也需要删除掉</li></ul></div><h4 id="接口设计-2"><a href="#接口设计-2" class="headerlink" title="接口设计"></a>接口设计</h4><p>根据原型图，设计出相应的接口。</p><div class="img-wrap"><div class="img-bg"><img class="img" src="https://raw.githubusercontent.com/silvan2077/markdown_pic1/main/Picgo/image-20221121211801121.png"/></div></div><div class="img-wrap"><div class="img-bg"><img class="img" src="https://raw.githubusercontent.com/silvan2077/markdown_pic1/main/Picgo/image-20221121211814429.png"/></div></div><div class="note warning no-icon flat"><p><strong>注意：</strong> 删除一个菜品和批量删除菜品共用一个接口，区别只是参数个数不同。</p></div><h4 id="表设计-2"><a href="#表设计-2" class="headerlink" title="表设计"></a>表设计</h4><p>删除菜品操作时，会涉及到以下三张表。</p><div class="img-wrap"><div class="img-bg"><img class="img" src="https://raw.githubusercontent.com/silvan2077/markdown_pic1/main/Picgo/image-20221121212436851.png"/></div></div><div class="note warning no-icon flat"><p><strong>注意事项：</strong></p><ul><li>在dish表中删除菜品基本数据时，同时，也要把关联在dish_flavor表中的数据一块删除。</li><li>setmeal_dish表为菜品和套餐关联的中间表。</li><li>若删除的菜品数据关联着某个套餐，此时，删除失败。</li><li>若要删除套餐关联的菜品数据，先解除两者关联，再对菜品进行删除。</li></ul></div><h3 id="代码开发-7"><a href="#代码开发-7" class="headerlink" title="代码开发"></a>代码开发</h3><h4 id="Controller层-3"><a href="#Controller层-3" class="headerlink" title="Controller层"></a>Controller层</h4><p><strong>在DishController中添加批量删除菜品的方法：</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></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">@param</span> ids</span></span><br><span class="line"><span class="comment">    * <span class="doctag">@return</span></span></span><br><span class="line"><span class="comment">    */</span></span><br><span class="line">   <span class="meta">@DeleteMapping</span></span><br><span class="line">   <span class="meta">@ApiOperation(&quot;菜品批量删除&quot;)</span></span><br><span class="line">   <span class="keyword">public</span> Result <span class="title function_">delete</span><span class="params">(<span class="meta">@RequestParam</span> List&lt;Long&gt; ids)</span> &#123;</span><br><span class="line">       log.info(<span class="string">&quot;菜品批量删除：&#123;&#125;&quot;</span>, ids);</span><br><span class="line">       dishService.deleteBatch(ids);<span class="comment">//后绪步骤实现</span></span><br><span class="line">       <span class="keyword">return</span> Result.success();</span><br><span class="line">   &#125;</span><br></pre></td></tr></table></figure><h4 id="Service层接口-2"><a href="#Service层接口-2" class="headerlink" title="Service层接口"></a>Service层接口</h4><p><strong>在DishService接口中声明deleteBatch方法：</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></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">@param</span> ids</span></span><br><span class="line"><span class="comment">    */</span></span><br><span class="line">   <span class="keyword">void</span> <span class="title function_">deleteBatch</span><span class="params">(List&lt;Long&gt; ids)</span>;</span><br></pre></td></tr></table></figure><h4 id="4-2-3-Service层实现类"><a href="#4-2-3-Service层实现类" class="headerlink" title="4.2.3 Service层实现类"></a>4.2.3 Service层实现类</h4><p><strong>在DishServiceImpl中实现deleteBatch方法：</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></pre></td><td class="code"><pre><span class="line">   <span class="meta">@Autowired</span></span><br><span class="line">   <span class="keyword">private</span> SetmealDishMapper setmealDishMapper;</span><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">@param</span> ids</span></span><br><span class="line"><span class="comment">    */</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">void</span> <span class="title function_">deleteBatch</span><span class="params">(List&lt;Long&gt; ids)</span> &#123;</span><br><span class="line">       <span class="comment">//判断当前菜品是否能够删除---是否存在起售中的菜品？？</span></span><br><span class="line">       <span class="keyword">for</span> (Long id : ids) &#123;</span><br><span class="line">           <span class="type">Dish</span> <span class="variable">dish</span> <span class="operator">=</span> dishMapper.getById(id);<span class="comment">//后绪步骤实现</span></span><br><span class="line">           <span class="keyword">if</span> (dish.getStatus() == StatusConstant.ENABLE) &#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_">DeletionNotAllowedException</span>(MessageConstant.DISH_ON_SALE);</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">       List&lt;Long&gt; setmealIds = setmealDishMapper.getSetmealIdsByDishIds(ids);</span><br><span class="line">       <span class="keyword">if</span> (setmealIds != <span class="literal">null</span> &amp;&amp; setmealIds.size() &gt; <span class="number">0</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_">DeletionNotAllowedException</span>(MessageConstant.DISH_BE_RELATED_BY_SETMEAL);</span><br><span 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">for</span> (Long id : ids) &#123;</span><br><span class="line">           dishMapper.deleteById(id);<span class="comment">//后绪步骤实现</span></span><br><span class="line">           <span class="comment">//删除菜品关联的口味数据</span></span><br><span class="line">           dishFlavorMapper.deleteByDishId(id);<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="Mapper层-3"><a href="#Mapper层-3" class="headerlink" title="Mapper层"></a>Mapper层</h4><p><strong>在DishMapper中添加getById方法</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="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">@param</span> id</span></span><br><span class="line"><span class="comment">    * <span class="doctag">@return</span></span></span><br><span class="line"><span class="comment">    */</span></span><br><span class="line">   <span class="meta">@Select(&quot;select * from dish where id = #&#123;id&#125;&quot;)</span></span><br><span class="line">   Dish <span class="title function_">getById</span><span class="params">(Long id)</span>;</span><br></pre></td></tr></table></figure><p><strong>创建SetmealDishMapper，声明getSetmealIdsByDishIds方法，并在xml文件中编写SQL：</strong></p><div class="tabs" id="删除菜品mapper层"><ul class="nav-tabs"><li class="tab active"><button type="button" data-href="#删除菜品mapper层-1">SetmealDishMapper.java</button></li><li class="tab"><button type="button" data-href="#删除菜品mapper层-2">SetmealDishMapper.xml</button></li></ul><div class="tab-contents"><div class="tab-item-content active" id="删除菜品mapper层-1"><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><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="meta">@Mapper</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">interface</span> <span class="title class_">SetmealDishMapper</span> &#123;</span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     * 根据菜品id查询对应的套餐id</span></span><br><span class="line"><span class="comment">     *</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@param</span> dishIds</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@return</span></span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    <span class="comment">//select setmeal_id from setmeal_dish where dish_id in (1,2,3,4)</span></span><br><span class="line">    List&lt;Long&gt; <span class="title function_">getSetmealIdsByDishIds</span><span class="params">(List&lt;Long&gt; dishIds)</span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><button type="button" class="tab-to-top" aria-label="scroll to top"><i class="fas fa-arrow-up"></i></button></div><div class="tab-item-content" id="删除菜品mapper层-2"><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></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 class="tag">&lt;<span class="name">mapper</span> <span class="attr">namespace</span>=<span class="string">&quot;com.sky.mapper.SetmealDishMapper&quot;</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;getSetmealIdsByDishIds&quot;</span> <span class="attr">resultType</span>=<span class="string">&quot;java.lang.Long&quot;</span>&gt;</span></span><br><span class="line">        select setmeal_id from setmeal_dish where dish_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;dishIds&quot;</span> <span class="attr">item</span>=<span class="string">&quot;dishId&quot;</span> <span class="attr">separator</span>=<span class="string">&quot;,&quot;</span> <span class="attr">open</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;dishId&#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">select</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;/<span class="name">mapper</span>&gt;</span></span><br></pre></td></tr></table></figure><button type="button" class="tab-to-top" aria-label="scroll to top"><i class="fas fa-arrow-up"></i></button></div></div></div><p><strong>在DishMapper.java中声明<code>deleteById</code>方法并配置SQL：</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">/**</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">@param</span> id</span></span><br><span class="line"><span class="comment">    */</span></span><br><span class="line">   <span class="meta">@Delete(&quot;delete from dish where id = #&#123;id&#125;&quot;)</span></span><br><span class="line">   <span class="keyword">void</span> <span class="title function_">deleteById</span><span class="params">(Long id)</span>;</span><br></pre></td></tr></table></figure><p><strong>在DishFlavorMapper中声明<code>deleteByDishId</code>方法并配置SQL：</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></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 class="doctag">@param</span> dishId</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="meta">@Delete(&quot;delete from dish_flavor where dish_id = #&#123;dishId&#125;&quot;)</span></span><br><span class="line"><span class="keyword">void</span> <span class="title function_">deleteByDishId</span><span class="params">(Long dishId)</span>;</span><br></pre></td></tr></table></figure><h2 id="修改菜品"><a href="#修改菜品" class="headerlink" title="修改菜品"></a>修改菜品</h2><h3 id="需求分析和设计-8"><a href="#需求分析和设计-8" class="headerlink" title="需求分析和设计"></a>需求分析和设计</h3><p>点击修改按钮后跳转到菜品修改页面，修改页面会回显菜品相关信息，点击保存则完成修改操作<br><div class="img-wrap"><div class="img-bg"><img class="img" src="https://raw.githubusercontent.com/silvan2077/markdown_pic1/main/Picgo/image-20221122130837173.png"/></div></div></p><p><strong>接口设计</strong><br>分析可得该页面涉及四个接口：</p><ul><li>根据id查询菜品</li><li>根据类型查询分类(已实现)</li><li>文件上传(已实现)</li><li>修改菜品</li></ul><ul><li>根据id查询菜品<div class="tabs" id="修改菜品接口设计"><ul class="nav-tabs"><li class="tab active"><button type="button" data-href="#修改菜品接口设计-1">根据id查询菜品</button></li><li class="tab"><button type="button" data-href="#修改菜品接口设计-2">修改菜品</button></li></ul><div class="tab-contents"><div class="tab-item-content active" id="修改菜品接口设计-1"><div class="img-wrap"><div class="img-bg"><img class="img" src="https://raw.githubusercontent.com/silvan2077/markdown_pic1/main/Picgo/image-20221122131733147.png"/></div></div><div class="img-wrap"><div class="img-bg"><img class="img" src="https://raw.githubusercontent.com/silvan2077/markdown_pic1/main/Picgo/image-20221122131743509.png"/></div></div><button type="button" class="tab-to-top" aria-label="scroll to top"><i class="fas fa-arrow-up"></i></button></div><div class="tab-item-content" id="修改菜品接口设计-2"><div class="img-wrap"><div class="img-bg"><img class="img" src="https://raw.githubusercontent.com/silvan2077/markdown_pic1/main/Picgo/image-20221122131914533.png"/></div></div><div class="img-wrap"><div class="img-bg"><img class="img" src="https://raw.githubusercontent.com/silvan2077/markdown_pic1/main/Picgo/image-20221122131837393.png"/></div></div><button type="button" class="tab-to-top" aria-label="scroll to top"><i class="fas fa-arrow-up"></i></button></div></div></div></li></ul><h3 id="代码开发-8"><a href="#代码开发-8" class="headerlink" title="代码开发"></a>代码开发</h3><h4 id="根据id查询菜品实现"><a href="#根据id查询菜品实现" class="headerlink" title="根据id查询菜品实现"></a>根据id查询菜品实现</h4><div class="tabs" id="修改菜品代码开发-根据id查询菜品实现"><ul class="nav-tabs"><li class="tab active"><button type="button" data-href="#修改菜品代码开发-根据id查询菜品实现-1">Controller层</button></li><li class="tab"><button type="button" data-href="#修改菜品代码开发-根据id查询菜品实现-2">Service层接口</button></li><li class="tab"><button type="button" data-href="#修改菜品代码开发-根据id查询菜品实现-3">Service层实现类</button></li><li class="tab"><button type="button" data-href="#修改菜品代码开发-根据id查询菜品实现-4">Mapper层</button></li></ul><div class="tab-contents"><div class="tab-item-content active" id="修改菜品代码开发-根据id查询菜品实现-1"><p>在DishController中创建方法：<br><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><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="comment"> * 根据id查询菜品</span></span><br><span class="line"><span class="comment"> *</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@param</span> id</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@return</span></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="meta">@ApiOperation(&quot;根据id查询菜品&quot;)</span></span><br><span class="line"><span class="keyword">public</span> Result&lt;DishVO&gt; <span class="title function_">getById</span><span class="params">(<span class="meta">@PathVariable</span> Long id)</span> &#123;</span><br><span class="line">    log.info(<span class="string">&quot;根据id查询菜品：&#123;&#125;&quot;</span>, id);</span><br><span class="line">    <span class="type">DishVO</span> <span class="variable">dishVO</span> <span class="operator">=</span> dishService.getByIdWithFlavor(id);<span class="comment">//后绪步骤实现</span></span><br><span class="line">    <span class="keyword">return</span> Result.success(dishVO);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></p><button type="button" class="tab-to-top" aria-label="scroll to top"><i class="fas fa-arrow-up"></i></button></div><div class="tab-item-content" id="修改菜品代码开发-根据id查询菜品实现-2"><p>在DishService接口中声明getByIdWithFlavor方法：<br><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><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="comment"> * 根据id查询菜品和对应的口味数据</span></span><br><span class="line"><span class="comment"> *</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@param</span> id</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@return</span></span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line">DishVO <span class="title function_">getByIdWithFlavor</span><span class="params">(Long id)</span>;</span><br></pre></td></tr></table></figure></p><button type="button" class="tab-to-top" aria-label="scroll to top"><i class="fas fa-arrow-up"></i></button></div><div class="tab-item-content" id="修改菜品代码开发-根据id查询菜品实现-3"><p>在DishServiceImpl中实现getByIdWithFlavor方法：<br><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><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"> * 根据id查询菜品和对应的口味数据</span></span><br><span class="line"><span class="comment"> *</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@param</span> id</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@return</span></span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="keyword">public</span> DishVO <span class="title function_">getByIdWithFlavor</span><span class="params">(Long id)</span> &#123;</span><br><span class="line">    <span class="comment">//根据id查询菜品数据</span></span><br><span class="line">    <span class="type">Dish</span> <span class="variable">dish</span> <span class="operator">=</span> dishMapper.getById(id);</span><br><span class="line"></span><br><span class="line">    <span class="comment">//根据菜品id查询口味数据</span></span><br><span class="line">    List&lt;DishFlavor&gt; dishFlavors = dishFlavorMapper.getByDishId(id);<span class="comment">//后绪步骤实现</span></span><br><span class="line"></span><br><span class="line">    <span class="comment">//将查询到的数据封装到VO</span></span><br><span class="line">    <span class="type">DishVO</span> <span class="variable">dishVO</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">DishVO</span>();</span><br><span class="line">    BeanUtils.copyProperties(dish, dishVO);</span><br><span class="line">    dishVO.setFlavors(dishFlavors);</span><br><span class="line"></span><br><span class="line">    <span class="keyword">return</span> dishVO;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></p><button type="button" class="tab-to-top" aria-label="scroll to top"><i class="fas fa-arrow-up"></i></button></div><div class="tab-item-content" id="修改菜品代码开发-根据id查询菜品实现-4"><p>在DishFlavorMapper中声明getByDishId方法，并配置SQL：<br><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><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="comment"> * 根据菜品id查询对应的口味数据</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@param</span> dishId</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@return</span></span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="meta">@Select(&quot;select * from dish_flavor where dish_id = #&#123;dishId&#125;&quot;)</span></span><br><span class="line">List&lt;DishFlavor&gt; <span class="title function_">getByDishId</span><span class="params">(Long dishId)</span>;</span><br></pre></td></tr></table></figure></p><button type="button" class="tab-to-top" aria-label="scroll to top"><i class="fas fa-arrow-up"></i></button></div></div></div><h4 id="修改菜品实现"><a href="#修改菜品实现" class="headerlink" title="修改菜品实现"></a>修改菜品实现</h4><div class="tabs" id="修改菜品代码开发-修改菜品实现"><ul class="nav-tabs"><li class="tab active"><button type="button" data-href="#修改菜品代码开发-修改菜品实现-1">Controller层</button></li><li class="tab"><button type="button" data-href="#修改菜品代码开发-修改菜品实现-2">Service层接口</button></li><li class="tab"><button type="button" data-href="#修改菜品代码开发-修改菜品实现-3">Service层实现类</button></li><li class="tab"><button type="button" data-href="#修改菜品代码开发-修改菜品实现-4">Mapper层</button></li></ul><div class="tab-contents"><div class="tab-item-content active" id="修改菜品代码开发-修改菜品实现-1"><p><strong>根据修改菜品的接口定义在DishController中创建方法：</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></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">@param</span> dishDTO</span></span><br><span class="line"><span class="comment">    * <span class="doctag">@return</span></span></span><br><span class="line"><span class="comment">    */</span></span><br><span class="line">   <span class="meta">@PutMapping</span></span><br><span class="line">   <span class="meta">@ApiOperation(&quot;修改菜品&quot;)</span></span><br><span class="line">   <span class="keyword">public</span> Result <span class="title function_">update</span><span class="params">(<span class="meta">@RequestBody</span> DishDTO dishDTO)</span> &#123;</span><br><span class="line">       log.info(<span class="string">&quot;修改菜品：&#123;&#125;&quot;</span>, dishDTO);</span><br><span class="line">       dishService.updateWithFlavor(dishDTO);</span><br><span class="line">       <span class="keyword">return</span> Result.success();</span><br><span class="line">   &#125;</span><br></pre></td></tr></table></figure><button type="button" class="tab-to-top" aria-label="scroll to top"><i class="fas fa-arrow-up"></i></button></div><div class="tab-item-content" id="修改菜品代码开发-修改菜品实现-2"><p><strong>在DishService接口中声明updateWithFlavor方法：</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></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="comment">    * <span class="doctag">@param</span> dishDTO</span></span><br><span class="line"><span class="comment">    */</span></span><br><span class="line">   <span class="keyword">void</span> <span class="title function_">updateWithFlavor</span><span class="params">(DishDTO dishDTO)</span>;</span><br></pre></td></tr></table></figure><button type="button" class="tab-to-top" aria-label="scroll to top"><i class="fas fa-arrow-up"></i></button></div><div class="tab-item-content" id="修改菜品代码开发-修改菜品实现-3"><p><strong>在DishServiceImpl中实现updateWithFlavor方法：</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">    * 根据id修改菜品基本信息和对应的口味信息</span></span><br><span class="line"><span class="comment">    *</span></span><br><span class="line"><span class="comment">    * <span class="doctag">@param</span> dishDTO</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_">updateWithFlavor</span><span class="params">(DishDTO dishDTO)</span> &#123;</span><br><span class="line">       <span class="type">Dish</span> <span class="variable">dish</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">Dish</span>();</span><br><span class="line">       BeanUtils.copyProperties(dishDTO, dish);</span><br><span class="line"></span><br><span class="line">       <span class="comment">//修改菜品表基本信息</span></span><br><span class="line">       dishMapper.update(dish);</span><br><span class="line"></span><br><span class="line">       <span class="comment">//删除原有的口味数据</span></span><br><span class="line">       dishFlavorMapper.deleteByDishId(dishDTO.getId());</span><br><span class="line"></span><br><span class="line">       <span class="comment">//重新插入口味数据</span></span><br><span class="line">       List&lt;DishFlavor&gt; flavors = dishDTO.getFlavors();</span><br><span class="line">       <span class="keyword">if</span> (flavors != <span class="literal">null</span> &amp;&amp; flavors.size() &gt; <span class="number">0</span>) &#123;</span><br><span class="line">           flavors.forEach(dishFlavor -&gt; &#123;</span><br><span class="line">               dishFlavor.setDishId(dishDTO.getId());</span><br><span class="line">           &#125;);</span><br><span class="line">           <span class="comment">//向口味表插入n条数据</span></span><br><span class="line">           dishFlavorMapper.insertBatch(flavors);</span><br><span class="line">       &#125;</span><br><span class="line">   &#125;</span><br></pre></td></tr></table></figure><button type="button" class="tab-to-top" aria-label="scroll to top"><i class="fas fa-arrow-up"></i></button></div><div class="tab-item-content" id="修改菜品代码开发-修改菜品实现-4"><p><strong>在DishMapper中，声明update方法：</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">/**</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="comment">    * <span class="doctag">@param</span> dish</span></span><br><span class="line"><span class="comment">    */</span></span><br><span class="line">   <span class="meta">@AutoFill(value = OperationType.UPDATE)</span></span><br><span class="line">   <span class="keyword">void</span> <span class="title function_">update</span><span class="params">(Dish dish)</span>;</span><br></pre></td></tr></table></figure><p><strong>并在DishMapper.xml文件中编写SQL:</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><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="tag">&lt;<span class="name">update</span> <span class="attr">id</span>=<span class="string">&quot;update&quot;</span>&gt;</span></span><br><span class="line">        update dish</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;name != null&quot;</span>&gt;</span>name = #&#123;name&#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;categoryId != null&quot;</span>&gt;</span>category_id = #&#123;categoryId&#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;price != null&quot;</span>&gt;</span>price = #&#123;price&#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;image != null&quot;</span>&gt;</span>image = #&#123;image&#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;description != null&quot;</span>&gt;</span>description = #&#123;description&#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;status != null&quot;</span>&gt;</span>status = #&#123;status&#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;updateTime != null&quot;</span>&gt;</span>update_time = #&#123;updateTime&#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;updateUser != null&quot;</span>&gt;</span>update_user = #&#123;updateUser&#125;,<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></pre></td></tr></table></figure><button type="button" class="tab-to-top" aria-label="scroll to top"><i class="fas fa-arrow-up"></i></button></div></div></div><h2 id="菜品的起售-停售"><a href="#菜品的起售-停售" class="headerlink" title="菜品的起售/停售"></a>菜品的起售/停售</h2><h1 id="day04"><a href="#day04" class="headerlink" title="day04"></a>day04</h1><h2 id="完成内容"><a href="#完成内容" class="headerlink" title="完成内容"></a>完成内容</h2><p>完成套餐管理模块的相关业务功能，包括：</p><ul><li>新增套餐</li><li>套餐分页查询</li><li>删除套餐</li><li>修改套餐</li><li>套餐的起售/停售</li></ul><h2 id="新增套餐"><a href="#新增套餐" class="headerlink" title="新增套餐"></a>新增套餐</h2><h3 id="需求分析和设计-9"><a href="#需求分析和设计-9" class="headerlink" title="需求分析和设计"></a>需求分析和设计</h3><div class="img-wrap"><div class="img-bg"><img class="img" src="https://raw.githubusercontent.com/silvan2077/markdown_pic1/main/Picgo/image-20221018135930842.png"/></div></div><div class="note info no-icon flat"><p>业务规则：</p><ul><li>套餐名称唯一</li><li>套餐必须属于某个分类</li><li>套餐必须包含菜品</li><li>名称、分类、价格、图片为必填项</li><li>添加菜品窗口需要根据分类类型来展示菜品</li><li>新增的套餐默认为停售状态</li></ul></div><p><strong>接口设计</strong><br>涉及四个接口：</p><ul><li>根据类型查询分类（已完成）</li><li>根据分类id查询菜品</li><li>图片上传（已完成）</li><li>新增套餐</li></ul><div class="tabs" id="新增套餐"><ul class="nav-tabs"><li class="tab active"><button type="button" data-href="#新增套餐-1">根据分类id查询菜品</button></li><li class="tab"><button type="button" data-href="#新增套餐-2">新增套餐</button></li></ul><div class="tab-contents"><div class="tab-item-content active" id="新增套餐-1"><div class="img-wrap"><div class="img-bg"><img class="img" src="https://raw.githubusercontent.com/silvan2077/markdown_pic1/main/Picgo/image-20221018141521068.png"/></div></div><button type="button" class="tab-to-top" aria-label="scroll to top"><i class="fas fa-arrow-up"></i></button></div><div class="tab-item-content" id="新增套餐-2"><div class="img-wrap"><div class="img-bg"><img class="img" src="https://raw.githubusercontent.com/silvan2077/markdown_pic1/main/Picgo/image-20221018141606787.png"/></div></div><button type="button" class="tab-to-top" aria-label="scroll to top"><i class="fas fa-arrow-up"></i></button></div></div></div><p><strong>数据库设计</strong></p><div class="tabs" id="新增套餐数据库表"><ul class="nav-tabs"><li class="tab active"><button type="button" data-href="#新增套餐数据库表-1">setmeal表</button></li><li class="tab"><button type="button" data-href="#新增套餐数据库表-2">setmeal_dish表</button></li></ul><div class="tab-contents"><div class="tab-item-content active" id="新增套餐数据库表-1"><p>setmeal表为套餐表，用于存储套餐的信息。</p><div class="table-container"><table><thead><tr><th>字段名</th><th>数据类型</th><th>说明</th><th>备注</th></tr></thead><tbody><tr><td>id</td><td>bigint</td><td>主键</td><td>自增</td></tr><tr><td>name</td><td>varchar(32)</td><td>套餐名称</td><td>唯一</td></tr><tr><td>category_id</td><td>bigint</td><td>分类id</td><td>逻辑外键</td></tr><tr><td>price</td><td>decimal(10,2)</td><td>套餐价格</td><td></td></tr><tr><td>image</td><td>varchar(255)</td><td>图片路径</td><td></td></tr><tr><td>description</td><td>varchar(255)</td><td>套餐描述</td><td></td></tr><tr><td>status</td><td>int</td><td>售卖状态</td><td>1起售 0停售</td></tr><tr><td>create_time</td><td>datetime</td><td>创建时间</td><td></td></tr><tr><td>update_time</td><td>datetime</td><td>最后修改时间</td><td></td></tr><tr><td>create_user</td><td>bigint</td><td>创建人id</td><td></td></tr><tr><td>update_user</td><td>bigint</td><td>最后修改人id</td></tr></tbody></table></div><button type="button" class="tab-to-top" aria-label="scroll to top"><i class="fas fa-arrow-up"></i></button></div><div class="tab-item-content" id="新增套餐数据库表-2"><p>setmeal_dish表为套餐菜品关系表，用于存储套餐和菜品的关联关系</p><div class="table-container"><table><thead><tr><th>字段名</th><th>数据类型</th><th>说明</th><th>备注</th></tr></thead><tbody><tr><td>id</td><td>bigint</td><td>主键</td><td>自增</td></tr><tr><td>setmeal_id</td><td>bigint</td><td>套餐id</td><td>逻辑外键</td></tr><tr><td>dish_id</td><td>bigint</td><td>菜品id</td><td>逻辑外键</td></tr><tr><td>name</td><td>varchar(32)</td><td>菜品名称</td><td>冗余字段</td></tr><tr><td>price</td><td>decimal(10,2)</td><td>菜品单价</td><td>冗余字段</td></tr><tr><td>copies</td><td>int</td><td>菜品份数</td></tr></tbody></table></div><button type="button" class="tab-to-top" aria-label="scroll to top"><i class="fas fa-arrow-up"></i></button></div></div></div><h3 id="代码实现"><a href="#代码实现" class="headerlink" title="代码实现"></a>代码实现</h3><h4 id="Controller层-4"><a href="#Controller层-4" class="headerlink" title="Controller层"></a>Controller层</h4><div class="tabs" id="新增套餐controller层"><ul class="nav-tabs"><li class="tab active"><button type="button" data-href="#新增套餐controller层-1">DishController</button></li><li class="tab"><button type="button" data-href="#新增套餐controller层-2">SetmealController</button></li></ul><div class="tab-contents"><div class="tab-item-content active" id="新增套餐controller层-1"><p>在DishController中添加根据分类id查询菜品的方法：<br><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><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">     * 根据分类id查询菜品</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@param</span> categoryId</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@return</span></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="meta">@ApiOperation(&quot;根据分类id查询菜品&quot;)</span></span><br><span class="line"><span class="keyword">public</span> Result&lt;List&lt;Dish&gt;&gt; <span class="title function_">list</span><span class="params">(Long categoryId)</span>&#123;</span><br><span class="line">    List&lt;Dish&gt; list = dishService.list(categoryId);</span><br><span class="line">    <span class="keyword">return</span> Result.success(list);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></p><button type="button" class="tab-to-top" aria-label="scroll to top"><i class="fas fa-arrow-up"></i></button></div><div class="tab-item-content" id="新增套餐controller层-2"><p>在SetmealController中添加新增套餐的方法：<br><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><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"> * 套餐管理</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;/admin/setmeal&quot;)</span></span><br><span class="line"><span class="meta">@Api(tags = &quot;套餐相关接口&quot;)</span></span><br><span class="line"><span class="meta">@Slf4j</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">SetmealController</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> SetmealService setmealService;</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 class="doctag">@param</span> setmealDTO</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@return</span></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="meta">@ApiOperation(&quot;新增套餐&quot;)</span></span><br><span class="line">    <span class="keyword">public</span> Result <span class="title function_">save</span><span class="params">(<span class="meta">@RequestBody</span> SetmealDTO setmealDTO)</span> &#123;</span><br><span class="line">        setmealService.saveWithDish(setmealDTO);</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><span class="line"></span><br></pre></td></tr></table></figure></p><button type="button" class="tab-to-top" aria-label="scroll to top"><i class="fas fa-arrow-up"></i></button></div></div></div><h4 id="Service层-1"><a href="#Service层-1" class="headerlink" title="Service层"></a>Service层</h4><div class="tabs" id="新增套餐service层"><ul class="nav-tabs"><li class="tab active"><button type="button" data-href="#新增套餐service层-1">DishService</button></li><li class="tab"><button type="button" data-href="#新增套餐service层-2">DishServiceImpl</button></li><li class="tab"><button type="button" data-href="#新增套餐service层-3">SetmealService</button></li><li class="tab"><button type="button" data-href="#新增套餐service层-4">SetmealServiceImpl</button></li></ul><div class="tab-contents"><div class="tab-item-content active" id="新增套餐service层-1"><p>在DishService中添加根据分类id查询菜品的方法：<br><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><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="comment">     * 根据分类id查询菜品</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@param</span> categoryId</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@return</span></span></span><br><span class="line"><span class="comment">*/</span></span><br><span class="line">List&lt;Dish&gt; <span class="title function_">list</span><span class="params">(Long categoryId)</span>;</span><br></pre></td></tr></table></figure></p><button type="button" class="tab-to-top" aria-label="scroll to top"><i class="fas fa-arrow-up"></i></button></div><div class="tab-item-content" id="新增套餐service层-2"><p>在DishServiceImpl中实现该方法：<br><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><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">     * 根据分类id查询菜品</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@param</span> categoryId</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@return</span></span></span><br><span class="line"><span class="comment">*/</span></span><br><span class="line"><span class="keyword">public</span> List&lt;Dish&gt; <span class="title function_">list</span><span class="params">(Long categoryId)</span> &#123;</span><br><span class="line">    <span class="type">Dish</span> <span class="variable">dish</span> <span class="operator">=</span> Dish.builder()</span><br><span class="line">        .categoryId(categoryId)</span><br><span class="line">        .status(StatusConstant.ENABLE)</span><br><span class="line">        .build();</span><br><span class="line">    <span class="keyword">return</span> dishMapper.list(dish);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></p><button type="button" class="tab-to-top" aria-label="scroll to top"><i class="fas fa-arrow-up"></i></button></div><div class="tab-item-content" id="新增套餐service层-3"><p>在SetmealService中添加新增套餐的方法<br><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><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">interface</span> <span class="title class_">SetmealService</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 class="doctag">@param</span> setmealDTO</span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    <span class="keyword">void</span> <span class="title function_">saveWithDish</span><span class="params">(SetmealDTO setmealDTO)</span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></p><button type="button" class="tab-to-top" aria-label="scroll to top"><i class="fas fa-arrow-up"></i></button></div><div class="tab-item-content" id="新增套餐service层-4"><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><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="meta">@Slf4j</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">SetmealServiceImpl</span> <span class="keyword">implements</span> <span class="title class_">SetmealService</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> SetmealMapper setmealMapper;</span><br><span class="line">    <span class="meta">@Autowired</span></span><br><span class="line">    <span class="keyword">private</span> SetmealDishMapper setmealDishMapper;</span><br><span class="line">    <span class="meta">@Autowired</span></span><br><span class="line">    <span class="keyword">private</span> DishMapper dishMapper;</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 class="doctag">@param</span> setmealDTO</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_">saveWithDish</span><span class="params">(SetmealDTO setmealDTO)</span> &#123;</span><br><span class="line">        <span class="type">Setmeal</span> <span class="variable">setmeal</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">Setmeal</span>();</span><br><span class="line">        BeanUtils.copyProperties(setmealDTO, setmeal);</span><br><span class="line"></span><br><span class="line">        <span class="comment">//向套餐表插入数据</span></span><br><span class="line">        setmealMapper.insert(setmeal);</span><br><span class="line"></span><br><span class="line">        <span class="comment">//获取生成的套餐id</span></span><br><span class="line">        <span class="type">Long</span> <span class="variable">setmealId</span> <span class="operator">=</span> setmeal.getId();</span><br><span class="line"></span><br><span class="line">        List&lt;SetmealDish&gt; setmealDishes = setmealDTO.getSetmealDishes();</span><br><span class="line">        setmealDishes.forEach(setmealDish -&gt; &#123;</span><br><span class="line">            setmealDish.setSetmealId(setmealId);</span><br><span class="line">        &#125;);</span><br><span class="line"></span><br><span class="line">        <span class="comment">//保存套餐和菜品的关联关系</span></span><br><span class="line">        setmealDishMapper.insertBatch(setmealDishes);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><button type="button" class="tab-to-top" aria-label="scroll to top"><i class="fas fa-arrow-up"></i></button></div></div></div><h4 id="Mapper层-4"><a href="#Mapper层-4" class="headerlink" title="Mapper层"></a>Mapper层</h4><div class="tabs" id="新增套餐mapper层"><ul class="nav-tabs"><li class="tab active"><button type="button" data-href="#新增套餐mapper层-1">DishMapper</button></li><li class="tab"><button type="button" data-href="#新增套餐mapper层-2">SetmealMapper</button></li><li class="tab"><button type="button" data-href="#新增套餐mapper层-3">SetmealDishMapper</button></li></ul><div class="tab-contents"><div class="tab-item-content active" id="新增套餐mapper层-1"><p>在DishMapper中添加list方法：<br><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><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="comment">     * 动态条件查询菜品</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@param</span> dish</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@return</span></span></span><br><span class="line"><span class="comment">*/</span></span><br><span class="line">List&lt;Dish&gt; <span class="title function_">list</span><span class="params">(Dish dish)</span>;</span><br></pre></td></tr></table></figure></p><p>在DishMapper.xml文件中编写SQL：<br><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="tag">&lt;<span class="name">select</span> <span class="attr">id</span>=<span class="string">&quot;list&quot;</span> <span class="attr">resultType</span>=<span class="string">&quot;Dish&quot;</span> <span class="attr">parameterType</span>=<span class="string">&quot;Dish&quot;</span>&gt;</span></span><br><span class="line">    select * from dish</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;name != null&quot;</span>&gt;</span></span><br><span class="line">            and name like concat(&#x27;%&#x27;,#&#123;name&#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;categoryId != null&quot;</span>&gt;</span></span><br><span class="line">            and category_id = #&#123;categoryId&#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">            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">where</span>&gt;</span></span><br><span class="line">    order by create_time desc</span><br><span class="line"><span class="tag">&lt;/<span class="name">select</span>&gt;</span></span><br></pre></td></tr></table></figure></p><button type="button" class="tab-to-top" aria-label="scroll to top"><i class="fas fa-arrow-up"></i></button></div><div class="tab-item-content" id="新增套餐mapper层-2"><p>在SetmealMapper中添加insert方法：<br><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><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="comment">     * 新增套餐</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@param</span> setmeal</span></span><br><span class="line"><span class="comment">*/</span></span><br><span class="line"><span class="meta">@AutoFill(OperationType.INSERT)</span></span><br><span class="line"><span class="keyword">void</span> <span class="title function_">insert</span><span class="params">(Setmeal setmeal)</span>;</span><br></pre></td></tr></table></figure><br>在SetmealMapper.xml文件中编写SQL：<br><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="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;Setmeal&quot;</span> <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 setmeal</span><br><span class="line">    (category_id, name, price, status, description, image, create_time, update_time, create_user, update_user)</span><br><span class="line">    values (#&#123;categoryId&#125;, #&#123;name&#125;, #&#123;price&#125;, #&#123;status&#125;, #&#123;description&#125;, #&#123;image&#125;, #&#123;createTime&#125;, #&#123;updateTime&#125;,</span><br><span class="line">    #&#123;createUser&#125;, #&#123;updateUser&#125;)</span><br><span class="line"><span class="tag">&lt;/<span class="name">insert</span>&gt;</span></span><br></pre></td></tr></table></figure></p><button type="button" class="tab-to-top" aria-label="scroll to top"><i class="fas fa-arrow-up"></i></button></div><div class="tab-item-content" id="新增套餐mapper层-3"><p>在SetmealDishMapper中添加insertBatch方法：<br><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><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="comment">     * 批量保存套餐和菜品的关联关系</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@param</span> setmealDishes</span></span><br><span class="line"><span class="comment">*/</span></span><br><span class="line"><span class="keyword">void</span> <span class="title function_">insertBatch</span><span class="params">(List&lt;SetmealDish&gt; setmealDishes)</span>;</span><br></pre></td></tr></table></figure><br>在SetmealDishMapper.xml文件中编写SQL：<br><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></pre></td><td class="code"><pre><span class="line"><span class="tag">&lt;<span class="name">insert</span> <span class="attr">id</span>=<span class="string">&quot;insertBatch&quot;</span> <span class="attr">parameterType</span>=<span class="string">&quot;list&quot;</span>&gt;</span></span><br><span class="line">    insert into setmeal_dish</span><br><span class="line">    (setmeal_id,dish_id,name,price,copies)</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;setmealDishes&quot;</span> <span class="attr">item</span>=<span class="string">&quot;sd&quot;</span> <span class="attr">separator</span>=<span class="string">&quot;,&quot;</span>&gt;</span></span><br><span class="line">        (#&#123;sd.setmealId&#125;,#&#123;sd.dishId&#125;,#&#123;sd.name&#125;,#&#123;sd.price&#125;,#&#123;sd.copies&#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></pre></td></tr></table></figure></p><button type="button" class="tab-to-top" aria-label="scroll to top"><i class="fas fa-arrow-up"></i></button></div></div></div><h3 id="功能测试-8"><a href="#功能测试-8" class="headerlink" title="功能测试"></a>功能测试</h3><p>点击新增套餐按钮，并填写相关套餐信息<br><div class="img-wrap"><div class="img-bg"><img class="img" src="https://raw.githubusercontent.com/silvan2077/markdown_pic1/main/Picgo/20251114144406.png"/></div></div><br>点击保存按钮后，去数据库的setmeal表中查看数据是否添加成功<br><div class="img-wrap"><div class="img-bg"><img class="img" src="https://raw.githubusercontent.com/silvan2077/markdown_pic1/main/Picgo/20251114144759.png"/></div></div></p><h2 id="套餐分页查询"><a href="#套餐分页查询" class="headerlink" title="套餐分页查询"></a>套餐分页查询</h2><h3 id="需求分析和设计-10"><a href="#需求分析和设计-10" class="headerlink" title="需求分析和设计"></a>需求分析和设计</h3><div class="img-wrap"><div class="img-bg"><img class="img" src="https://raw.githubusercontent.com/silvan2077/markdown_pic1/main/Picgo/image-20221018152429246.png"/></div></div><div class="note info no-icon flat"><p>业务规则：</p><ul><li>根据页码进行分页展示</li><li>每页展示10条数据</li><li>可以根据需要，按照套餐名称、分类、售卖状态进行查询</li></ul></div><p><strong>接口设计</strong><br><div class="img-wrap"><div class="img-bg"><img class="img" src="https://raw.githubusercontent.com/silvan2077/markdown_pic1/main/Picgo/image-20221018152731141.png"/></div></div></p><h3 id="代码实现-1"><a href="#代码实现-1" class="headerlink" title="代码实现"></a>代码实现</h3><h4 id="Controller层-5"><a href="#Controller层-5" class="headerlink" title="Controller层"></a>Controller层</h4><p>在SetmealController中添加分页查询方法：<br><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><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 class="doctag">@param</span> setmealPageQueryDTO</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@return</span></span></span><br><span class="line"><span class="comment">*/</span></span><br><span class="line"><span class="meta">@GetMapping(&quot;/page&quot;)</span></span><br><span class="line"><span class="meta">@ApiOperation(&quot;分页查询&quot;)</span></span><br><span class="line"><span class="keyword">public</span> Result&lt;PageResult&gt; <span class="title function_">page</span><span class="params">(SetmealPageQueryDTO setmealPageQueryDTO)</span> &#123;</span><br><span class="line">    <span class="type">PageResult</span> <span class="variable">pageResult</span> <span class="operator">=</span> setmealService.pageQuery(setmealPageQueryDTO);</span><br><span class="line">    <span class="keyword">return</span> Result.success(pageResult);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></p><h4 id="Service层-2"><a href="#Service层-2" class="headerlink" title="Service层"></a>Service层</h4><p>在SetmealService中添加分页查询方法：<br><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><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="comment">     * 分页查询</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@param</span> setmealPageQueryDTO</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@return</span></span></span><br><span class="line"><span class="comment">*/</span></span><br><span class="line">PageResult <span class="title function_">pageQuery</span><span class="params">(SetmealPageQueryDTO setmealPageQueryDTO)</span>;</span><br></pre></td></tr></table></figure></p><p>在SetmealServiceImpl中实现该方法：<br><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><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="comment">     * 分页查询</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@param</span> setmealPageQueryDTO</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@return</span></span></span><br><span class="line"><span class="comment">*/</span></span><br><span class="line"><span class="keyword">public</span> PageResult <span class="title function_">pageQuery</span><span class="params">(SetmealPageQueryDTO setmealPageQueryDTO)</span> &#123;</span><br><span class="line">    <span class="type">int</span> <span class="variable">pageNum</span> <span class="operator">=</span> setmealPageQueryDTO.getPage();</span><br><span class="line">    <span class="type">int</span> <span class="variable">pageSize</span> <span class="operator">=</span> setmealPageQueryDTO.getPageSize();</span><br><span class="line"></span><br><span class="line">    PageHelper.startPage(pageNum, pageSize);</span><br><span class="line">    Page&lt;SetmealVO&gt; page = setmealMapper.pageQuery(setmealPageQueryDTO);</span><br><span class="line">    <span class="keyword">return</span> <span class="keyword">new</span> <span class="title class_">PageResult</span>(page.getTotal(), page.getResult());</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></p><h4 id="Mapper层-5"><a href="#Mapper层-5" class="headerlink" title="Mapper层"></a>Mapper层</h4><p>在SetmealMapper中添加分页查询方法：<br><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><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="comment">     * 分页查询</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@param</span> setmealPageQueryDTO</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@return</span></span></span><br><span class="line"><span class="comment">*/</span></span><br><span class="line">Page&lt;SetmealVO&gt; <span class="title function_">pageQuery</span><span class="params">(SetmealPageQueryDTO setmealPageQueryDTO)</span>;</span><br></pre></td></tr></table></figure><br>在SetmealMapper.xml文件中编写SQL：<br><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></pre></td><td class="code"><pre><span class="line"><span class="tag">&lt;<span class="name">select</span> <span class="attr">id</span>=<span class="string">&quot;pageQuery&quot;</span> <span class="attr">resultType</span>=<span class="string">&quot;com.sky.vo.SetmealVO&quot;</span>&gt;</span></span><br><span class="line">    select</span><br><span class="line">    s.*,c.name categoryName</span><br><span class="line">    from</span><br><span class="line">    setmeal s</span><br><span class="line">    left join</span><br><span class="line">    category c</span><br><span class="line">    on</span><br><span class="line">    s.category_id = c.id</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;name != null&quot;</span>&gt;</span></span><br><span class="line">            and s.name like concat(&#x27;%&#x27;,#&#123;name&#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 s.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;categoryId != null&quot;</span>&gt;</span></span><br><span class="line">            and s.category_id = #&#123;categoryId&#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 s.create_time desc</span><br><span class="line"><span class="tag">&lt;/<span class="name">select</span>&gt;</span></span><br></pre></td></tr></table></figure></p><h3 id="功能测试-9"><a href="#功能测试-9" class="headerlink" title="功能测试"></a>功能测试</h3><p>重启服务器，访问<a href="http://localhost">http://localhost</a>，点击套餐页面的数据已经能够正常显示在浏览器中了<br><div class="img-wrap"><div class="img-bg"><img class="img" src="https://raw.githubusercontent.com/silvan2077/markdown_pic1/main/Picgo/20251114150708.png"/></div></div></p><h2 id="删除套餐"><a href="#删除套餐" class="headerlink" title="删除套餐"></a>删除套餐</h2><h3 id="需求分析和设计-11"><a href="#需求分析和设计-11" class="headerlink" title="需求分析和设计"></a>需求分析和设计</h3><p>点击删除套餐按钮即可在数据库中删除该套餐，套餐可以单个删除，也可以批量删除,<strong>起售中的套餐不能删除</strong><br><div class="img-wrap"><div class="img-bg"><img class="img" src="https://raw.githubusercontent.com/silvan2077/markdown_pic1/main/Picgo/image-20221018153756531.png"/></div></div></p><p><strong>接口设计</strong><br><div class="img-wrap"><div class="img-bg"><img class="img" src="https://raw.githubusercontent.com/silvan2077/markdown_pic1/main/Picgo/image-20221018154541067.png"/></div></div></p><h3 id="代码实现-2"><a href="#代码实现-2" class="headerlink" title="代码实现"></a>代码实现</h3><h4 id="Controller层-6"><a href="#Controller层-6" class="headerlink" title="Controller层"></a>Controller层</h4><p>在SetmealController中添加批量删除方法<br><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><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 class="doctag">@param</span> ids</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@return</span></span></span><br><span class="line"><span class="comment">*/</span></span><br><span class="line"><span class="meta">@DeleteMapping</span></span><br><span class="line"><span class="meta">@ApiOperation(&quot;批量删除套餐&quot;)</span></span><br><span class="line"><span class="keyword">public</span> Result <span class="title function_">delete</span><span class="params">(<span class="meta">@RequestParam</span> List&lt;Long&gt; ids)</span>&#123;</span><br><span class="line">    setmealService.deleteBatch(ids);</span><br><span class="line">    <span class="keyword">return</span> Result.success();</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></p><h4 id="Service层-3"><a href="#Service层-3" class="headerlink" title="Service层"></a>Service层</h4><p>在SetmealService中添加批量删除方法<br><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><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="comment">     * 批量删除套餐</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@param</span> ids</span></span><br><span class="line"><span class="comment">*/</span></span><br><span class="line"><span class="keyword">void</span> <span class="title function_">deleteBatch</span><span class="params">(List&lt;Long&gt; ids)</span>;</span><br></pre></td></tr></table></figure><br>在SetmealServiceImpl中实现该方法<br><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><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 class="doctag">@param</span> ids</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_">deleteBatch</span><span class="params">(List&lt;Long&gt; ids)</span> &#123;</span><br><span class="line">    ids.forEach(id -&gt; &#123;</span><br><span class="line">        <span class="type">Setmeal</span> <span class="variable">setmeal</span> <span class="operator">=</span> setmealMapper.getById(id);</span><br><span class="line">        <span class="keyword">if</span>(StatusConstant.ENABLE == setmeal.getStatus())&#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_">DeletionNotAllowedException</span>(MessageConstant.SETMEAL_ON_SALE);</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;);</span><br><span class="line"></span><br><span class="line">    ids.forEach(setmealId -&gt; &#123;</span><br><span class="line">        <span class="comment">//删除套餐表中的数据</span></span><br><span class="line">        setmealMapper.deleteById(setmealId);</span><br><span class="line">        <span class="comment">//删除套餐菜品关系表中的数据</span></span><br><span class="line">        setmealDishMapper.deleteBySetmealId(setmealId);</span><br><span class="line">    &#125;);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></p><h4 id="Mapper层-6"><a href="#Mapper层-6" class="headerlink" title="Mapper层"></a>Mapper层</h4><div class="tabs" id="删除套餐mapper层"><ul class="nav-tabs"><li class="tab active"><button type="button" data-href="#删除套餐mapper层-1">SetmealMapper</button></li><li class="tab"><button type="button" data-href="#删除套餐mapper层-2">SetmealDishMapper</button></li></ul><div class="tab-contents"><div class="tab-item-content active" id="删除套餐mapper层-1"><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><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="comment">     * 根据id查询套餐</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@param</span> id</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@return</span></span></span><br><span class="line"><span class="comment">*/</span></span><br><span class="line"><span class="meta">@Select(&quot;select * from setmeal where id = #&#123;id&#125;&quot;)</span></span><br><span class="line">Setmeal <span class="title function_">getById</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">     * 根据id删除套餐</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@param</span> setmealId</span></span><br><span class="line"><span class="comment">*/</span></span><br><span class="line"><span class="meta">@Delete(&quot;delete from setmeal where id = #&#123;id&#125;&quot;)</span></span><br><span class="line"><span class="keyword">void</span> <span class="title function_">deleteById</span><span class="params">(Long setmealId)</span>;</span><br></pre></td></tr></table></figure><button type="button" class="tab-to-top" aria-label="scroll to top"><i class="fas fa-arrow-up"></i></button></div><div class="tab-item-content" id="删除套餐mapper层-2"><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><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="comment">     * 根据套餐id删除套餐和菜品的关联关系</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@param</span> setmealId</span></span><br><span class="line"><span class="comment">*/</span></span><br><span class="line"><span class="meta">@Delete(&quot;delete from setmeal_dish where setmeal_id = #&#123;setmealId&#125;&quot;)</span></span><br><span class="line"><span class="keyword">void</span> <span class="title function_">deleteBySetmealId</span><span class="params">(Long setmealId)</span>;</span><br></pre></td></tr></table></figure><button type="button" class="tab-to-top" aria-label="scroll to top"><i class="fas fa-arrow-up"></i></button></div></div></div><h3 id="功能测试-10"><a href="#功能测试-10" class="headerlink" title="功能测试"></a>功能测试</h3><p>可以通过前后端联调，也可以通过接口文档测试，此处通过接口文档简单进行测试：<br><div class="img-wrap"><div class="img-bg"><img class="img" src="https://raw.githubusercontent.com/silvan2077/markdown_pic1/main/Picgo/20251114171126.png"/></div></div></p><p>再次刷新数据库，该id对应的套餐就被删除了</p><h2 id="修改套餐"><a href="#修改套餐" class="headerlink" title="修改套餐"></a>修改套餐</h2><h3 id="需求分析和设计-12"><a href="#需求分析和设计-12" class="headerlink" title="需求分析和设计"></a>需求分析和设计</h3><p>和之前一样，修改套餐需要有数据的回显和对数据的更新<br><div class="img-wrap"><div class="img-bg"><img class="img" src="https://raw.githubusercontent.com/silvan2077/markdown_pic1/main/Picgo/image-20221018160214225.png"/></div></div></p><p><strong>接口设计</strong><br>共涉及5个接口</p><ul><li>根据id查询套餐</li><li>根据类型查询分类（已完成）</li><li>根据分类id查询菜品（已完成）</li><li>图片上传（已完成）</li><li>修改套餐</li></ul><div class="tabs" id="修改套餐接口设计"><ul class="nav-tabs"><li class="tab active"><button type="button" data-href="#修改套餐接口设计-1">根据id查询套餐</button></li><li class="tab"><button type="button" data-href="#修改套餐接口设计-2">修改套餐</button></li></ul><div class="tab-contents"><div class="tab-item-content active" id="修改套餐接口设计-1"><div class="img-wrap"><div class="img-bg"><img class="img" src="https://raw.githubusercontent.com/silvan2077/markdown_pic1/main/Picgo/image-20221018160915177.png" style="width:400px;"/></div></div><div class="img-wrap"><div class="img-bg"><img class="img" src="https://raw.githubusercontent.com/silvan2077/markdown_pic1/main/Picgo/image-20221018160949864.png" style="width:400px;"/></div></div><button type="button" class="tab-to-top" aria-label="scroll to top"><i class="fas fa-arrow-up"></i></button></div><div class="tab-item-content" id="修改套餐接口设计-2"><div class="img-wrap"><div class="img-bg"><img class="img" src="https://raw.githubusercontent.com/silvan2077/markdown_pic1/main/Picgo/image-20221018161046352.png" style="width:400px;"/></div></div><div class="img-wrap"><div class="img-bg"><img class="img" src="https://raw.githubusercontent.com/silvan2077/markdown_pic1/main/Picgo/image-20221018161117780.png" style="width:400px;"/></div></div><div class="img-wrap"><div class="img-bg"><img class="img" src="https://raw.githubusercontent.com/silvan2077/markdown_pic1/main/Picgo/image-20221018161139861.png"/></div></div><button type="button" class="tab-to-top" aria-label="scroll to top"><i class="fas fa-arrow-up"></i></button></div></div></div><h3 id="代码实现-3"><a href="#代码实现-3" class="headerlink" title="代码实现"></a>代码实现</h3><h4 id="Controller层-7"><a href="#Controller层-7" class="headerlink" title="Controller层"></a>Controller层</h4><p>在SetmealController中添加<code>数据回显</code>和<code>修改套餐</code>的方法<br><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><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">     * 根据id查询套餐，用于修改页面回显数据</span></span><br><span class="line"><span class="comment">     *</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@param</span> id</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@return</span></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="meta">@ApiOperation(&quot;根据id查询套餐&quot;)</span></span><br><span class="line"><span class="keyword">public</span> Result&lt;SetmealVO&gt; <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="type">SetmealVO</span> <span class="variable">setmealVO</span> <span class="operator">=</span> setmealService.getByIdWithDish(id);</span><br><span class="line">    <span class="keyword">return</span> Result.success(setmealVO);</span><br><span 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 class="doctag">@param</span> setmealDTO</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@return</span></span></span><br><span class="line"><span class="comment">*/</span></span><br><span class="line"><span class="meta">@PutMapping</span></span><br><span class="line"><span class="meta">@ApiOperation(&quot;修改套餐&quot;)</span></span><br><span class="line"><span class="keyword">public</span> Result <span class="title function_">update</span><span class="params">(<span class="meta">@RequestBody</span> SetmealDTO setmealDTO)</span> &#123;</span><br><span class="line">    setmealService.update(setmealDTO);</span><br><span class="line">    <span class="keyword">return</span> Result.success();</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></p><h4 id="Service层-4"><a href="#Service层-4" class="headerlink" title="Service层"></a>Service层</h4><p>在SetmealService中添加<code>数据回显</code>和<code>修改套餐</code>的方法<br><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><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="comment">     * 根据id查询套餐，用于修改页面回显数据</span></span><br><span class="line"><span class="comment">     *</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@param</span> id</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@return</span></span></span><br><span class="line"><span class="comment">*/</span></span><br><span class="line">SetmealVO <span class="title function_">getByIdWithDish</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="comment">     * <span class="doctag">@param</span> setmealDTO</span></span><br><span class="line"><span class="comment">*/</span></span><br><span class="line"><span class="keyword">void</span> <span class="title function_">update</span><span class="params">(SetmealDTO setmealDTO)</span>;</span><br></pre></td></tr></table></figure><br>在SetmealServiceImpl中实现这两个方法<br><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><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">     * 根据id查询套餐和套餐菜品关系</span></span><br><span class="line"><span class="comment">     *</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@param</span> id</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@return</span></span></span><br><span class="line"><span class="comment">*/</span></span><br><span class="line"><span class="keyword">public</span> SetmealVO <span class="title function_">getByIdWithDish</span><span class="params">(Long id)</span> &#123;</span><br><span class="line">    <span class="type">Setmeal</span> <span class="variable">setmeal</span> <span class="operator">=</span> setmealMapper.getById(id);</span><br><span class="line">    List&lt;SetmealDish&gt; setmealDishes = setmealDishMapper.getBySetmealId(id);</span><br><span class="line"></span><br><span class="line">    <span class="type">SetmealVO</span> <span class="variable">setmealVO</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">SetmealVO</span>();</span><br><span class="line">    BeanUtils.copyProperties(setmeal, setmealVO);</span><br><span class="line">    setmealVO.setSetmealDishes(setmealDishes);</span><br><span class="line">    </span><br><span class="line">    <span class="keyword">return</span> setmealVO;</span><br><span 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 class="doctag">@param</span> setmealDTO</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_">update</span><span class="params">(SetmealDTO setmealDTO)</span> &#123;</span><br><span class="line">    <span class="type">Setmeal</span> <span class="variable">setmeal</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">Setmeal</span>();</span><br><span class="line">    BeanUtils.copyProperties(setmealDTO, setmeal);</span><br><span class="line"></span><br><span class="line">    <span class="comment">//1、修改套餐表，执行update</span></span><br><span class="line">    setmealMapper.update(setmeal);</span><br><span class="line"></span><br><span class="line">    <span class="comment">//套餐id</span></span><br><span class="line">    <span class="type">Long</span> <span class="variable">setmealId</span> <span class="operator">=</span> setmealDTO.getId();</span><br><span class="line"></span><br><span class="line">    <span class="comment">//2、删除套餐和菜品的关联关系，操作setmeal_dish表，执行delete</span></span><br><span class="line">    setmealDishMapper.deleteBySetmealId(setmealId);</span><br><span class="line"></span><br><span class="line">    List&lt;SetmealDish&gt; setmealDishes = setmealDTO.getSetmealDishes();</span><br><span class="line">    setmealDishes.forEach(setmealDish -&gt; &#123;</span><br><span class="line">        setmealDish.setSetmealId(setmealId);</span><br><span class="line">    &#125;);</span><br><span class="line">    <span class="comment">//3、重新插入套餐和菜品的关联关系，操作setmeal_dish表，执行insert</span></span><br><span class="line">    setmealDishMapper.insertBatch(setmealDishes);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></p><h4 id="Mapper层-7"><a href="#Mapper层-7" class="headerlink" title="Mapper层"></a>Mapper层</h4><p>在SetmealDishMapper中添加查询套餐和套餐菜品关系的方法<br><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><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="comment"> * 根据套餐id查询套餐和菜品的关联关系</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@param</span> setmealId</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@return</span></span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="meta">@Select(&quot;select * from setmeal_dish where setmeal_id = #&#123;setmealId&#125;&quot;)</span></span><br><span class="line">List&lt;SetmealDish&gt; <span class="title function_">getBySetmealId</span><span class="params">(Long setmealId)</span>;</span><br></pre></td></tr></table></figure></p><h3 id="功能测试-11"><a href="#功能测试-11" class="headerlink" title="功能测试"></a>功能测试</h3><ul><li>直接通过前后端联调进行简单测试即可，首先新增一个套餐<div class="img-wrap"><div class="img-bg"><img class="img" src="https://raw.githubusercontent.com/silvan2077/markdown_pic1/main/Picgo/20251114174039.png"/></div></div></li><li>对该套餐各项数据都进行修改，修改完后点击保存<div class="img-wrap"><div class="img-bg"><img class="img" src="https://raw.githubusercontent.com/silvan2077/markdown_pic1/main/Picgo/20251114174401.png"/></div></div></li></ul><h2 id="起售-停售套餐"><a href="#起售-停售套餐" class="headerlink" title="起售/停售套餐"></a>起售/停售套餐</h2><h3 id="需求分析和设计-13"><a href="#需求分析和设计-13" class="headerlink" title="需求分析和设计"></a>需求分析和设计</h3><p>和对菜品的起售/停售操作一样<br><div class="img-wrap"><div class="img-bg"><img class="img" src="https://raw.githubusercontent.com/silvan2077/markdown_pic1/main/Picgo/image-20221018163720881.png" style="width:600px;"/></div></div><br><div class="img-wrap"><div class="img-bg"><img class="img" src="https://raw.githubusercontent.com/silvan2077/markdown_pic1/main/Picgo/image-20221018163720881.png"/></div></div></p><div class="note info no-icon flat"><p>业务规则：</p><ul><li>对状态为起售的套餐进行停售操作，对状态为停售的套餐进行起售操作</li><li>起售的套餐可以展示在用户端，停售的套餐不能展示在用户端</li><li>起售套餐时，如果套餐内包含停售的菜品，则不能起售</li></ul></div><p><strong>接口设计</strong><br><div class="img-wrap"><div class="img-bg"><img class="img" src="https://raw.githubusercontent.com/silvan2077/markdown_pic1/main/Picgo/image-20221018165055208.png"/></div></div></p><h3 id="代码实现-4"><a href="#代码实现-4" class="headerlink" title="代码实现"></a>代码实现</h3><h4 id="Controller层-8"><a href="#Controller层-8" class="headerlink" title="Controller层"></a>Controller层</h4><p>在SetmealController中添加<code>起售/停售套餐</code>的方法<br><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><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 class="doctag">@param</span> status</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@param</span> id</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@return</span></span></span><br><span class="line"><span class="comment">*/</span></span><br><span class="line"><span class="meta">@PostMapping(&quot;/status/&#123;status&#125;&quot;)</span></span><br><span class="line"><span class="meta">@ApiOperation(&quot;套餐起售停售&quot;)</span></span><br><span class="line"><span class="keyword">public</span> Result <span class="title function_">startOrStop</span><span class="params">(<span class="meta">@PathVariable</span> Integer status, Long id)</span> &#123;</span><br><span class="line">    setmealService.startOrStop(status, id);</span><br><span class="line">    <span class="keyword">return</span> Result.success();</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></p><h4 id="Service层-5"><a href="#Service层-5" class="headerlink" title="Service层"></a>Service层</h4><p>在SetmealService中添加<code>起售/停售套餐</code>的方法<br><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><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="comment">     * 套餐起售、停售</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@param</span> status</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@param</span> id</span></span><br><span class="line"><span class="comment">*/</span></span><br><span class="line"><span class="keyword">void</span> <span class="title function_">startOrStop</span><span class="params">(Integer status, Long id)</span>;</span><br></pre></td></tr></table></figure><br>在SetmealServiceImpl中实现该方法<br><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><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">     * 套餐起售、停售</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@param</span> status</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@param</span> id</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_">startOrStop</span><span class="params">(Integer status, Long id)</span> &#123;</span><br><span class="line">    <span class="comment">//起售套餐时，判断套餐内是否有停售菜品，有停售菜品提示&quot;套餐内包含未启售菜品，无法启售&quot;</span></span><br><span class="line">    <span class="keyword">if</span>(status == StatusConstant.ENABLE)&#123;</span><br><span class="line">        <span class="comment">//select a.* from dish a left join setmeal_dish b on a.id = b.dish_id where b.setmeal_id = ?</span></span><br><span class="line">        List&lt;Dish&gt; dishList = dishMapper.getBySetmealId(id);</span><br><span class="line">        <span class="keyword">if</span>(dishList != <span class="literal">null</span> &amp;&amp; dishList.size() &gt; <span class="number">0</span>)&#123;</span><br><span class="line">            dishList.forEach(dish -&gt; &#123;</span><br><span class="line">                <span class="keyword">if</span>(StatusConstant.DISABLE == dish.getStatus())&#123;</span><br><span class="line">                    <span class="keyword">throw</span> <span class="keyword">new</span> <span class="title class_">SetmealEnableFailedException</span>(MessageConstant.SETMEAL_ENABLE_FAILED);</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="type">Setmeal</span> <span class="variable">setmeal</span> <span class="operator">=</span> Setmeal.builder()</span><br><span class="line">        .id(id)</span><br><span class="line">        .status(status)</span><br><span class="line">        .build();</span><br><span class="line">    setmealMapper.update(setmeal);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></p><h4 id="Mapper层-8"><a href="#Mapper层-8" class="headerlink" title="Mapper层"></a>Mapper层</h4><p>在DishMapper中添加查询菜品的方法<br><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><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="comment">     * 根据套餐id查询菜品</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@param</span> setmealId</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@return</span></span></span><br><span class="line"><span class="comment">*/</span></span><br><span class="line"><span class="meta">@Select(&quot;select a.* from dish a left join setmeal_dish b on a.id = b.dish_id where b.setmeal_id = #&#123;setmealId&#125;&quot;)</span></span><br><span class="line">List&lt;Dish&gt; <span class="title function_">getBySetmealId</span><span class="params">(Long setmealId)</span>;</span><br></pre></td></tr></table></figure></p><h3 id="功能测试-12"><a href="#功能测试-12" class="headerlink" title="功能测试"></a>功能测试</h3><p>通过接口文档，测试套餐起售停售功能<br><div class="img-wrap"><div class="img-bg"><img class="img" src="https://raw.githubusercontent.com/silvan2077/markdown_pic1/main/Picgo/20251114180102.png"/></div></div></p><h1 id="day05"><a href="#day05" class="headerlink" title="day05"></a>day05</h1><h2 id="课程入门"><a href="#课程入门" class="headerlink" title="课程入门"></a>课程入门</h2><ul><li>Redis入门</li><li>Redis数据类型</li><li>Redis常用命令</li><li>在Java中操作Redis</li><li>店铺营业状态设置</li></ul><div class="note info no-icon flat"><p>Redis相关的内容之前学习过并做了相关笔记，此处不再详细记录</p></div><p>功能实现：<strong>营业状态设置</strong></p><p><strong>效果图：</strong><br><div class="img-wrap"><div class="img-bg"><img class="img" src="https://raw.githubusercontent.com/silvan2077/markdown_pic1/main/Picgo/image-20221130223019209.png"/></div></div><br>商家可以自己选择在线状态，状态为营业时客户可以在小程序下单，状态为打样时，客户无法在小程序端下单<br><div class="tabs" id="day05效果图"><ul class="nav-tabs"><li class="tab active"><button type="button" data-href="#day05效果图-1">营业中</button></li><li class="tab"><button type="button" data-href="#day05效果图-2">打烊中</button></li></ul><div class="tab-contents"><div class="tab-item-content active" id="day05效果图-1"><div class="img-wrap"><div class="img-bg"><img class="img" src="https://raw.githubusercontent.com/silvan2077/markdown_pic1/main/Picgo/image-20221130223113561.png"/></div></div><button type="button" class="tab-to-top" aria-label="scroll to top"><i class="fas fa-arrow-up"></i></button></div><div class="tab-item-content" id="day05效果图-2"><div class="img-wrap"><div class="img-bg"><img class="img" src="https://raw.githubusercontent.com/silvan2077/markdown_pic1/main/Picgo/image-20221130223205032.png"/></div></div><button type="button" class="tab-to-top" aria-label="scroll to top"><i class="fas fa-arrow-up"></i></button></div></div></div></p><h2 id="Java中操作Redis"><a href="#Java中操作Redis" class="headerlink" title="Java中操作Redis"></a>Java中操作Redis</h2><h3 id="Redis的Java客户端"><a href="#Redis的Java客户端" class="headerlink" title="Redis的Java客户端"></a>Redis的Java客户端</h3><p>在java程序中操作Redis就需要使用Redis的Java客户端，就如同我们使用JDBC操作MySQL数据库一样。<br>Redis 的 Java 客户端很多，常用的几种：</p><ul><li>Jedis</li><li>Lettuce</li><li>Spring Data Redis</li></ul><p>Spring 对 Redis 客户端进行了整合，提供了 <code>Spring Data Redis</code>，在Spring Boot项目中还提供了对应的Starter，即 <code>spring-boot-starter-data-redis</code>。</p><h3 id="Spring-Data-Redis使用方式"><a href="#Spring-Data-Redis使用方式" class="headerlink" title="Spring Data Redis使用方式"></a>Spring Data Redis使用方式</h3><h4 id="介绍-1"><a href="#介绍-1" class="headerlink" title="介绍"></a>介绍</h4><p><code>Spring Data Redis</code> 是 Spring 的一部分，提供了在 Spring 应用中通过简单的配置就可以访问 Redis 服务，对 Redis 底层开发包进行了高度封装。<br>网址：<a href="https://spring.io/projects/spring-data-redis">https://spring.io/projects/spring-data-redis</a><br><div class="img-wrap"><div class="img-bg"><img class="img" src="https://raw.githubusercontent.com/silvan2077/markdown_pic1/main/Picgo/image-20210927143741458.png"/></div></div></p><p>Spring Boot提供了对应的Starter，maven坐标：</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.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></pre></td></tr></table></figure><p>Spring Data Redis中提供了一个高度封装的类：<strong>RedisTemplate</strong>，对相关api进行了归类封装,将同一类型操作封装为operation接口，具体分类如下：</p><ul><li>ValueOperations：string数据操作</li><li>SetOperations：set类型数据操作</li><li>ZSetOperations：zset类型数据操作</li><li>HashOperations：hash类型的数据操作</li><li>ListOperations：list类型的数据操作</li></ul><h4 id="环境搭建"><a href="#环境搭建" class="headerlink" title="环境搭建"></a>环境搭建</h4><p>进入到sky-server模块</p><ol><li>导入Spring Data Redis的maven坐标(已完成)<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.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></pre></td></tr></table></figure></li></ol><ol><li>配置Redis数据源<br>在application-dev.yml中添加<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="attr">sky:</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 class="attr">password:</span> <span class="string">password</span></span><br><span class="line">    <span class="attr">database:</span> <span class="number">10</span></span><br></pre></td></tr></table></figure></li></ol><div class="note warning no-icon flat"><p><strong>说明：</strong><br>database:指定使用Redis的哪个数据库，Redis服务启动后默认有16个数据库，编号分别是从0到15。<br>可以通过修改Redis配置文件来指定数据库的数量。</p></div><p>在application.yml中添加读取application-dev.yml中的相关Redis配置<br><figure class="highlight yml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><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="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><br><span class="line">  <span class="attr">redis:</span></span><br><span class="line">    <span class="attr">host:</span> <span class="string">$&#123;sky.redis.host&#125;</span></span><br><span class="line">    <span class="attr">port:</span> <span class="string">$&#123;sky.redis.port&#125;</span></span><br><span class="line">    <span class="attr">password:</span> <span class="string">$&#123;sky.redis.password&#125;</span></span><br><span class="line">    <span class="attr">database:</span> <span class="string">$&#123;sky.redis.database&#125;</span></span><br></pre></td></tr></table></figure></p><ol><li><p>编写配置类，创建RedisTemplate对象</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">@Configuration</span></span><br><span class="line"><span class="meta">@Slf4j</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">RedisConfiguration</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> RedisTemplate <span class="title function_">redisTemplate</span><span class="params">(RedisConnectionFactory redisConnectionFactory)</span>&#123;</span><br><span class="line">        log.info(<span class="string">&quot;开始创建redis模板对象...&quot;</span>);</span><br><span class="line">        <span class="type">RedisTemplate</span> <span class="variable">redisTemplate</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">RedisTemplate</span>();</span><br><span class="line">        <span class="comment">//设置redis的连接工厂对象</span></span><br><span class="line">        redisTemplate.setConnectionFactory(redisConnectionFactory);</span><br><span class="line">        <span class="comment">//设置redis key的序列化器</span></span><br><span class="line">        redisTemplate.setKeySerializer(<span class="keyword">new</span> <span class="title class_">StringRedisSerializer</span>());</span><br><span class="line">        <span class="keyword">return</span> redisTemplate;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><div class="note warning no-icon flat"><p><strong>注意</strong><br>当前配置类不是必须的，因为 Spring Boot 框架会自动装配 RedisTemplate 对象，但是默认的key序列化器为<code>JdkSerializationRedisSerializer</code>，导致我们存到Redis中后的数据和原始数据有差别，故设置为<code>StringRedisSerializer</code>序列化器。</p></div></li><li><p>通过RedisTemplate对象操作Redis<br>在test下新建测试类</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="meta">@SpringBootTest</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">SpringDataRedisTest</span> &#123;</span><br><span class="line">    <span class="meta">@Autowired</span></span><br><span class="line">    <span class="keyword">private</span> RedisTemplate redisTemplate;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Test</span></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">testRedisTemplate</span><span class="params">()</span>&#123;</span><br><span class="line">        System.out.println(redisTemplate);</span><br><span class="line">        <span class="comment">//string数据操作</span></span><br><span class="line">        <span class="type">ValueOperations</span> <span class="variable">valueOperations</span> <span class="operator">=</span> redisTemplate.opsForValue();</span><br><span class="line">        <span class="comment">//hash类型的数据操作</span></span><br><span class="line">        <span class="type">HashOperations</span> <span class="variable">hashOperations</span> <span class="operator">=</span> redisTemplate.opsForHash();</span><br><span class="line">        <span class="comment">//list类型的数据操作</span></span><br><span class="line">        <span class="type">ListOperations</span> <span class="variable">listOperations</span> <span class="operator">=</span> redisTemplate.opsForList();</span><br><span class="line">        <span class="comment">//set类型数据操作</span></span><br><span class="line">        <span class="type">SetOperations</span> <span class="variable">setOperations</span> <span class="operator">=</span> redisTemplate.opsForSet();</span><br><span class="line">        <span class="comment">//zset类型数据操作</span></span><br><span class="line">        <span class="type">ZSetOperations</span> <span class="variable">zSetOperations</span> <span class="operator">=</span> redisTemplate.opsForZSet();</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p><strong>测试：</strong></p><div class="img-wrap"><div class="img-bg"><img class="img" src="https://raw.githubusercontent.com/silvan2077/markdown_pic1/main/Picgo/image-20221130205351403.png"/></div></div><p>说明RedisTemplate对象注入成功，并且通过该RedisTemplate对象获取操作5种数据类型相关对象。</p></li></ol><h4 id="常见数据类型"><a href="#常见数据类型" class="headerlink" title="常见数据类型"></a>常见数据类型</h4><div class="tabs" id="springdataredis"><ul class="nav-tabs"><li class="tab active"><button type="button" data-href="#springdataredis-1">字符串类型</button></li><li class="tab"><button type="button" data-href="#springdataredis-2">哈希类型</button></li><li class="tab"><button type="button" data-href="#springdataredis-3">列表类型</button></li><li class="tab"><button type="button" data-href="#springdataredis-4">集合类型</button></li><li class="tab"><button type="button" data-href="#springdataredis-5">有序集合类型</button></li><li class="tab"><button type="button" data-href="#springdataredis-6">通用命令操作</button></li></ul><div class="tab-contents"><div class="tab-item-content active" id="springdataredis-1"><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><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="comment">    * 操作字符串类型的数据</span></span><br><span class="line"><span class="comment">    */</span></span><br><span class="line">   <span class="meta">@Test</span></span><br><span class="line">   <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">testString</span><span class="params">()</span>&#123;</span><br><span class="line">       <span class="comment">// set get setex setnx</span></span><br><span class="line">       redisTemplate.opsForValue().set(<span class="string">&quot;name&quot;</span>,<span class="string">&quot;小明&quot;</span>);</span><br><span class="line">       <span class="type">String</span> <span class="variable">city</span> <span class="operator">=</span> (String) redisTemplate.opsForValue().get(<span class="string">&quot;name&quot;</span>);</span><br><span class="line">       System.out.println(city);</span><br><span class="line">       redisTemplate.opsForValue().set(<span class="string">&quot;code&quot;</span>,<span class="string">&quot;1234&quot;</span>,<span class="number">3</span>, TimeUnit.MINUTES);</span><br><span class="line">       redisTemplate.opsForValue().setIfAbsent(<span class="string">&quot;lock&quot;</span>,<span class="string">&quot;1&quot;</span>);</span><br><span class="line">       redisTemplate.opsForValue().setIfAbsent(<span class="string">&quot;lock&quot;</span>,<span class="string">&quot;2&quot;</span>);</span><br><span class="line">   &#125;</span><br></pre></td></tr></table></figure><button type="button" class="tab-to-top" aria-label="scroll to top"><i class="fas fa-arrow-up"></i></button></div><div class="tab-item-content" id="springdataredis-2"><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><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">    * 操作哈希类型的数据</span></span><br><span class="line"><span class="comment">    */</span></span><br><span class="line">   <span class="meta">@Test</span></span><br><span class="line">   <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">testHash</span><span class="params">()</span>&#123;</span><br><span class="line">       <span class="comment">//hset hget hdel hkeys hvals</span></span><br><span class="line">       <span class="type">HashOperations</span> <span class="variable">hashOperations</span> <span class="operator">=</span> redisTemplate.opsForHash();</span><br><span class="line"></span><br><span class="line">       hashOperations.put(<span class="string">&quot;100&quot;</span>,<span class="string">&quot;name&quot;</span>,<span class="string">&quot;tom&quot;</span>);</span><br><span class="line">       hashOperations.put(<span class="string">&quot;100&quot;</span>,<span class="string">&quot;age&quot;</span>,<span class="string">&quot;20&quot;</span>);</span><br><span class="line"></span><br><span class="line">       <span class="type">String</span> <span class="variable">name</span> <span class="operator">=</span> (String) hashOperations.get(<span class="string">&quot;100&quot;</span>, <span class="string">&quot;name&quot;</span>);</span><br><span class="line">       System.out.println(name);</span><br><span class="line"></span><br><span class="line">       <span class="type">Set</span> <span class="variable">keys</span> <span class="operator">=</span> hashOperations.keys(<span class="string">&quot;100&quot;</span>);</span><br><span class="line">       System.out.println(keys);</span><br><span class="line"></span><br><span class="line">       <span class="type">List</span> <span class="variable">values</span> <span class="operator">=</span> hashOperations.values(<span class="string">&quot;100&quot;</span>);</span><br><span class="line">       System.out.println(values);</span><br><span class="line"></span><br><span class="line">       hashOperations.delete(<span class="string">&quot;100&quot;</span>,<span class="string">&quot;age&quot;</span>);</span><br><span class="line">   &#125;</span><br></pre></td></tr></table></figure><button type="button" class="tab-to-top" aria-label="scroll to top"><i class="fas fa-arrow-up"></i></button></div><div class="tab-item-content" id="springdataredis-3"><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><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 class="meta">@Test</span></span><br><span class="line">   <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">testList</span><span class="params">()</span>&#123;</span><br><span class="line">       <span class="comment">//lpush lrange rpop llen</span></span><br><span class="line">       <span class="type">ListOperations</span> <span class="variable">listOperations</span> <span class="operator">=</span> redisTemplate.opsForList();</span><br><span class="line"></span><br><span class="line">       listOperations.leftPushAll(<span class="string">&quot;mylist&quot;</span>,<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">       listOperations.leftPush(<span class="string">&quot;mylist&quot;</span>,<span class="string">&quot;d&quot;</span>);</span><br><span class="line"></span><br><span class="line">       <span class="type">List</span> <span class="variable">mylist</span> <span class="operator">=</span> listOperations.range(<span class="string">&quot;mylist&quot;</span>, <span class="number">0</span>, -<span class="number">1</span>);</span><br><span class="line">       System.out.println(mylist);</span><br><span class="line"></span><br><span class="line">       listOperations.rightPop(<span class="string">&quot;mylist&quot;</span>);</span><br><span class="line"></span><br><span class="line">       <span class="type">Long</span> <span class="variable">size</span> <span class="operator">=</span> listOperations.size(<span class="string">&quot;mylist&quot;</span>);</span><br><span class="line">       System.out.println(size);</span><br><span class="line">   &#125;</span><br></pre></td></tr></table></figure><button type="button" class="tab-to-top" aria-label="scroll to top"><i class="fas fa-arrow-up"></i></button></div><div class="tab-item-content" id="springdataredis-4"><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><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">    * 操作集合类型的数据</span></span><br><span class="line"><span class="comment">    */</span></span><br><span class="line">   <span class="meta">@Test</span></span><br><span class="line">   <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">testSet</span><span class="params">()</span>&#123;</span><br><span class="line">       <span class="comment">//sadd smembers scard sinter sunion srem</span></span><br><span class="line">       <span class="type">SetOperations</span> <span class="variable">setOperations</span> <span class="operator">=</span> redisTemplate.opsForSet();</span><br><span class="line"></span><br><span class="line">       setOperations.add(<span class="string">&quot;set1&quot;</span>,<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">       setOperations.add(<span class="string">&quot;set2&quot;</span>,<span class="string">&quot;a&quot;</span>,<span class="string">&quot;b&quot;</span>,<span class="string">&quot;x&quot;</span>,<span class="string">&quot;y&quot;</span>);</span><br><span class="line"></span><br><span class="line">       <span class="type">Set</span> <span class="variable">members</span> <span class="operator">=</span> setOperations.members(<span class="string">&quot;set1&quot;</span>);</span><br><span class="line">       System.out.println(members);</span><br><span class="line"></span><br><span class="line">       <span class="type">Long</span> <span class="variable">size</span> <span class="operator">=</span> setOperations.size(<span class="string">&quot;set1&quot;</span>);</span><br><span class="line">       System.out.println(size);</span><br><span class="line"></span><br><span class="line">       <span class="type">Set</span> <span class="variable">intersect</span> <span class="operator">=</span> setOperations.intersect(<span class="string">&quot;set1&quot;</span>, <span class="string">&quot;set2&quot;</span>);</span><br><span class="line">       System.out.println(intersect);</span><br><span class="line"></span><br><span class="line">       <span class="type">Set</span> <span class="variable">union</span> <span class="operator">=</span> setOperations.union(<span class="string">&quot;set1&quot;</span>, <span class="string">&quot;set2&quot;</span>);</span><br><span class="line">       System.out.println(union);</span><br><span class="line"></span><br><span class="line">       setOperations.remove(<span class="string">&quot;set1&quot;</span>,<span class="string">&quot;a&quot;</span>,<span class="string">&quot;b&quot;</span>);</span><br><span class="line">   &#125;</span><br></pre></td></tr></table></figure><button type="button" class="tab-to-top" aria-label="scroll to top"><i class="fas fa-arrow-up"></i></button></div><div class="tab-item-content" id="springdataredis-5"><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><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 class="meta">@Test</span></span><br><span class="line">   <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">testZset</span><span class="params">()</span>&#123;</span><br><span class="line">       <span class="comment">//zadd zrange zincrby zrem</span></span><br><span class="line">       <span class="type">ZSetOperations</span> <span class="variable">zSetOperations</span> <span class="operator">=</span> redisTemplate.opsForZSet();</span><br><span class="line"></span><br><span class="line">       zSetOperations.add(<span class="string">&quot;zset1&quot;</span>,<span class="string">&quot;a&quot;</span>,<span class="number">10</span>);</span><br><span class="line">       zSetOperations.add(<span class="string">&quot;zset1&quot;</span>,<span class="string">&quot;b&quot;</span>,<span class="number">12</span>);</span><br><span class="line">       zSetOperations.add(<span class="string">&quot;zset1&quot;</span>,<span class="string">&quot;c&quot;</span>,<span class="number">9</span>);</span><br><span class="line"></span><br><span class="line">       <span class="type">Set</span> <span class="variable">zset1</span> <span class="operator">=</span> zSetOperations.range(<span class="string">&quot;zset1&quot;</span>, <span class="number">0</span>, -<span class="number">1</span>);</span><br><span class="line">       System.out.println(zset1);</span><br><span class="line"></span><br><span class="line">       zSetOperations.incrementScore(<span class="string">&quot;zset1&quot;</span>,<span class="string">&quot;c&quot;</span>,<span class="number">10</span>);</span><br><span class="line"></span><br><span class="line">       zSetOperations.remove(<span class="string">&quot;zset1&quot;</span>,<span class="string">&quot;a&quot;</span>,<span class="string">&quot;b&quot;</span>);</span><br><span class="line">   &#125;</span><br></pre></td></tr></table></figure><button type="button" class="tab-to-top" aria-label="scroll to top"><i class="fas fa-arrow-up"></i></button></div><div class="tab-item-content" id="springdataredis-6"><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><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 class="meta">@Test</span></span><br><span class="line">   <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">testCommon</span><span class="params">()</span>&#123;</span><br><span class="line">       <span class="comment">//keys exists type del</span></span><br><span class="line">       <span class="type">Set</span> <span class="variable">keys</span> <span class="operator">=</span> redisTemplate.keys(<span class="string">&quot;*&quot;</span>);</span><br><span class="line">       System.out.println(keys);</span><br><span class="line"></span><br><span class="line">       <span class="type">Boolean</span> <span class="variable">name</span> <span class="operator">=</span> redisTemplate.hasKey(<span class="string">&quot;name&quot;</span>);</span><br><span class="line">       <span class="type">Boolean</span> <span class="variable">set1</span> <span class="operator">=</span> redisTemplate.hasKey(<span class="string">&quot;set1&quot;</span>);</span><br><span class="line"></span><br><span class="line">       <span class="keyword">for</span> (Object key : keys) &#123;</span><br><span class="line">           <span class="type">DataType</span> <span class="variable">type</span> <span class="operator">=</span> redisTemplate.type(key);</span><br><span class="line">           System.out.println(type.name());</span><br><span class="line">       &#125;</span><br><span class="line"></span><br><span class="line">       redisTemplate.delete(<span class="string">&quot;mylist&quot;</span>);</span><br><span class="line">   &#125;</span><br></pre></td></tr></table></figure><button type="button" class="tab-to-top" aria-label="scroll to top"><i class="fas fa-arrow-up"></i></button></div></div></div><h2 id="店铺营业状态设置"><a href="#店铺营业状态设置" class="headerlink" title="店铺营业状态设置"></a>店铺营业状态设置</h2><h3 id="需求分析和设计-14"><a href="#需求分析和设计-14" class="headerlink" title="需求分析和设计"></a>需求分析和设计</h3><p>进到苍穹外卖后台，显示餐厅的营业状态，营业状态分为<strong>营业中</strong>和<strong>打烊中</strong>，若当前餐厅处于营业状态，自动接收任何订单，客户可在小程序进行下单操作；若当前餐厅处于打烊状态，不接受任何订单，客户便无法在小程序进行下单操作。</p><div class="img-wrap"><div class="img-bg"><img class="img" src="https://raw.githubusercontent.com/silvan2077/markdown_pic1/main/Picgo/image-20221130212134915.png"/></div></div><p><strong>接口设计：</strong></p><ul><li>设置营业状态</li><li>管理端查询营业状态</li><li>用户端查询营业状态</li></ul><div class="note warning no-icon flat"><p><strong>注：</strong>从技术层面分析，其实管理端和用户端查询营业状态时，可通过一个接口去实现即可。因为营业状态是一致的。但是，本项目约定：</p><ul><li><strong>管理端</strong>发出的请求，统一使用/admin作为前缀。</li><li><strong>用户端</strong>发出的请求，统一使用/user作为前缀。</li></ul></div><div class="tabs" id="店铺营业状态设置接口设计"><ul class="nav-tabs"><li class="tab active"><button type="button" data-href="#店铺营业状态设置接口设计-1">设置营业状态</button></li><li class="tab"><button type="button" data-href="#店铺营业状态设置接口设计-2">管理端营业状态</button></li><li class="tab"><button type="button" data-href="#店铺营业状态设置接口设计-3">用户端营业状态</button></li></ul><div class="tab-contents"><div class="tab-item-content active" id="店铺营业状态设置接口设计-1"><div class="img-wrap"><div class="img-bg"><img class="img" src="https://raw.githubusercontent.com/silvan2077/markdown_pic1/main/Picgo/image-20221130215725802.png"/></div></div><button type="button" class="tab-to-top" aria-label="scroll to top"><i class="fas fa-arrow-up"></i></button></div><div class="tab-item-content" id="店铺营业状态设置接口设计-2"><div class="img-wrap"><div class="img-bg"><img class="img" src="https://raw.githubusercontent.com/silvan2077/markdown_pic1/main/Picgo/image-20221130215814021.png"/></div></div><button type="button" class="tab-to-top" aria-label="scroll to top"><i class="fas fa-arrow-up"></i></button></div><div class="tab-item-content" id="店铺营业状态设置接口设计-3"><div class="img-wrap"><div class="img-bg"><img class="img" src="https://raw.githubusercontent.com/silvan2077/markdown_pic1/main/Picgo/image-20221130215836785.png"/></div></div><button type="button" class="tab-to-top" aria-label="scroll to top"><i class="fas fa-arrow-up"></i></button></div></div></div><div class="note warning no-icon flat"><p>可以通过一张表来存储营业状态数据，但整个表就一个字段的意义不大，所以营业状态存储方式：基于Redis的字符串来存储<br><strong>约定：</strong> 1表示营业 0表示打烊</p></div><h3 id="代码开发-9"><a href="#代码开发-9" class="headerlink" title="代码开发"></a>代码开发</h3><h4 id="设置营业状态"><a href="#设置营业状态" class="headerlink" title="设置营业状态"></a>设置营业状态</h4><p>在sky-server模块中，创建ShopController.java</p><p><strong>根据接口定义创建ShopController的setStatus设置营业状态方法：</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">@RestController(&quot;adminShopController&quot;)</span></span><br><span class="line"><span class="meta">@RequestMapping(&quot;/admin/shop&quot;)</span></span><br><span class="line"><span class="meta">@Api(tags = &quot;店铺相关接口&quot;)</span></span><br><span class="line"><span class="meta">@Slf4j</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">ShopController</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">KEY</span> <span class="operator">=</span> <span class="string">&quot;SHOP_STATUS&quot;</span>;</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 redisTemplate;</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 class="doctag">@param</span> status</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@return</span></span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    <span class="meta">@PutMapping(&quot;/&#123;status&#125;&quot;)</span></span><br><span class="line">    <span class="meta">@ApiOperation(&quot;设置店铺的营业状态&quot;)</span></span><br><span class="line">    <span class="keyword">public</span> Result <span class="title function_">setStatus</span><span class="params">(<span class="meta">@PathVariable</span> Integer status)</span>&#123;</span><br><span class="line">        log.info(<span class="string">&quot;设置店铺的营业状态为：&#123;&#125;&quot;</span>,status == <span class="number">1</span> ? <span class="string">&quot;营业中&quot;</span> : <span class="string">&quot;打烊中&quot;</span>);</span><br><span class="line">        redisTemplate.opsForValue().set(KEY,status);</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><h4 id="管理端查询营业状态"><a href="#管理端查询营业状态" class="headerlink" title="管理端查询营业状态"></a>管理端查询营业状态</h4><p><strong>根据接口定义创建ShopController的getStatus查询营业状态方法：</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></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">@return</span></span></span><br><span class="line"><span class="comment">    */</span></span><br><span class="line">   <span class="meta">@GetMapping(&quot;/status&quot;)</span></span><br><span class="line">   <span class="meta">@ApiOperation(&quot;获取店铺的营业状态&quot;)</span></span><br><span class="line">   <span class="keyword">public</span> Result&lt;Integer&gt; <span class="title function_">getStatus</span><span class="params">()</span>&#123;</span><br><span class="line">       <span class="type">Integer</span> <span class="variable">status</span> <span class="operator">=</span> (Integer) redisTemplate.opsForValue().get(KEY);</span><br><span class="line">       log.info(<span class="string">&quot;获取到店铺的营业状态为：&#123;&#125;&quot;</span>,status == <span class="number">1</span> ? <span class="string">&quot;营业中&quot;</span> : <span class="string">&quot;打烊中&quot;</span>);</span><br><span class="line">       <span class="keyword">return</span> Result.success(status);</span><br><span class="line">   &#125;</span><br></pre></td></tr></table></figure><h4 id="用户端查询营业状态"><a href="#用户端查询营业状态" class="headerlink" title="用户端查询营业状态"></a>用户端查询营业状态</h4><p>创建com.sky.controller.user包，在该包下创建ShopController.java</p><p><strong>根据接口定义创建ShopController的getStatus查询营业状态方法：</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">@RestController(&quot;userShopController&quot;)</span></span><br><span class="line"><span class="meta">@RequestMapping(&quot;/user/shop&quot;)</span></span><br><span class="line"><span class="meta">@Api(tags = &quot;店铺相关接口&quot;)</span></span><br><span class="line"><span class="meta">@Slf4j</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">ShopController</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">KEY</span> <span class="operator">=</span> <span class="string">&quot;SHOP_STATUS&quot;</span>;</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 redisTemplate;</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 class="doctag">@return</span></span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    <span class="meta">@GetMapping(&quot;/status&quot;)</span></span><br><span class="line">    <span class="meta">@ApiOperation(&quot;获取店铺的营业状态&quot;)</span></span><br><span class="line">    <span class="keyword">public</span> Result&lt;Integer&gt; <span class="title function_">getStatus</span><span class="params">()</span>&#123;</span><br><span class="line">        <span class="type">Integer</span> <span class="variable">status</span> <span class="operator">=</span> (Integer) redisTemplate.opsForValue().get(KEY);</span><br><span class="line">        log.info(<span class="string">&quot;获取到店铺的营业状态为：&#123;&#125;&quot;</span>,status == <span class="number">1</span> ? <span class="string">&quot;营业中&quot;</span> : <span class="string">&quot;打烊中&quot;</span>);</span><br><span class="line">        <span class="keyword">return</span> Result.success(status);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h1 id="day06"><a href="#day06" class="headerlink" title="day06"></a>day06</h1><h2 id="课程内容-2"><a href="#课程内容-2" class="headerlink" title="课程内容"></a>课程内容</h2><ul><li>HttpClient</li><li>微信小程序开发</li><li>微信登录</li><li>导入商品浏览功能代码</li></ul><h2 id="微信小程序开发"><a href="#微信小程序开发" class="headerlink" title="微信小程序开发"></a>微信小程序开发</h2><h3 id="介绍-2"><a href="#介绍-2" class="headerlink" title="介绍"></a>介绍</h3><p>小程序是一种新的开放能力，开发者可以快速地开发一个小程序。可以在微信内被便捷地获取和传播，同时具有出色的使用体验。<br><strong>官方网址：</strong> <a href="https://mp.weixin.qq.com/cgi-bin/wx?token=&amp;lang=zh_CN">https://mp.weixin.qq.com/cgi-bin/wx?token=&amp;lang=zh_CN</a></p><div class="img-wrap"><div class="img-bg"><img class="img" src="https://raw.githubusercontent.com/silvan2077/markdown_pic1/main/Picgo/image-20221203205305487.png"/></div></div><ol><li>进行小程序开发时，需要先才注册一个小程序，可以以个人的身份来注册，也可以以企业或者其他组织的方式来注册，不同的主题注册小程序，最终的开放权限也不同，比如以个人身份来注册，则无法开通支付权限。<div class="img-wrap"><div class="img-bg"><img class="img" src="https://raw.githubusercontent.com/silvan2077/markdown_pic1/main/Picgo/image-20221203210640473.png"/></div></div></li><li>微信官方提供了一系列工具来帮助开发者快速完成小程序的开发，提供了完善的开发文档和开发者工具，还提供了相应的设计指南，同时也提供了一些小程序体验DEMO，可以快速的体验小程序实现的功能。<div class="img-wrap"><div class="img-bg"><img class="img" src="https://raw.githubusercontent.com/silvan2077/markdown_pic1/main/Picgo/image-20221203211226920.png"/></div></div></li><li>开发完的小程序想要上线，也提供了详细的接入教程<div class="img-wrap"><div class="img-bg"><img class="img" src="https://raw.githubusercontent.com/silvan2077/markdown_pic1/main/Picgo/image-20221203211535565.png"/></div></div></li></ol><h3 id="准备工作"><a href="#准备工作" class="headerlink" title="准备工作"></a>准备工作</h3><p>开发微信小程序之前需要做如下准备工作：</p><ul><li>注册小程序</li><li>完善小程序信息</li><li>下载开发者工具</li></ul><ol><li><p>注册小程序,注册地址：<a href="https://mp.weixin.qq.com/wxopen/waregister?action=step1">https://mp.weixin.qq.com/wxopen/waregister?action=step1</a></p></li><li><p>完善小程序信息<br>登录小程序后台：<a href="https://mp.weixin.qq.com/">https://mp.weixin.qq.com/</a><br>完善小程序信息、小程序类目</p><div class="img-wrap"><div class="img-bg"><img class="img" src="https://raw.githubusercontent.com/silvan2077/markdown_pic1/main/Picgo/image-20221203212615981.png"/></div></div><p>查看小程序的 <code>AppID</code></p><div class="img-wrap"><div class="img-bg"><img class="img" src="https://raw.githubusercontent.com/silvan2077/markdown_pic1/main/Picgo/image-20221203212702993.png"/></div></div></li><li><p>下载开发者工具,下载地址：<a href="https://developers.weixin.qq.com/miniprogram/dev/devtools/stable.html">https://developers.weixin.qq.com/miniprogram/dev/devtools/stable.html</a></p></li><li><p>创建小程序项目</p><div class="img-wrap"><div class="img-bg"><img class="img" src="https://raw.githubusercontent.com/silvan2077/markdown_pic1/main/Picgo/image-20221203213042020.png"/></div></div><p>熟悉开发者工具布局</p><div class="img-wrap"><div class="img-bg"><img class="img" src="https://raw.githubusercontent.com/silvan2077/markdown_pic1/main/Picgo/image-20221203213108317.png"/></div></div></li><li>设置不校验合法域名<div class="img-wrap"><div class="img-bg"><img class="img" src="https://raw.githubusercontent.com/silvan2077/markdown_pic1/main/Picgo/image-20221203213212370.png"/></div></div></li></ol><div class="note warning no-icon flat"><p><strong>注：</strong> 开发阶段，小程序发出请求到后端的Tomcat服务器，若不勾选，请求发送失败。</p></div><h3 id="入门案例"><a href="#入门案例" class="headerlink" title="入门案例"></a>入门案例</h3><div class="note danger no-icon flat"><p>小程序开发本质上属于前端开发，主要使用<code>JavaScript</code>进行开发，简单了解即可，后面会直接给出小程序的代码</p></div><h4 id="小程序目录结构"><a href="#小程序目录结构" class="headerlink" title="小程序目录结构"></a>小程序目录结构</h4><p>小程序包含一个描述整体程序的 app 和多个描述各自页面的 page。一个小程序主体部分由三个文件组成，必须放在项目的根目录，如下：<br><div class="img-wrap"><div class="img-bg"><img class="img" src="https://raw.githubusercontent.com/silvan2077/markdown_pic1/main/Picgo/image-20221203220557676.png"/></div></div></p><p><strong>文件说明：</strong><br><div class="img-wrap"><div class="img-bg"><img class="img" src="https://raw.githubusercontent.com/silvan2077/markdown_pic1/main/Picgo/image-20221203220635867.png"/></div></div></p><p><strong>app.js：</strong> 必须存在，主要存放小程序的逻辑代码</p><p><strong>app.json：</strong> 必须存在，小程序配置文件，主要存放小程序的公共配置</p><p><strong>app.wxss:</strong>  非必须存在，主要存放小程序公共样式表，类似于前端的CSS样式</p><p>对小程序主体三个文件了解后，其实一个小程序又有多个页面。比如说，有商品浏览页面、购物车的页面、订单支付的页面、商品的详情页面等等。那这些页面会放在哪呢？<br>会存放在pages目录。</p><p>一个小程序会有许多个页面，比如商品浏览页面、购物车的页面、订单支付的页面、商品的详情页面等等，这些页面会存放在pages目录下，每个小程序页面主要由四个文件组成</p><div class="img-wrap"><div class="img-bg"><img class="img" src="https://raw.githubusercontent.com/silvan2077/markdown_pic1/main/Picgo/image-20221203220826893.png"/></div></div><p><strong>文件说明：</strong><br><div class="img-wrap"><div class="img-bg"><img class="img" src="https://raw.githubusercontent.com/silvan2077/markdown_pic1/main/Picgo/image-20221203220839187.png"/></div></div></p><p><strong>js文件：</strong> 必须存在，存放页面业务逻辑代码，编写的js代码。</p><p><strong>wxml文件：</strong> 必须存在，存放页面结构，主要是做页面布局，页面效果展示的，类似于HTML页面。</p><p><strong>json文件：</strong> 非必须，存放页面相关的配置。</p><p><strong>wxss文件：</strong> 非必须，存放页面样式表，相当于CSS文件。</p><h4 id="编写和编译小程序"><a href="#编写和编译小程序" class="headerlink" title="编写和编译小程序"></a>编写和编译小程序</h4><ol><li>编写<br>进入到index.wxml，编写页面布局<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="tag">&lt;<span class="name">view</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">view</span>&gt;</span>&#123;&#123;msg&#125;&#125;<span class="tag">&lt;/<span class="name">view</span>&gt;</span></span><br><span class="line">   <span class="tag">&lt;<span class="name">view</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">button</span> <span class="attr">type</span>=<span class="string">&quot;default&quot;</span> <span class="attr">bindtap</span>=<span class="string">&quot;getUserInfo&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">image</span> <span class="attr">style</span>=<span class="string">&quot;width: 100px;height: 100px;&quot;</span> <span class="attr">src</span>=<span class="string">&quot;&#123;&#123;avatarUrl&#125;&#125;&quot;</span>&gt;</span><span class="tag">&lt;/<span class="name">image</span>&gt;</span></span><br><span class="line">    &#123;&#123;nickName&#125;&#125;</span><br><span class="line">  <span class="tag">&lt;/<span class="name">view</span>&gt;</span></span><br><span class="line">   <span class="tag">&lt;<span class="name">view</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">button</span> <span class="attr">type</span>=<span class="string">&quot;primary&quot;</span> <span class="attr">bindtap</span>=<span class="string">&quot;wxlogin&quot;</span>&gt;</span>微信登录<span class="tag">&lt;/<span class="name">button</span>&gt;</span></span><br><span class="line">    授权码：&#123;&#123;code&#125;&#125;</span><br><span class="line">  <span class="tag">&lt;/<span class="name">view</span>&gt;</span></span><br><span class="line">   <span class="tag">&lt;<span class="name">view</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">button</span> <span class="attr">type</span>=<span class="string">&quot;warn&quot;</span> <span class="attr">bindtap</span>=<span class="string">&quot;sendRequest&quot;</span>&gt;</span>发送请求<span class="tag">&lt;/<span class="name">button</span>&gt;</span></span><br><span class="line">    响应结果：&#123;&#123;result&#125;&#125;</span><br><span class="line">  <span class="tag">&lt;/<span class="name">view</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;/<span class="name">view</span>&gt;</span></span><br></pre></td></tr></table></figure>进入到index.js，编写业务逻辑代码<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><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="title class_">Page</span>(&#123;</span><br><span class="line">  <span class="attr">data</span>:&#123;</span><br><span class="line">    <span class="attr">msg</span>:<span class="string">&#x27;hello world&#x27;</span>,</span><br><span class="line">    <span class="attr">avatarUrl</span>:<span class="string">&#x27;&#x27;</span>,</span><br><span class="line">    <span class="attr">nickName</span>:<span class="string">&#x27;&#x27;</span>,</span><br><span class="line">    <span class="attr">code</span>:<span class="string">&#x27;&#x27;</span>,</span><br><span class="line">    <span class="attr">result</span>:<span class="string">&#x27;&#x27;</span></span><br><span class="line">  &#125;,</span><br><span class="line">  <span class="attr">getUserInfo</span>:<span class="keyword">function</span>(<span class="params"></span>)&#123;</span><br><span class="line">    wx.<span class="title function_">getUserProfile</span>(&#123;</span><br><span class="line">      <span class="attr">desc</span>: <span class="string">&#x27;获取用户信息&#x27;</span>,</span><br><span class="line">      <span class="attr">success</span>:<span class="function">(<span class="params">res</span>) =&gt;</span> &#123;</span><br><span class="line">        <span class="variable language_">console</span>.<span class="title function_">log</span>(res)</span><br><span class="line">        <span class="variable language_">this</span>.<span class="title function_">setData</span>(&#123;</span><br><span class="line">          <span class="attr">avatarUrl</span>:res.<span class="property">userInfo</span>.<span class="property">avatarUrl</span>,</span><br><span class="line">          <span class="attr">nickName</span>:res.<span class="property">userInfo</span>.<span class="property">nickName</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="attr">wxlogin</span>:<span class="keyword">function</span>(<span class="params"></span>)&#123;</span><br><span class="line">    wx.<span class="title function_">login</span>(&#123;</span><br><span class="line">      <span class="attr">success</span>: <span class="function">(<span class="params">res</span>) =&gt;</span> &#123;</span><br><span class="line">        <span class="variable language_">console</span>.<span class="title function_">log</span>(<span class="string">&quot;授权码：&quot;</span>+res.<span class="property">code</span>)</span><br><span class="line">        <span class="variable language_">this</span>.<span class="title function_">setData</span>(&#123;</span><br><span class="line">          <span class="attr">code</span>:res.<span class="property">code</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="attr">sendRequest</span>:<span class="keyword">function</span>(<span class="params"></span>)&#123;</span><br><span class="line">    wx.<span class="title function_">request</span>(&#123;</span><br><span class="line">      <span class="attr">url</span>: <span class="string">&#x27;http://localhost:8080/user/shop/status&#x27;</span>,</span><br><span class="line">      <span class="attr">method</span>:<span class="string">&#x27;GET&#x27;</span>,</span><br><span class="line">      <span class="attr">success</span>:<span class="function">(<span class="params">res</span>) =&gt;</span> &#123;</span><br><span class="line">        <span class="variable language_">console</span>.<span class="title function_">log</span>(<span class="string">&quot;响应结果：&quot;</span> + res.<span class="property">data</span>.<span class="property">data</span>)</span><br><span class="line">        <span class="variable language_">this</span>.<span class="title function_">setData</span>(&#123;</span><br><span class="line">          <span class="attr">result</span>:res.<span class="property">data</span>.<span class="property">data</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;&#125;)</span><br></pre></td></tr></table></figure></li><li>编译小程序<br>点击开发者工具的编译按钮，编译小程序，就能看到运行效果了<div class="img-wrap"><div class="img-bg"><img class="img" src="https://raw.githubusercontent.com/silvan2077/markdown_pic1/main/Picgo/image-20221204181606927.png"/></div></div></li></ol><h4 id="发布小程序"><a href="#发布小程序" class="headerlink" title="发布小程序"></a>发布小程序</h4><p>想要将小程序发布上线，让所有用户都用到该小程序<br>点击上传按钮:<br><div class="img-wrap"><div class="img-bg"><img class="img" src="https://raw.githubusercontent.com/silvan2077/markdown_pic1/main/Picgo/image-20221204225355015.png"/></div></div><br>上传成功：<br><div class="img-wrap"><div class="img-bg"><img class="img" src="https://raw.githubusercontent.com/silvan2077/markdown_pic1/main/Picgo/image-20221204225557820.png"/></div></div><br><div class="note info no-icon flat"><ul><li><p>将代码上传到微信服务端并不是表示小程序已经发布了，当前小程序只是一个开发版本，需提交审核，变成审核版本，审核通过后，进行发布，变成线上版本。</p></li><li><p>一旦成为线上版本，这就说明小程序就已经发布上线了，微信用户就可以在微信里面去搜索和使用这个小程序了。</p></li></ul></div></p><h2 id="微信登录"><a href="#微信登录" class="headerlink" title="微信登录"></a>微信登录</h2><h3 id="导入小程序代码"><a href="#导入小程序代码" class="headerlink" title="导入小程序代码"></a>导入小程序代码</h3><ol><li>在微信小程序开发者工具中导入小程序代码<div class="img-wrap"><div class="img-bg"><img class="img" src="https://raw.githubusercontent.com/silvan2077/markdown_pic1/main/Picgo/image-20221204205631809.png"/></div></div></li><li>输入自己的AppID<div class="img-wrap"><div class="img-bg"><img class="img" src="https://raw.githubusercontent.com/silvan2077/markdown_pic1/main/Picgo/image-20221204210011364.png"/></div></div></li><li>查看项目结构<div class="img-wrap"><div class="img-bg"><img class="img" src="https://raw.githubusercontent.com/silvan2077/markdown_pic1/main/Picgo/image-20221204210739195.png"/></div></div></li><li>修改配置<br>将小程序请求的后端服务url修改为自己后端服务的ip地址和端口号(默认不需要修改)<div class="img-wrap"><div class="img-bg"><img class="img" src="https://raw.githubusercontent.com/silvan2077/markdown_pic1/main/Picgo/image-20221204211239035.png"/></div></div></li></ol><h3 id="微信登录流程"><a href="#微信登录流程" class="headerlink" title="微信登录流程"></a>微信登录流程</h3><p>官方给出了小程序微信登录的详细流程：<a href="https://developers.weixin.qq.com/miniprogram/dev/framework/open-ability/login.html">https://developers.weixin.qq.com/miniprogram/dev/framework/open-ability/login.html</a></p><p><strong>流程图：</strong><br><div class="img-wrap"><div class="img-bg"><img class="img" src="https://raw.githubusercontent.com/silvan2077/markdown_pic1/main/Picgo/image-20221204211800753.png"/></div></div></p><p><strong>步骤分析：</strong></p><ol><li>小程序端，调用wx.login()获取code，就是授权码。</li><li>小程序端，调用wx.request()发送请求并携带code，请求开发者服务器(自己编写的后端服务)。</li><li>开发者服务端，通过HttpClient向微信接口服务发送请求，并携带appId+appsecret+code三个参数。</li><li>开发者服务端，接收微信接口服务返回的数据，session_key+opendId等。opendId是微信用户的唯一标识。</li><li>开发者服务端，自定义登录态，生成令牌(token)和openid等数据返回给小程序端，方便后绪请求身份校验。</li><li>小程序端，收到自定义登录态，存储storage。</li><li>小程序端，后绪通过wx.request()发起业务请求时，携带token。</li><li>开发者服务端，收到请求后，通过携带的token，解析当前登录用户的id。</li><li>开发者服务端，身份校验通过后，继续相关的业务逻辑处理，最终返回业务数据。</li></ol><p><strong>下面使用Postman来进行测试：</strong></p><div class="note info no-icon flat"><ol><li>调用 <a href="https://developers.weixin.qq.com/miniprogram/dev/api/open-api/login/wx.login.html">wx.login()</a> 获取 <strong>临时登录凭证code</strong> ，并回传到开发者服务器。</li><li>调用 <a href="https://developers.weixin.qq.com/miniprogram/dev/api-backend/open-api/login/auth.code2Session.html">auth.code2Session</a> 接口，换取 <strong>用户唯一标识 OpenID</strong> 、 用户在微信开放平台帐号下的<strong>唯一标识UnionID</strong>（若当前小程序已绑定到微信开放平台帐号） 和 <strong>会话密钥 session_key</strong>。</li></ol><p>之后开发者服务器可以根据用户标识来生成自定义登录态，用于后续业务逻辑中前后端交互时识别用户身份。</p></div><p><strong>实现步骤</strong></p><ol><li><strong>获取授权码</strong><br>每次登录小程序时，小程序会弹出提示框，点击确定按钮，即可获取授权码，每个授权码只对应一次登录，因此每次测试都需要重新获取授权码<div class="img-wrap"><div class="img-bg"><img class="img" src="https://raw.githubusercontent.com/silvan2077/markdown_pic1/main/Picgo/image-20221204222008130.png"/></div></div></li><li><strong>明确请求接口</strong><br>请求方式、请求路径、请求参数<div class="img-wrap"><div class="img-bg"><img class="img" src="https://raw.githubusercontent.com/silvan2077/markdown_pic1/main/Picgo/image-20221204222434054.png"/></div></div></li><li><strong>发送请求</strong><br>获取<code>session_key</code>和<code>openid</code><div class="img-wrap"><div class="img-bg"><img class="img" src="https://raw.githubusercontent.com/silvan2077/markdown_pic1/main/Picgo/image-20221204223956568.png"/></div></div>如果出现code been used错误提示，说明授权码已被使用过，需要重新获取<div class="img-wrap"><div class="img-bg"><img class="img" src="https://raw.githubusercontent.com/silvan2077/markdown_pic1/main/Picgo/image-20221204224130409.png"/></div></div></li></ol><h3 id="需求分析和设计-15"><a href="#需求分析和设计-15" class="headerlink" title="需求分析和设计"></a>需求分析和设计</h3><h4 id="产品原型"><a href="#产品原型" class="headerlink" title="产品原型"></a>产品原型</h4><p>用户进入小程序后，点击授权后才能点餐，需要获取当前用户的相关信息，比如昵称、头像等。该方式基于微信登录来实现小程序的登录，若用户第一次使用小程序进行点餐，则说明是新用户，需要将新用户的信息保存到数据库中完成自动注册<br><div class="img-wrap"><div class="img-bg"><img class="img" src="https://raw.githubusercontent.com/silvan2077/markdown_pic1/main/Picgo/image-20221205173711304.png"/></div></div></p><h4 id="接口设计-3"><a href="#接口设计-3" class="headerlink" title="接口设计"></a>接口设计</h4><p>通过微信登录就需要获得微信用户的openid，在小程序端获取授权码后，向后端服务发送请求并携带授权码，这样后端服务在收到授权码后就可以去请求微信你接口服务，最终后端向小程序返回openid和token等数据<br><div class="img-wrap"><div class="img-bg"><img class="img" src="https://raw.githubusercontent.com/silvan2077/markdown_pic1/main/Picgo/image-20221205175429394.png"/></div></div></p><div class="img-wrap"><div class="img-bg"><img class="img" src="https://raw.githubusercontent.com/silvan2077/markdown_pic1/main/Picgo/image-20221205175441256.png"/></div></div><div class="note warning no-icon flat"><p><strong>说明：</strong> 请求路径/user/user/login,第一个user代表用户端，第二个user代表用户模块。</p></div><h4 id="表设计-3"><a href="#表设计-3" class="headerlink" title="表设计"></a>表设计</h4><p>用户第一次使用小程序时会自动注册账号，并将用户信息存储到User表。</p><div class="table-container"><table><thead><tr><th><strong>字段名</strong></th><th><strong>数据类型</strong></th><th><strong>说明</strong></th><th><strong>备注</strong></th></tr></thead><tbody><tr><td>id</td><td>bigint</td><td>主键</td><td>自增</td></tr><tr><td>openid</td><td>varchar(45)</td><td>微信用户的唯一标识</td><td></td></tr><tr><td>name</td><td>varchar(32)</td><td>用户姓名</td><td></td></tr><tr><td>phone</td><td>varchar(11)</td><td>手机号</td><td></td></tr><tr><td>sex</td><td>varchar(2)</td><td>性别</td><td></td></tr><tr><td>id_number</td><td>varchar(18)</td><td>身份证号</td><td></td></tr><tr><td>avatar</td><td>varchar(500)</td><td>微信用户头像路径</td><td></td></tr><tr><td>create_time</td><td>datetime</td><td>注册时间</td></tr></tbody></table></div><div class="note warning no-icon flat"><p><strong>说明：</strong> 手机号字段比较特殊，个人身份注册的小程序没有权限获取到微信用户的手机号。如果是以企业的资质<br>注册的小程序就能够拿到微信用户的手机号。</p></div><h3 id="代码开发-10"><a href="#代码开发-10" class="headerlink" title="代码开发"></a>代码开发</h3><h4 id="添加相关配置"><a href="#添加相关配置" class="headerlink" title="添加相关配置"></a>添加相关配置</h4><ul><li>添加微信登录所需的配置项<div class="tabs" id="微信登录-代码开发"><ul class="nav-tabs"><li class="tab active"><button type="button" data-href="#微信登录-代码开发-1">application-dev.yml</button></li><li class="tab"><button type="button" data-href="#微信登录-代码开发-2">application.yml</button></li></ul><div class="tab-contents"><div class="tab-item-content active" id="微信登录-代码开发-1"><figure class="highlight yml"><table><tr><td class="gutter"><pre><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="attr">sky:</span></span><br><span class="line">  <span class="attr">wechat:</span></span><br><span class="line">    <span class="attr">appid:</span> <span class="string">wxffb3637a228223b8</span></span><br><span class="line">    <span class="attr">secret:</span> <span class="string">84311df9199ecacdf4f12d27b6b9522d</span></span><br></pre></td></tr></table></figure><button type="button" class="tab-to-top" aria-label="scroll to top"><i class="fas fa-arrow-up"></i></button></div><div class="tab-item-content" id="微信登录-代码开发-2"><figure class="highlight yml"><table><tr><td class="gutter"><pre><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="attr">sky:</span></span><br><span class="line">  <span class="attr">wechat:</span></span><br><span class="line">    <span class="attr">appid:</span> <span class="string">$&#123;sky.wechat.appid&#125;</span></span><br><span class="line">    <span class="attr">secret:</span> <span class="string">$&#123;sky.wechat.secret&#125;</span></span><br></pre></td></tr></table></figure><button type="button" class="tab-to-top" aria-label="scroll to top"><i class="fas fa-arrow-up"></i></button></div></div></div></li></ul><p>在<code>application.yml</code>中配置为用户生成jwt令牌时需要的配置项:<br><figure class="highlight yml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><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="attr">sky:</span></span><br><span class="line">  <span class="attr">jwt:</span></span><br><span class="line">    <span class="comment"># 设置jwt签名加密时使用的秘钥</span></span><br><span class="line">    <span class="attr">admin-secret-key:</span> <span class="string">itcast</span></span><br><span class="line">    <span class="comment"># 设置jwt过期时间</span></span><br><span class="line">    <span class="attr">admin-ttl:</span> <span class="number">7200000</span></span><br><span class="line">    <span class="comment"># 设置前端传递过来的令牌名称</span></span><br><span class="line">    <span class="attr">admin-token-name:</span> <span class="string">token</span></span><br><span class="line">    <span class="attr">user-secret-key:</span> <span class="string">itheima</span></span><br><span class="line">    <span class="attr">user-ttl:</span> <span class="number">7200000</span></span><br><span class="line">    <span class="attr">user-token-name:</span> <span class="string">authentication</span></span><br></pre></td></tr></table></figure></p><h4 id="DTO设计"><a href="#DTO设计" class="headerlink" title="DTO设计"></a>DTO设计</h4><p><strong>根据传入参数设计DTO类：</strong><br><div class="img-wrap"><div class="img-bg"><img class="img" src="https://raw.githubusercontent.com/silvan2077/markdown_pic1/main/Picgo/image-20221205183625049.png"/></div></div><br>在sky-pojo模块中已经给出了UserLoginDTO类</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"> * C端用户登录</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_">UserLoginDTO</span> <span class="keyword">implements</span> <span class="title class_">Serializable</span> &#123;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">private</span> String code;</span><br><span class="line"></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h4 id="VO设计"><a href="#VO设计" class="headerlink" title="VO设计"></a>VO设计</h4><p><strong>根据返回数据设计VO类：</strong><br><div class="img-wrap"><div class="img-bg"><img class="img" src="https://raw.githubusercontent.com/silvan2077/markdown_pic1/main/Picgo/image-20221205183923272.png"/></div></div><br>在sky-pojo模块中已经给出了UserLoginVO类</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="meta">@Data</span></span><br><span class="line"><span class="meta">@Builder</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="keyword">public</span> <span class="keyword">class</span> <span class="title class_">UserLoginVO</span> <span class="keyword">implements</span> <span class="title class_">Serializable</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 openid;</span><br><span class="line">    <span class="keyword">private</span> String token;</span><br><span class="line"></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h4 id="Controller层-9"><a href="#Controller层-9" class="headerlink" title="Controller层"></a>Controller层</h4><p><strong>根据接口定义创建UserController的login方法：</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><br><span class="line"><span class="meta">@RestController</span></span><br><span class="line"><span class="meta">@RequestMapping(&quot;/user/user&quot;)</span></span><br><span class="line"><span class="meta">@Api(tags = &quot;C端用户相关接口&quot;)</span></span><br><span class="line"><span class="meta">@Slf4j</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 class="meta">@Autowired</span></span><br><span class="line">    <span class="keyword">private</span> JwtProperties jwtProperties;</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 class="doctag">@param</span> userLoginDTO</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@return</span></span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    <span class="meta">@PostMapping(&quot;/login&quot;)</span></span><br><span class="line">    <span class="meta">@ApiOperation(&quot;微信登录&quot;)</span></span><br><span class="line">    <span class="keyword">public</span> Result&lt;UserLoginVO&gt; <span class="title function_">login</span><span class="params">(<span class="meta">@RequestBody</span> UserLoginDTO userLoginDTO)</span>&#123;</span><br><span class="line">        log.info(<span class="string">&quot;微信用户登录：&#123;&#125;&quot;</span>,userLoginDTO.getCode());</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.wxLogin(userLoginDTO);<span class="comment">//后绪步骤实现</span></span><br><span class="line"></span><br><span class="line">        <span class="comment">//为微信用户生成jwt令牌</span></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">        claims.put(JwtClaimsConstant.USER_ID,user.getId());</span><br><span class="line">        <span class="type">String</span> <span class="variable">token</span> <span class="operator">=</span> JwtUtil.createJWT(jwtProperties.getUserSecretKey(), jwtProperties.getUserTtl(), claims);</span><br><span class="line"></span><br><span class="line">        <span class="type">UserLoginVO</span> <span class="variable">userLoginVO</span> <span class="operator">=</span> UserLoginVO.builder()</span><br><span class="line">                .id(user.getId())</span><br><span class="line">                .openid(user.getOpenid())</span><br><span class="line">                .token(token)</span><br><span class="line">                .build();</span><br><span class="line">        <span class="keyword">return</span> Result.success(userLoginVO);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h4 id="Service层-6"><a href="#Service层-6" class="headerlink" title="Service层"></a>Service层</h4><p><strong>创建UserService接口：</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="keyword">public</span> <span class="keyword">interface</span> <span class="title class_">UserService</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 class="doctag">@param</span> userLoginDTO</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@return</span></span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    User <span class="title function_">wxLogin</span><span class="params">(UserLoginDTO userLoginDTO)</span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p><strong>创建UserServiceImpl实现类：</strong> 实现获取微信用户的openid和微信登录功能</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></pre></td><td class="code"><pre><span class="line"><span class="meta">@Service</span></span><br><span class="line"><span class="meta">@Slf4j</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="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">WX_LOGIN</span> <span class="operator">=</span> <span class="string">&quot;https://api.weixin.qq.com/sns/jscode2session&quot;</span>;</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> WeChatProperties weChatProperties;</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="comment">     * 微信登录</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@param</span> userLoginDTO</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@return</span></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_">wxLogin</span><span class="params">(UserLoginDTO userLoginDTO)</span> &#123;</span><br><span class="line">        <span class="type">String</span> <span class="variable">openid</span> <span class="operator">=</span> getOpenid(userLoginDTO.getCode());</span><br><span class="line"></span><br><span class="line">        <span class="comment">//判断openid是否为空，如果为空表示登录失败，抛出业务异常</span></span><br><span class="line">        <span class="keyword">if</span>(openid == <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_">LoginFailedException</span>(MessageConstant.LOGIN_FAILED);</span><br><span 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> userMapper.getByOpenid(openid);</span><br><span class="line"></span><br><span class="line">        <span class="comment">//如果是新用户，自动完成注册</span></span><br><span class="line">        <span class="keyword">if</span>(user == <span class="literal">null</span>)&#123;</span><br><span class="line">            user = User.builder()</span><br><span class="line">                    .openid(openid)</span><br><span class="line">                    .createTime(LocalDateTime.now())</span><br><span class="line">                    .build();</span><br><span class="line">            userMapper.insert(user);<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">return</span> 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">     * 调用微信接口服务，获取微信用户的openid</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@param</span> code</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@return</span></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_">getOpenid</span><span class="params">(String code)</span>&#123;</span><br><span class="line">        <span class="comment">//调用微信接口服务，获得当前微信用户的openid</span></span><br><span class="line">        Map&lt;String, String&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;appid&quot;</span>,weChatProperties.getAppid());</span><br><span class="line">        map.put(<span class="string">&quot;secret&quot;</span>,weChatProperties.getSecret());</span><br><span class="line">        map.put(<span class="string">&quot;js_code&quot;</span>,code);</span><br><span class="line">        map.put(<span class="string">&quot;grant_type&quot;</span>,<span class="string">&quot;authorization_code&quot;</span>);</span><br><span class="line">        <span class="type">String</span> <span class="variable">json</span> <span class="operator">=</span> HttpClientUtil.doGet(WX_LOGIN, map);</span><br><span class="line"></span><br><span class="line">        <span class="type">JSONObject</span> <span class="variable">jsonObject</span> <span class="operator">=</span> JSON.parseObject(json);</span><br><span class="line">        <span class="type">String</span> <span class="variable">openid</span> <span class="operator">=</span> jsonObject.getString(<span class="string">&quot;openid&quot;</span>);</span><br><span class="line">        <span class="keyword">return</span> openid;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h4 id="Mapper层-9"><a href="#Mapper层-9" class="headerlink" title="Mapper层"></a>Mapper层</h4><p><strong>创建UserMapper接口：</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="meta">@Mapper</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">     * 根据openid查询用户</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@param</span> openid</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@return</span></span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    <span class="meta">@Select(&quot;select * from user where openid = #&#123;openid&#125;&quot;)</span></span><br><span class="line">    User <span class="title function_">getByOpenid</span><span class="params">(String openid)</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 class="doctag">@param</span> user</span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    <span class="keyword">void</span> <span class="title function_">insert</span><span class="params">(User user)</span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p><strong>创建UserMapper.xml映射文件：</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><span class="line">11</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 class="tag">&lt;<span class="name">mapper</span> <span class="attr">namespace</span>=<span class="string">&quot;com.sky.mapper.UserMapper&quot;</span>&gt;</span></span><br><span class="line"></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">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 user (openid, name, phone, sex, id_number, avatar, create_time)</span><br><span class="line">        values (#&#123;openid&#125;, #&#123;name&#125;, #&#123;phone&#125;, #&#123;sex&#125;, #&#123;idNumber&#125;, #&#123;avatar&#125;, #&#123;createTime&#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="tag">&lt;/<span class="name">mapper</span>&gt;</span></span><br></pre></td></tr></table></figure><h4 id="编写拦截器"><a href="#编写拦截器" class="headerlink" title="编写拦截器"></a>编写拦截器</h4><p><strong>编写拦截器JwtTokenUserInterceptor：</strong> 统一拦截用户端发送的请求并进行jwt校验</p><div class="note danger no-icon flat"><p>注意此处<code>handler instanceof HandlerMethod</code>中的HandlerMethod导入的是<code>org.springframework.web.method</code>包下的，之前引入的<code>org.springframework.Message.handler</code>包下的HandlerMethod类，导致此处判断条件不成立，从而导致拦截器无法生效。</p><div class="img-wrap"><div class="img-bg"><img class="img" src="https://raw.githubusercontent.com/silvan2077/markdown_pic1/main/Picgo/20251116123356.png"/></div></div></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></pre></td><td class="code"><pre><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="meta">@Component</span></span><br><span class="line"><span class="meta">@Slf4j</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">JwtTokenUserInterceptor</span> <span class="keyword">implements</span> <span class="title class_">HandlerInterceptor</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> JwtProperties jwtProperties;</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="comment">     * <span class="doctag">@param</span> request</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@param</span> response</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@param</span> handler</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@return</span></span></span><br><span class="line"><span class="comment">     * <span class="doctag">@throws</span> Exception</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_">preHandle</span><span class="params">(HttpServletRequest request, HttpServletResponse response, Object handler)</span> <span class="keyword">throws</span> Exception &#123;</span><br><span class="line">        <span class="comment">//判断当前拦截到的是Controller的方法还是其他资源</span></span><br><span class="line">        <span class="keyword">if</span> (!(handler <span class="keyword">instanceof</span> HandlerMethod)) &#123;</span><br><span class="line">            <span class="comment">//当前拦截到的不是动态方法，直接放行</span></span><br><span class="line">            <span class="keyword">return</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">//1、从请求头中获取令牌</span></span><br><span class="line">        <span class="type">String</span> <span class="variable">token</span> <span class="operator">=</span> request.getHeader(jwtProperties.getUserTokenName());</span><br><span class="line"></span><br><span class="line">        <span class="comment">//2、校验令牌</span></span><br><span class="line">        <span class="keyword">try</span> &#123;</span><br><span class="line">            log.info(<span class="string">&quot;jwt校验:&#123;&#125;&quot;</span>, token);</span><br><span class="line">            <span class="type">Claims</span> <span class="variable">claims</span> <span class="operator">=</span> JwtUtil.parseJWT(jwtProperties.getUserSecretKey(), token);</span><br><span class="line">            <span class="type">Long</span> <span class="variable">userId</span> <span class="operator">=</span> Long.valueOf(claims.get(JwtClaimsConstant.USER_ID).toString());</span><br><span class="line">            log.info(<span class="string">&quot;当前用户的id：&quot;</span>, userId);</span><br><span class="line">            BaseContext.setCurrentId(userId);</span><br><span class="line">            <span class="comment">//3、通过，放行</span></span><br><span class="line">            <span class="keyword">return</span> <span class="literal">true</span>;</span><br><span class="line">        &#125; <span class="keyword">catch</span> (Exception ex) &#123;</span><br><span class="line">            <span class="comment">//4、不通过，响应401状态码</span></span><br><span class="line">            response.setStatus(<span class="number">401</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">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p><strong>在WebMvcConfiguration配置类中注册拦截器：</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">@Autowired</span></span><br><span class="line">   <span class="keyword">private</span> JwtTokenUserInterceptor jwtTokenUserInterceptor;</span><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">@param</span> registry</span></span><br><span class="line"><span class="comment">    */</span></span><br><span class="line">   <span class="keyword">protected</span> <span class="keyword">void</span> <span class="title function_">addInterceptors</span><span class="params">(InterceptorRegistry registry)</span> &#123;</span><br><span class="line">       log.info(<span class="string">&quot;开始注册自定义拦截器...&quot;</span>);</span><br><span class="line">       <span class="comment">//.........</span></span><br><span class="line"></span><br><span class="line">       registry.addInterceptor(jwtTokenUserInterceptor)</span><br><span class="line">               .addPathPatterns(<span class="string">&quot;/user/**&quot;</span>)</span><br><span class="line">               .excludePathPatterns(<span class="string">&quot;/user/user/login&quot;</span>)</span><br><span class="line">               .excludePathPatterns(<span class="string">&quot;/user/shop/status&quot;</span>);</span><br><span class="line">   &#125;</span><br></pre></td></tr></table></figure><h3 id="功能测试-13"><a href="#功能测试-13" class="headerlink" title="功能测试"></a>功能测试</h3><p>重新编译并登录，能成功获取到openid和token即可<br><div class="img-wrap"><div class="img-bg"><img class="img" src="https://raw.githubusercontent.com/silvan2077/markdown_pic1/main/Picgo/image-20221205192506490.png"/></div></div><br>此时查看数据库的user表，就能看到成功将第一次登录的用户信息注册到表里了</p><h2 id="导入商品浏览功能代码"><a href="#导入商品浏览功能代码" class="headerlink" title="导入商品浏览功能代码"></a>导入商品浏览功能代码</h2><h3 id="需求分析和设计-16"><a href="#需求分析和设计-16" class="headerlink" title="需求分析和设计"></a>需求分析和设计</h3><p>用户登录成功后跳转到系统首页，在首页需要根据分类来展示菜品和套餐。如果菜品设置了口味信息，需要展示<code>选择规格</code>按钮，否则显示<code>+</code>按钮。<br><div class="tabs" id="导入商品浏览功能代码原型图"><ul class="nav-tabs"><li class="tab active"><button type="button" data-href="#导入商品浏览功能代码原型图-1">菜品列表效果图</button></li><li class="tab"><button type="button" data-href="#导入商品浏览功能代码原型图-2">菜品口味效果图</button></li><li class="tab"><button type="button" data-href="#导入商品浏览功能代码原型图-3">套餐列表效果图</button></li><li class="tab"><button type="button" data-href="#导入商品浏览功能代码原型图-4">套餐详情效果图</button></li></ul><div class="tab-contents"><div class="tab-item-content active" id="导入商品浏览功能代码原型图-1"><div class="img-wrap"><div class="img-bg"><img class="img" src="https://raw.githubusercontent.com/silvan2077/markdown_pic1/main/Picgo/image-20221205193821129.png"/></div></div><button type="button" class="tab-to-top" aria-label="scroll to top"><i class="fas fa-arrow-up"></i></button></div><div class="tab-item-content" id="导入商品浏览功能代码原型图-2"><div class="img-wrap"><div class="img-bg"><img class="img" src="https://raw.githubusercontent.com/silvan2077/markdown_pic1/main/Picgo/image-20221205193846867.png"/></div></div><button type="button" class="tab-to-top" aria-label="scroll to top"><i class="fas fa-arrow-up"></i></button></div><div class="tab-item-content" id="导入商品浏览功能代码原型图-3"><div class="img-wrap"><div class="img-bg"><img class="img" src="https://raw.githubusercontent.com/silvan2077/markdown_pic1/main/Picgo/image-20221205193859016.png"/></div></div><button type="button" class="tab-to-top" aria-label="scroll to top"><i class="fas fa-arrow-up"></i></button></div><div class="tab-item-content" id="导入商品浏览功能代码原型图-4"><div class="img-wrap"><div class="img-bg"><img class="img" src="https://raw.githubusercontent.com/silvan2077/markdown_pic1/main/Picgo/image-20221205193911476.png"/></div></div><button type="button" class="tab-to-top" aria-label="scroll to top"><i class="fas fa-arrow-up"></i></button></div></div></div></p><p><strong>接口设计</strong></p><ul><li>查询分类</li><li>根据分类id查询菜品</li><li>根据分类id查询套餐</li><li>根据套餐id查询包含的菜品</li></ul><p>明确每个接口的请求方式、请求路径、传入参数和返回值。</p><div class="tabs" id="商品浏览功能接口设计"><ul class="nav-tabs"><li class="tab active"><button type="button" data-href="#商品浏览功能接口设计-1">查询分类</button></li><li class="tab"><button type="button" data-href="#商品浏览功能接口设计-2">根据分类id查询菜品</button></li><li class="tab"><button type="button" data-href="#商品浏览功能接口设计-3">根据分类id查询套餐</button></li><li class="tab"><button type="button" data-href="#商品浏览功能接口设计-4">根据套餐id查询包含的菜品</button></li></ul><div class="tab-contents"><div class="tab-item-content active" id="商品浏览功能接口设计-1"><div class="img-wrap"><div class="img-bg"><img class="img" src="https://raw.githubusercontent.com/silvan2077/markdown_pic1/main/Picgo/image-20221205200235328.png"/></div></div><div class="img-wrap"><div class="img-bg"><img class="img" src="https://raw.githubusercontent.com/silvan2077/markdown_pic1/main/Picgo/image-20221205200246588.png"/></div></div><button type="button" class="tab-to-top" aria-label="scroll to top"><i class="fas fa-arrow-up"></i></button></div><div class="tab-item-content" id="商品浏览功能接口设计-2"><div class="img-wrap"><div class="img-bg"><img class="img" src="https://raw.githubusercontent.com/silvan2077/markdown_pic1/main/Picgo/image-20221205200319686.png"/></div></div><div class="img-wrap"><div class="img-bg"><img class="img" src="https://raw.githubusercontent.com/silvan2077/markdown_pic1/main/Picgo/image-20221205200425607.png"/></div></div><button type="button" class="tab-to-top" aria-label="scroll to top"><i class="fas fa-arrow-up"></i></button></div><div class="tab-item-content" id="商品浏览功能接口设计-3"><div class="img-wrap"><div class="img-bg"><img class="img" src="https://raw.githubusercontent.com/silvan2077/markdown_pic1/main/Picgo/image-20221205200415135.png"/></div></div><div class="img-wrap"><div class="img-bg"><img class="img" src="https://raw.githubusercontent.com/silvan2077/markdown_pic1/main/Picgo/image-20221205200425607.png"/></div></div><button type="button" class="tab-to-top" aria-label="scroll to top"><i class="fas fa-arrow-up"></i></button></div><div class="tab-item-content" id="商品浏览功能接口设计-4"><div class="img-wrap"><div class="img-bg"><img class="img" src="https://raw.githubusercontent.com/silvan2077/markdown_pic1/main/Picgo/image-20221205200537591.png"/></div></div><button type="button" class="tab-to-top" aria-label="scroll to top"><i class="fas fa-arrow-up"></i></button></div></div></div><h3 id="代码导入-1"><a href="#代码导入-1" class="headerlink" title="代码导入"></a>代码导入</h3><p>导入资料中的商品浏览功能代码即可<br><div class="img-wrap"><div class="img-bg"><img class="img" src="https://raw.githubusercontent.com/silvan2077/markdown_pic1/main/Picgo/image-20221205201043358.png"/></div></div></p><h4 id="Mapper层-10"><a href="#Mapper层-10" class="headerlink" title="Mapper层"></a>Mapper层</h4><p>在SetmealMapper.java中添加<code>list</code>和<code>getDishItemBySetmealId</code>两个方法<br><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><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"><span class="comment"> * 动态条件查询套餐</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@param</span> setmeal</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@return</span></span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line">List&lt;Setmeal&gt; <span class="title function_">list</span><span class="params">(Setmeal setmeal)</span>;</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 class="doctag">@param</span> setmealId</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@return</span></span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="meta">@Select(&quot;select sd.name, sd.copies, d.image, d.description &quot; +</span></span><br><span class="line"><span class="meta">        &quot;from setmeal_dish sd left join dish d on sd.dish_id = d.id &quot; +</span></span><br><span class="line"><span class="meta">        &quot;where sd.setmeal_id = #&#123;setmealId&#125;&quot;)</span></span><br><span class="line">List&lt;DishItemVO&gt; <span class="title function_">getDishItemBySetmealId</span><span class="params">(Long setmealId)</span>;</span><br></pre></td></tr></table></figure><br><strong>创建SetmealMapper.xml文件</strong><br><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></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 class="tag">&lt;<span class="name">mapper</span> <span class="attr">namespace</span>=<span class="string">&quot;com.sky.mapper.SetmealMapper&quot;</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;list&quot;</span> <span class="attr">parameterType</span>=<span class="string">&quot;Setmeal&quot;</span> <span class="attr">resultType</span>=<span class="string">&quot;Setmeal&quot;</span>&gt;</span></span><br><span class="line">        select * from setmeal</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;name != null&quot;</span>&gt;</span></span><br><span class="line">                and name like concat(&#x27;%&#x27;,#&#123;name&#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;categoryId != null&quot;</span>&gt;</span></span><br><span class="line">                and category_id = #&#123;categoryId&#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">                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">where</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 class="tag">&lt;/<span class="name">mapper</span>&gt;</span></span><br></pre></td></tr></table></figure></p><h4 id="Service层-7"><a href="#Service层-7" class="headerlink" title="Service层"></a>Service层</h4><div class="tabs" id="导入商品浏览功能service层"><ul class="nav-tabs"><li class="tab active"><button type="button" data-href="#导入商品浏览功能service层-1">SetmealService</button></li><li class="tab"><button type="button" data-href="#导入商品浏览功能service层-2">SetmealServiceImpl</button></li><li class="tab"><button type="button" data-href="#导入商品浏览功能service层-3">DishService</button></li><li class="tab"><button type="button" data-href="#导入商品浏览功能service层-4">DishServiceImpl</button></li></ul><div class="tab-contents"><div class="tab-item-content active" id="导入商品浏览功能service层-1"><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><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">interface</span> <span class="title class_">SetmealService</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 class="doctag">@param</span> setmeal</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@return</span></span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    List&lt;Setmeal&gt; <span class="title function_">list</span><span class="params">(Setmeal setmeal)</span>;</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 class="doctag">@param</span> id</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@return</span></span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    List&lt;DishItemVO&gt; <span class="title function_">getDishItemById</span><span class="params">(Long id)</span>;</span><br><span class="line"></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><button type="button" class="tab-to-top" aria-label="scroll to top"><i class="fas fa-arrow-up"></i></button></div><div class="tab-item-content" id="导入商品浏览功能service层-2"><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><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">@Service</span></span><br><span class="line"><span class="meta">@Slf4j</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">SetmealServiceImpl</span> <span class="keyword">implements</span> <span class="title class_">SetmealService</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> SetmealMapper setmealMapper;</span><br><span class="line">    <span class="meta">@Autowired</span></span><br><span class="line">    <span class="keyword">private</span> SetmealDishMapper setmealDishMapper;</span><br><span class="line">    <span class="meta">@Autowired</span></span><br><span class="line">    <span class="keyword">private</span> DishMapper dishMapper;</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 class="doctag">@param</span> setmeal</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@return</span></span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    <span class="keyword">public</span> List&lt;Setmeal&gt; <span class="title function_">list</span><span class="params">(Setmeal setmeal)</span> &#123;</span><br><span class="line">        List&lt;Setmeal&gt; list = setmealMapper.list(setmeal);</span><br><span class="line">        <span class="keyword">return</span> list;</span><br><span 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 class="doctag">@param</span> id</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@return</span></span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    <span class="keyword">public</span> List&lt;DishItemVO&gt; <span class="title function_">getDishItemById</span><span class="params">(Long id)</span> &#123;</span><br><span class="line">        <span class="keyword">return</span> setmealMapper.getDishItemBySetmealId(id);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><button type="button" class="tab-to-top" aria-label="scroll to top"><i class="fas fa-arrow-up"></i></button></div><div class="tab-item-content" id="导入商品浏览功能service层-3"><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><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="comment">    * 条件查询菜品和口味</span></span><br><span class="line"><span class="comment">    * <span class="doctag">@param</span> dish</span></span><br><span class="line"><span class="comment">    * <span class="doctag">@return</span></span></span><br><span class="line"><span class="comment">    */</span></span><br><span class="line">   List&lt;DishVO&gt; <span class="title function_">listWithFlavor</span><span class="params">(Dish dish)</span>;</span><br></pre></td></tr></table></figure><button type="button" class="tab-to-top" aria-label="scroll to top"><i class="fas fa-arrow-up"></i></button></div><div class="tab-item-content" id="导入商品浏览功能service层-4"><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><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">@param</span> dish</span></span><br><span class="line"><span class="comment">    * <span class="doctag">@return</span></span></span><br><span class="line"><span class="comment">    */</span></span><br><span class="line">   <span class="keyword">public</span> List&lt;DishVO&gt; <span class="title function_">listWithFlavor</span><span class="params">(Dish dish)</span> &#123;</span><br><span class="line">       List&lt;Dish&gt; dishList = dishMapper.list(dish);</span><br><span class="line"></span><br><span class="line">       List&lt;DishVO&gt; dishVOList = <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> (Dish d : dishList) &#123;</span><br><span class="line">           <span class="type">DishVO</span> <span class="variable">dishVO</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">DishVO</span>();</span><br><span class="line">           BeanUtils.copyProperties(d,dishVO);</span><br><span class="line"></span><br><span class="line">           <span class="comment">//根据菜品id查询对应的口味</span></span><br><span class="line">           List&lt;DishFlavor&gt; flavors = dishFlavorMapper.getByDishId(d.getId());</span><br><span class="line"></span><br><span class="line">           dishVO.setFlavors(flavors);</span><br><span class="line">           dishVOList.add(dishVO);</span><br><span class="line">       &#125;</span><br><span class="line"></span><br><span class="line">       <span class="keyword">return</span> dishVOList;</span><br><span class="line">   &#125;</span><br></pre></td></tr></table></figure><button type="button" class="tab-to-top" aria-label="scroll to top"><i class="fas fa-arrow-up"></i></button></div></div></div><h4 id="Controller层-10"><a href="#Controller层-10" class="headerlink" title="Controller层"></a>Controller层</h4><div class="tabs" id="商品浏览功能-controller层"><ul class="nav-tabs"><li class="tab active"><button type="button" data-href="#商品浏览功能-controller层-1">DishController</button></li><li class="tab"><button type="button" data-href="#商品浏览功能-controller层-2">CategoryController</button></li><li class="tab"><button type="button" data-href="#商品浏览功能-controller层-3">SetmealController</button></li></ul><div class="tab-contents"><div class="tab-item-content active" id="商品浏览功能-controller层-1"><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><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">@RestController(&quot;userDishController&quot;)</span></span><br><span class="line"><span class="meta">@RequestMapping(&quot;/user/dish&quot;)</span></span><br><span class="line"><span class="meta">@Slf4j</span></span><br><span class="line"><span class="meta">@Api(tags = &quot;C端-菜品浏览接口&quot;)</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">DishController</span> &#123;</span><br><span class="line">    <span class="meta">@Autowired</span></span><br><span class="line">    <span class="keyword">private</span> DishService dishService;</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="comment">     * <span class="doctag">@param</span> categoryId</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@return</span></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="meta">@ApiOperation(&quot;根据分类id查询菜品&quot;)</span></span><br><span class="line">    <span class="keyword">public</span> Result&lt;List&lt;DishVO&gt;&gt; <span class="title function_">list</span><span class="params">(Long categoryId)</span> &#123;</span><br><span class="line">        <span class="type">Dish</span> <span class="variable">dish</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">Dish</span>();</span><br><span class="line">        dish.setCategoryId(categoryId);</span><br><span class="line">        dish.setStatus(StatusConstant.ENABLE);<span class="comment">//查询起售中的菜品</span></span><br><span class="line"></span><br><span class="line">        List&lt;DishVO&gt; list = dishService.listWithFlavor(dish);</span><br><span class="line"></span><br><span class="line">        <span class="keyword">return</span> Result.success(list);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><button type="button" class="tab-to-top" aria-label="scroll to top"><i class="fas fa-arrow-up"></i></button></div><div class="tab-item-content" id="商品浏览功能-controller层-2"><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><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="meta">@RestController(&quot;userCategoryController&quot;)</span></span><br><span class="line"><span class="meta">@RequestMapping(&quot;/user/category&quot;)</span></span><br><span class="line"><span class="meta">@Api(tags = &quot;C端-分类接口&quot;)</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">CategoryController</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> CategoryService categoryService;</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 class="doctag">@param</span> type</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@return</span></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="meta">@ApiOperation(&quot;查询分类&quot;)</span></span><br><span class="line">    <span class="keyword">public</span> Result&lt;List&lt;Category&gt;&gt; <span class="title function_">list</span><span class="params">(Integer type)</span> &#123;</span><br><span class="line">        List&lt;Category&gt; list = categoryService.list(type);</span><br><span class="line">        <span class="keyword">return</span> Result.success(list);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><button type="button" class="tab-to-top" aria-label="scroll to top"><i class="fas fa-arrow-up"></i></button></div><div class="tab-item-content" id="商品浏览功能-controller层-3"><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><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="meta">@RestController(&quot;userSetmealController&quot;)</span></span><br><span class="line"><span class="meta">@RequestMapping(&quot;/user/setmeal&quot;)</span></span><br><span class="line"><span class="meta">@Api(tags = &quot;C端-套餐浏览接口&quot;)</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">SetmealController</span> &#123;</span><br><span class="line">    <span class="meta">@Autowired</span></span><br><span class="line">    <span class="keyword">private</span> SetmealService setmealService;</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">@param</span> categoryId</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@return</span></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="meta">@ApiOperation(&quot;根据分类id查询套餐&quot;)</span></span><br><span class="line">    <span class="keyword">public</span> Result&lt;List&lt;Setmeal&gt;&gt; <span class="title function_">list</span><span class="params">(Long categoryId)</span> &#123;</span><br><span class="line">        <span class="type">Setmeal</span> <span class="variable">setmeal</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">Setmeal</span>();</span><br><span class="line">        setmeal.setCategoryId(categoryId);</span><br><span class="line">        setmeal.setStatus(StatusConstant.ENABLE);</span><br><span class="line"></span><br><span class="line">        List&lt;Setmeal&gt; list = setmealService.list(setmeal);</span><br><span class="line">        <span class="keyword">return</span> Result.success(list);</span><br><span 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="comment">     * <span class="doctag">@param</span> id</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@return</span></span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    <span class="meta">@GetMapping(&quot;/dish/&#123;id&#125;&quot;)</span></span><br><span class="line">    <span class="meta">@ApiOperation(&quot;根据套餐id查询包含的菜品列表&quot;)</span></span><br><span class="line">    <span class="keyword">public</span> Result&lt;List&lt;DishItemVO&gt;&gt; <span class="title function_">dishList</span><span class="params">(<span class="meta">@PathVariable(&quot;id&quot;)</span> Long id)</span> &#123;</span><br><span class="line">        List&lt;DishItemVO&gt; list = setmealService.getDishItemById(id);</span><br><span class="line">        <span class="keyword">return</span> Result.success(list);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><button type="button" class="tab-to-top" aria-label="scroll to top"><i class="fas fa-arrow-up"></i></button></div></div></div><h1 id="day07"><a href="#day07" class="headerlink" title="day07"></a>day07</h1><h2 id="课程内容-3"><a href="#课程内容-3" class="headerlink" title="课程内容"></a>课程内容</h2><ul><li>缓存菜品</li><li>缓存套餐</li><li>添加购物车</li><li>查看购物车</li><li>清空购物车</li></ul><p>功能实现：<strong>缓存商品</strong>、<strong>购物车</strong><br>效果图：<br><div class="img-wrap"><div class="img-bg"><img class="img" src="https://raw.githubusercontent.com/silvan2077/markdown_pic1/main/Picgo/20251115145936.png"/></div></div></p><h2 id="缓存菜品"><a href="#缓存菜品" class="headerlink" title="缓存菜品"></a>缓存菜品</h2><h3 id="问题说明"><a href="#问题说明" class="headerlink" title="问题说明"></a>问题说明</h3><p>用户端小程序展示的菜品数据都是通过查询数据库获得，如果用户端访问量比较大，数据库访问压力随之增大。<br><div class="img-wrap"><div class="img-bg"><img class="img" src="https://raw.githubusercontent.com/silvan2077/markdown_pic1/main/Picgo/image-20221208180228667.png"/></div></div></p><div class="note info no-icon flat"><p><strong>结果：</strong> 系统响应慢、用户体验差</p></div><h3 id="优化思路"><a href="#优化思路" class="headerlink" title="优化思路"></a>优化思路</h3><p>通过Redis来缓存菜品数据，减少数据库查询操作。<br><div class="img-wrap"><div class="img-bg"><img class="img" src="https://raw.githubusercontent.com/silvan2077/markdown_pic1/main/Picgo/image-20221208180818572.png"/></div></div><br><div class="note danger no-icon flat"><p><strong>缓存逻辑：</strong></p><ul><li>每个分类下的菜品保存一份缓存数据</li><li>数据库中菜品数据有变更时清理缓存数据</li></ul></div></p><h3 id="代码开发-11"><a href="#代码开发-11" class="headerlink" title="代码开发"></a>代码开发</h3><p>修改用户端的DishCOntroller的list方法，在展示时加入缓存处理逻辑：<br><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><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="meta">@Autowired</span></span><br><span class="line">   <span class="keyword">private</span> RedisTemplate redisTemplate;</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="comment">    * <span class="doctag">@param</span> categoryId</span></span><br><span class="line"><span class="comment">    * <span class="doctag">@return</span></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="meta">@ApiOperation(&quot;根据分类id查询菜品&quot;)</span></span><br><span class="line">   <span class="keyword">public</span> Result&lt;List&lt;DishVO&gt;&gt; <span class="title function_">list</span><span class="params">(Long categoryId)</span> &#123;</span><br><span class="line"></span><br><span class="line">       <span class="comment">//构造redis中的key，规则：dish_分类id</span></span><br><span class="line">       <span class="type">String</span> <span class="variable">key</span> <span class="operator">=</span> <span class="string">&quot;dish_&quot;</span> + categoryId;</span><br><span class="line"></span><br><span class="line">       <span class="comment">//查询redis中是否存在菜品数据</span></span><br><span class="line">       List&lt;DishVO&gt; list = (List&lt;DishVO&gt;) redisTemplate.opsForValue().get(key);</span><br><span class="line">       <span class="keyword">if</span>(list != <span class="literal">null</span> &amp;&amp; list.size() &gt; <span class="number">0</span>)&#123;</span><br><span class="line">           <span class="comment">//如果存在，直接返回，无须查询数据库</span></span><br><span class="line">           <span class="keyword">return</span> Result.success(list);</span><br><span class="line">       &#125;</span><br><span class="line"><span class="comment">////////////////////////////////////////////////////////</span></span><br><span class="line">       <span class="type">Dish</span> <span class="variable">dish</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">Dish</span>();</span><br><span class="line">       dish.setCategoryId(categoryId);</span><br><span class="line">       dish.setStatus(StatusConstant.ENABLE);<span class="comment">//查询起售中的菜品</span></span><br><span class="line"></span><br><span class="line">       <span class="comment">//如果不存在，查询数据库，将查询到的数据放入redis中</span></span><br><span class="line">       list = dishService.listWithFlavor(dish);</span><br><span class="line">       <span class="comment">////////////////////////////////////////////////////////</span></span><br><span class="line">       redisTemplate.opsForValue().set(key, list);</span><br><span class="line"></span><br><span class="line">       <span class="keyword">return</span> Result.success(list);</span><br><span class="line">   &#125;</span><br></pre></td></tr></table></figure><br>为了保证 <strong>数据库</strong> 和 <strong>Redis</strong> 中的数据保持一致，修改<strong>管理端接口DishController</strong> 的相关方法，加入清理缓存逻辑,在更新或删除菜品时同时清除缓存。</p><p>需要改造的方法：</p><ul><li>新增菜品</li><li>修改菜品</li><li>批量删除菜品</li><li>起售、停售菜品<br>在管理端DishController中添加<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><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">@Autowired</span></span><br><span class="line">   <span class="keyword">private</span> RedisTemplate redisTemplate;</span><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">@param</span> pattern</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_">cleanCache</span><span class="params">(String pattern)</span>&#123;</span><br><span class="line">       <span class="type">Set</span> <span class="variable">keys</span> <span class="operator">=</span> redisTemplate.keys(pattern);</span><br><span class="line">       redisTemplate.delete(keys);</span><br><span class="line">   &#125;</span><br></pre></td></tr></table></figure></li></ul><p>优化DishController中的相关方法：</p><div class="tabs" id="缓存菜品代码开发-dishcontroller"><ul class="nav-tabs"><li class="tab active"><button type="button" data-href="#缓存菜品代码开发-dishcontroller-1">新增菜品</button></li><li class="tab"><button type="button" data-href="#缓存菜品代码开发-dishcontroller-2">菜品批量删除</button></li><li class="tab"><button type="button" data-href="#缓存菜品代码开发-dishcontroller-3">修改菜品</button></li><li class="tab"><button type="button" data-href="#缓存菜品代码开发-dishcontroller-4">菜品起售停售</button></li></ul><div class="tab-contents"><div class="tab-item-content active" id="缓存菜品代码开发-dishcontroller-1"><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><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 class="comment">    * <span class="doctag">@param</span> dishDTO</span></span><br><span class="line"><span class="comment">    * <span class="doctag">@return</span></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="meta">@ApiOperation(&quot;新增菜品&quot;)</span></span><br><span class="line">   <span class="keyword">public</span> Result <span class="title function_">save</span><span class="params">(<span class="meta">@RequestBody</span> DishDTO dishDTO)</span> &#123;</span><br><span class="line">       log.info(<span class="string">&quot;新增菜品：&#123;&#125;&quot;</span>, dishDTO);</span><br><span class="line">       dishService.saveWithFlavor(dishDTO);</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">key</span> <span class="operator">=</span> <span class="string">&quot;dish_&quot;</span> + dishDTO.getCategoryId();</span><br><span class="line">       cleanCache(key);</span><br><span class="line">       <span class="keyword">return</span> Result.success();</span><br><span class="line">   &#125;</span><br></pre></td></tr></table></figure><button type="button" class="tab-to-top" aria-label="scroll to top"><i class="fas fa-arrow-up"></i></button></div><div class="tab-item-content" id="缓存菜品代码开发-dishcontroller-2"><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><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 class="comment">    * <span class="doctag">@param</span> ids</span></span><br><span class="line"><span class="comment">    * <span class="doctag">@return</span></span></span><br><span class="line"><span class="comment">    */</span></span><br><span class="line">   <span class="meta">@DeleteMapping</span></span><br><span class="line">   <span class="meta">@ApiOperation(&quot;菜品批量删除&quot;)</span></span><br><span class="line">   <span class="keyword">public</span> Result <span class="title function_">delete</span><span class="params">(<span class="meta">@RequestParam</span> List&lt;Long&gt; ids)</span> &#123;</span><br><span class="line">       log.info(<span class="string">&quot;菜品批量删除：&#123;&#125;&quot;</span>, ids);</span><br><span class="line">       dishService.deleteBatch(ids);</span><br><span class="line"></span><br><span class="line">       <span class="comment">//将所有的菜品缓存数据清理掉，所有以dish_开头的key</span></span><br><span class="line">       cleanCache(<span class="string">&quot;dish_*&quot;</span>);</span><br><span class="line"></span><br><span class="line">       <span class="keyword">return</span> Result.success();</span><br><span class="line">   &#125;</span><br></pre></td></tr></table></figure><button type="button" class="tab-to-top" aria-label="scroll to top"><i class="fas fa-arrow-up"></i></button></div><div class="tab-item-content" id="缓存菜品代码开发-dishcontroller-3"><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><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 class="comment">    * <span class="doctag">@param</span> dishDTO</span></span><br><span class="line"><span class="comment">    * <span class="doctag">@return</span></span></span><br><span class="line"><span class="comment">    */</span></span><br><span class="line">   <span class="meta">@PutMapping</span></span><br><span class="line">   <span class="meta">@ApiOperation(&quot;修改菜品&quot;)</span></span><br><span class="line">   <span class="keyword">public</span> Result <span class="title function_">update</span><span class="params">(<span class="meta">@RequestBody</span> DishDTO dishDTO)</span> &#123;</span><br><span class="line">       log.info(<span class="string">&quot;修改菜品：&#123;&#125;&quot;</span>, dishDTO);</span><br><span class="line">       dishService.updateWithFlavor(dishDTO);</span><br><span class="line"></span><br><span class="line">       <span class="comment">//将所有的菜品缓存数据清理掉，所有以dish_开头的key</span></span><br><span class="line">       cleanCache(<span class="string">&quot;dish_*&quot;</span>);</span><br><span class="line"></span><br><span class="line">       <span class="keyword">return</span> Result.success();</span><br><span class="line">   &#125;</span><br></pre></td></tr></table></figure><button type="button" class="tab-to-top" aria-label="scroll to top"><i class="fas fa-arrow-up"></i></button></div><div class="tab-item-content" id="缓存菜品代码开发-dishcontroller-4"><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><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 class="comment">    * <span class="doctag">@param</span> status</span></span><br><span class="line"><span class="comment">    * <span class="doctag">@param</span> id</span></span><br><span class="line"><span class="comment">    * <span class="doctag">@return</span></span></span><br><span class="line"><span class="comment">    */</span></span><br><span class="line">   <span class="meta">@PostMapping(&quot;/status/&#123;status&#125;&quot;)</span></span><br><span class="line">   <span class="meta">@ApiOperation(&quot;菜品起售停售&quot;)</span></span><br><span class="line">   <span class="keyword">public</span> Result&lt;String&gt; <span class="title function_">startOrStop</span><span class="params">(<span class="meta">@PathVariable</span> Integer status, Long id)</span> &#123;</span><br><span class="line">       dishService.startOrStop(status, id);</span><br><span class="line"></span><br><span class="line">       <span class="comment">//将所有的菜品缓存数据清理掉，所有以dish_开头的key</span></span><br><span class="line">       cleanCache(<span class="string">&quot;dish_*&quot;</span>);</span><br><span class="line"></span><br><span class="line">       <span class="keyword">return</span> Result.success();</span><br><span class="line">   &#125;</span><br></pre></td></tr></table></figure><button type="button" class="tab-to-top" aria-label="scroll to top"><i class="fas fa-arrow-up"></i></button></div></div></div><h2 id="缓存套餐"><a href="#缓存套餐" class="headerlink" title="缓存套餐"></a>缓存套餐</h2><h3 id="Spring-Cache"><a href="#Spring-Cache" class="headerlink" title="Spring Cache"></a>Spring Cache</h3><h4 id="介绍-3"><a href="#介绍-3" class="headerlink" title="介绍"></a>介绍</h4><p><code>Spring Cache</code>是一个框架，实现了基于注解的缓存功能，只需要简单地加一个注解，就能实现缓存功能。</p><p>Spring Cache提供了一层抽象，底层可以切换不同的缓存实现，例如：</p><ul><li>EHCache</li><li>Caffeine</li><li>Redis(常用)</li></ul><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></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.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-cache<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><h4 id="常用注解-1"><a href="#常用注解-1" class="headerlink" title="常用注解"></a>常用注解</h4><p>在SpringCache中提供了很多缓存操作的注解，常见的是以下的几个：</p><div class="table-container"><table><thead><tr><th><strong>注解</strong></th><th><strong>说明</strong></th></tr></thead><tbody><tr><td><code>@EnableCaching</code></td><td>开启缓存注解功能，通常加在启动类上</td></tr><tr><td><code>@Cacheable</code></td><td>在方法执行前先查询缓存中是否有数据，如果有数据，则直接返回缓存数据；如果没有缓存数据，调用方法并将方法返回值放到缓存中</td></tr><tr><td><code>@CachePut</code></td><td>将方法的返回值放到缓存中</td></tr><tr><td><code>@CacheEvict</code></td><td>将一条或多条数据从缓存中删除</td></tr></tbody></table></div><p>在spring boot项目中，使用缓存技术只需在项目中导入相关缓存技术的依赖包，并在启动类上使用<code>@EnableCaching</code>开启缓存支持即可。</p><p>例如，使用Redis作为缓存技术，只需要导入Spring data Redis的maven坐标即可。</p><h4 id="入门案例-1"><a href="#入门案例-1" class="headerlink" title="入门案例"></a>入门案例</h4><ol><li><strong>环境准备</strong></li></ol><p><strong>导入基础工程:</strong>底层已使用Redis缓存实现</p><p>资料中提供了入门案例的基本环境代码，直接导入进来即可。导入进来的工程结构如下：<br><div class="img-wrap"><div class="img-bg"><img class="img" src="https://raw.githubusercontent.com/silvan2077/markdown_pic1/main/Picgo/image-20221210183942040.png"/></div></div></p><p><strong>数据库准备:</strong></p><p>创建名为spring_cache_demo数据库，将<code>springcachedemo.sql</code>脚本直接导入数据库中。</p><div class="img-wrap"><div class="img-bg"><img class="img" src="https://raw.githubusercontent.com/silvan2077/markdown_pic1/main/Picgo/image-20221210184346304.png"/></div></div><p><strong>引导类上加@EnableCaching:</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="meta">@Slf4j</span></span><br><span class="line"><span class="meta">@SpringBootApplication</span></span><br><span class="line"><span class="meta">@EnableCaching</span><span class="comment">//开启缓存注解功能</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">CacheDemoApplication</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(CacheDemoApplication.class,args);</span><br><span class="line">        log.info(<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><ol><li><strong>@CachePut注解</strong></li></ol><p><strong>@CachePut 说明：</strong> </p><ul><li>作用: 将方法返回值，放入缓存  </li><li>value: 缓存的名称, 每个缓存名称下面可以有很多key</li><li>key: 缓存的key -&gt; 支持Spring的表达式语言SPEL语法</li></ul><p><strong>在save方法上添加@CachePut注解</strong></p><p>当前UserController的save方法是用来保存用户信息的，我们希望在该用户信息保存到数据库的同时，也往缓存中缓存一份数据，我们可以在save方法上加上注解 <code>@CachePut</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></pre></td><td class="code"><pre><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment">* CachePut：将方法返回值放入缓存</span></span><br><span class="line"><span class="comment">* value：缓存的名称，每个缓存名称下面可以有多个key</span></span><br><span class="line"><span class="comment">* key：缓存的key</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="meta">@CachePut(value = &quot;userCache&quot;, key = &quot;#user.id&quot;)</span><span class="comment">//key的生成：userCache::1</span></span><br><span class="line">   <span class="keyword">public</span> User <span class="title function_">save</span><span class="params">(<span class="meta">@RequestBody</span> User user)</span>&#123;</span><br><span class="line">       userMapper.insert(user);</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>说明：</strong> key的写法如下</p><h1 id="user-id-user指的是方法形参的名称-id指的是user的id属性-也就是使用user的id属性作为key"><a href="#user-id-user指的是方法形参的名称-id指的是user的id属性-也就是使用user的id属性作为key" class="headerlink" title="user.id : #user指的是方法形参的名称, id指的是user的id属性 , 也就是使用user的id属性作为key ;"></a>user.id : #user指的是方法形参的名称, id指的是user的id属性 , 也就是使用user的id属性作为key ;</h1><h1 id="result-id-result代表方法返回值，该表达式-代表以返回对象的id属性作为key-；"><a href="#result-id-result代表方法返回值，该表达式-代表以返回对象的id属性作为key-；" class="headerlink" title="result.id : #result代表方法返回值，该表达式 代表以返回对象的id属性作为key ；"></a>result.id : #result代表方法返回值，该表达式 代表以返回对象的id属性作为key ；</h1><h1 id="p0-id：-p0指的是方法中的第一个参数，id指的是第一个参数的id属性-也就是使用第一个参数的id属性作为key"><a href="#p0-id：-p0指的是方法中的第一个参数，id指的是第一个参数的id属性-也就是使用第一个参数的id属性作为key" class="headerlink" title="p0.id：#p0指的是方法中的第一个参数，id指的是第一个参数的id属性,也就是使用第一个参数的id属性作为key ;"></a>p0.id：#p0指的是方法中的第一个参数，id指的是第一个参数的id属性,也就是使用第一个参数的id属性作为key ;</h1><h1 id="a0-id：-a0指的是方法中的第一个参数，id指的是第一个参数的id属性-也就是使用第一个参数的id属性作为key"><a href="#a0-id：-a0指的是方法中的第一个参数，id指的是第一个参数的id属性-也就是使用第一个参数的id属性作为key" class="headerlink" title="a0.id：#a0指的是方法中的第一个参数，id指的是第一个参数的id属性,也就是使用第一个参数的id属性作为key ;"></a>a0.id：#a0指的是方法中的第一个参数，id指的是第一个参数的id属性,也就是使用第一个参数的id属性作为key ;</h1><h1 id="root-args-0-id-root-args-0-指的是方法中的第一个参数，id指的是第一个参数的id属性-也就是使用第一个参数"><a href="#root-args-0-id-root-args-0-指的是方法中的第一个参数，id指的是第一个参数的id属性-也就是使用第一个参数" class="headerlink" title="root.args[0].id:#root.args[0]指的是方法中的第一个参数，id指的是第一个参数的id属性,也就是使用第一个参数"></a>root.args[0].id:#root.args[0]指的是方法中的第一个参数，id指的是第一个参数的id属性,也就是使用第一个参数</h1><p>的id属性作为key ;</p><p><strong>启动服务,通过swagger接口文档测试，访问UserController的save()方法</strong></p><p>因为id是自增，所以不需要设置id属性</p><p> <img src="assets/image-20221210191702887.png" alt="image-20221210191702887" style="zoom:50%;" /> </p><p><strong>查看user表中的数据</strong></p><p><img src="assets/image-20221210192325931.png" alt="image-20221210192325931" style="zoom: 67%;" /> </p><p><strong>查看Redis中的数据</strong></p><p><img src="assets/image-20221210192418204.png" alt="image-20221210192418204" style="zoom:50%;" /> </p><p><strong>3. @Cacheable注解</strong></p><p><strong>@Cacheable 说明:</strong></p><p>​    作用: 在方法执行前，spring先查看缓存中是否有数据，如果有数据，则直接返回缓存数据；若没有数据，调用方法并将方法返回值放到缓存中</p><p>​    value: 缓存的名称，每个缓存名称下面可以有多个key</p><p>​    key: 缓存的key  —————&gt; 支持Spring的表达式语言SPEL语法</p><p> <strong>在getById上加注解@Cacheable</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></pre></td><td class="code"><pre><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment">* Cacheable：在方法执行前spring先查看缓存中是否有数据，如果有数据，则直接返回缓存数据；若没有数据，  *调用方法并将方法返回值放到缓存中</span></span><br><span class="line"><span class="comment">* value：缓存的名称，每个缓存名称下面可以有多个key</span></span><br><span class="line"><span class="comment">* key：缓存的key</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="meta">@Cacheable(cacheNames = &quot;userCache&quot;,key=&quot;#id&quot;)</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="type">User</span> <span class="variable">user</span> <span class="operator">=</span> userMapper.getById(id);</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>重启服务,通过swagger接口文档测试，访问UserController的getById()方法</strong></p><p>第一次访问，会请求我们controller的方法，查询数据库。后面再查询相同的id，就直接从Redis中查询数据，不用再查询数据库了，就说明缓存生效了。</p><p>提前在redis中手动删除掉id=1的用户数据</p><p><img src="assets/image-20221210193834150.png" alt="image-20221210193834150" style="zoom:50%;" /> </p><p><strong>查看控制台sql语句：</strong>说明从数据库查询的用户数据</p><p><img src="assets/image-20221210193948896.png" alt="image-20221210193948896" style="zoom: 67%;" /> </p><p><strong>查看Redis中的缓存数据：</strong>说明已成功缓存</p><p><img src="assets/image-20221210194112334.png" alt="image-20221210194112334" style="zoom:50%;" /> </p><p>再次查询相同id的数据时，直接从redis中直接获取，不再查询数据库。</p><p><strong>4). @CacheEvict注解</strong></p><p><strong>@CacheEvict 说明：</strong> </p><p>​    作用: 清理指定缓存</p><p>​    value: 缓存的名称，每个缓存名称下面可以有多个key</p><p>​    key: 缓存的key  —————&gt; 支持Spring的表达式语言SPEL语法</p><p><strong>在 delete 方法上加注解@CacheEvict</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></pre></td><td class="code"><pre><span class="line"><span class="meta">@DeleteMapping</span></span><br><span class="line">   <span class="meta">@CacheEvict(cacheNames = &quot;userCache&quot;,key = &quot;#id&quot;)</span><span class="comment">//删除某个key对应的缓存数据</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="meta">@DeleteMapping(&quot;/delAll&quot;)</span></span><br><span class="line">   <span class="meta">@CacheEvict(cacheNames = &quot;userCache&quot;,allEntries = true)</span><span class="comment">//删除userCache下所有的缓存数据</span></span><br><span class="line">   <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">deleteAll</span><span class="params">()</span>&#123;</span><br><span class="line">       userMapper.deleteAll();</span><br><span class="line">   &#125;</span><br></pre></td></tr></table></figure><p><strong>重启服务,通过swagger接口文档测试，访问UserController的deleteAll()方法</strong></p><p><img src="assets/image-20221210195254874.png" alt="image-20221210195254874" style="zoom:50%;" /> </p><p><strong>查看user表：</strong>数据清空</p><p><img src="assets/image-20221210195332101.png" alt="image-20221210195332101" style="zoom:80%;" /> </p><p><strong>查询Redis缓存数据</strong></p><p><img src="assets/image-20221210195500014.png" alt="image-20221210195500014" style="zoom:50%;" /> </p><h3 id="实现思路-1"><a href="#实现思路-1" class="headerlink" title="实现思路"></a>实现思路</h3><p><strong>实现步骤：</strong></p><p>1). 导入Spring Cache和Redis相关maven坐标</p><p>2). 在启动类上加入@EnableCaching注解，开启缓存注解功能</p><p>3). 在用户端接口SetmealController的 list 方法上加入@Cacheable注解</p><p>4). 在管理端接口SetmealController的 save、delete、update、startOrStop等方法上加入CacheEvict注解</p><h3 id="代码开发-12"><a href="#代码开发-12" class="headerlink" title="代码开发"></a>代码开发</h3><p>按照上述实现步骤：</p><p><strong>1). 导入Spring Cache和Redis相关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><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="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="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-cache<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). 在启动类上加入@EnableCaching注解，开启缓存注解功能</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></pre></td><td class="code"><pre><span class="line"><span class="keyword">package</span> com.sky;</span><br><span class="line"></span><br><span class="line"><span class="keyword">import</span> lombok.extern.slf4j.Slf4j;</span><br><span class="line"><span class="keyword">import</span> org.springframework.boot.SpringApplication;</span><br><span class="line"><span class="keyword">import</span> org.springframework.boot.autoconfigure.SpringBootApplication;</span><br><span class="line"><span class="keyword">import</span> org.springframework.cache.annotation.EnableCaching;</span><br><span class="line"><span class="keyword">import</span> org.springframework.transaction.annotation.EnableTransactionManagement;</span><br><span class="line"></span><br><span class="line"><span class="meta">@SpringBootApplication</span></span><br><span class="line"><span class="meta">@EnableTransactionManagement</span> <span class="comment">//开启注解方式的事务管理</span></span><br><span class="line"><span class="meta">@Slf4j</span></span><br><span class="line"><span class="meta">@EnableCaching</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">SkyApplication</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(SkyApplication.class, args);</span><br><span class="line">        log.info(<span class="string">&quot;server started&quot;</span>);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p><strong>3). 在用户端接口SetmealController的 list 方法上加入@Cacheable注解</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="comment">    * 条件查询</span></span><br><span class="line"><span class="comment">    *</span></span><br><span class="line"><span class="comment">    * <span class="doctag">@param</span> categoryId</span></span><br><span class="line"><span class="comment">    * <span class="doctag">@return</span></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="meta">@ApiOperation(&quot;根据分类id查询套餐&quot;)</span></span><br><span class="line">   <span class="meta">@Cacheable(cacheNames = &quot;setmealCache&quot;,key = &quot;#categoryId&quot;)</span> <span class="comment">//key: setmealCache::100</span></span><br><span class="line">   <span class="keyword">public</span> Result&lt;List&lt;Setmeal&gt;&gt; <span class="title function_">list</span><span class="params">(Long categoryId)</span> &#123;</span><br><span class="line">       <span class="type">Setmeal</span> <span class="variable">setmeal</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">Setmeal</span>();</span><br><span class="line">       setmeal.setCategoryId(categoryId);</span><br><span class="line">       setmeal.setStatus(StatusConstant.ENABLE);</span><br><span class="line"></span><br><span class="line">       List&lt;Setmeal&gt; list = setmealService.list(setmeal);</span><br><span class="line">       <span class="keyword">return</span> Result.success(list);</span><br><span class="line">   &#125;</span><br></pre></td></tr></table></figure><p><strong>4). 在管理端接口SetmealController的 save、delete、update、startOrStop等方法上加入CacheEvict注解</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></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">@param</span> setmealDTO</span></span><br><span class="line"><span class="comment">    * <span class="doctag">@return</span></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="meta">@ApiOperation(&quot;新增套餐&quot;)</span></span><br><span class="line">   <span class="meta">@CacheEvict(cacheNames = &quot;setmealCache&quot;,key = &quot;#setmealDTO.categoryId&quot;)</span><span class="comment">//key: setmealCache::100</span></span><br><span class="line">   <span class="keyword">public</span> Result <span class="title function_">save</span><span class="params">(<span class="meta">@RequestBody</span> SetmealDTO setmealDTO)</span> &#123;</span><br><span class="line">       setmealService.saveWithDish(setmealDTO);</span><br><span class="line">       <span class="keyword">return</span> Result.success();</span><br><span class="line">   &#125;</span><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">@param</span> ids</span></span><br><span class="line"><span class="comment">    * <span class="doctag">@return</span></span></span><br><span class="line"><span class="comment">    */</span></span><br><span class="line">   <span class="meta">@DeleteMapping</span></span><br><span class="line">   <span class="meta">@ApiOperation(&quot;批量删除套餐&quot;)</span></span><br><span class="line">   <span class="meta">@CacheEvict(cacheNames = &quot;setmealCache&quot;,allEntries = true)</span></span><br><span class="line">   <span class="keyword">public</span> Result <span class="title function_">delete</span><span class="params">(<span class="meta">@RequestParam</span> List&lt;Long&gt; ids)</span> &#123;</span><br><span class="line">       setmealService.deleteBatch(ids);</span><br><span class="line">       <span class="keyword">return</span> Result.success();</span><br><span class="line">   &#125;</span><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">@param</span> setmealDTO</span></span><br><span class="line"><span class="comment">    * <span class="doctag">@return</span></span></span><br><span class="line"><span class="comment">    */</span></span><br><span class="line">   <span class="meta">@PutMapping</span></span><br><span class="line">   <span class="meta">@ApiOperation(&quot;修改套餐&quot;)</span></span><br><span class="line">   <span class="meta">@CacheEvict(cacheNames = &quot;setmealCache&quot;,allEntries = true)</span></span><br><span class="line">   <span class="keyword">public</span> Result <span class="title function_">update</span><span class="params">(<span class="meta">@RequestBody</span> SetmealDTO setmealDTO)</span> &#123;</span><br><span class="line">       setmealService.update(setmealDTO);</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">    * 套餐起售停售</span></span><br><span class="line"><span class="comment">    *</span></span><br><span class="line"><span class="comment">    * <span class="doctag">@param</span> status</span></span><br><span class="line"><span class="comment">    * <span class="doctag">@param</span> id</span></span><br><span class="line"><span class="comment">    * <span class="doctag">@return</span></span></span><br><span class="line"><span class="comment">    */</span></span><br><span class="line">   <span class="meta">@PostMapping(&quot;/status/&#123;status&#125;&quot;)</span></span><br><span class="line">   <span class="meta">@ApiOperation(&quot;套餐起售停售&quot;)</span></span><br><span class="line">   <span class="meta">@CacheEvict(cacheNames = &quot;setmealCache&quot;,allEntries = true)</span></span><br><span class="line">   <span class="keyword">public</span> Result <span class="title function_">startOrStop</span><span class="params">(<span class="meta">@PathVariable</span> Integer status, Long id)</span> &#123;</span><br><span class="line">       setmealService.startOrStop(status, id);</span><br><span class="line">       <span class="keyword">return</span> Result.success();</span><br><span class="line">   &#125;</span><br></pre></td></tr></table></figure><h3 id="2-4-功能测试"><a href="#2-4-功能测试" class="headerlink" title="2.4 功能测试"></a>2.4 功能测试</h3><p>通过前后端联调方式来进行测试，同时观察redis中缓存的套餐数据。和<strong>缓存菜品</strong>功能测试基本一致，不再赘述。</p><h3 id="2-5-代码提交"><a href="#2-5-代码提交" class="headerlink" title="2.5 代码提交"></a>2.5 代码提交</h3><p><img src="assets/image-20221210203035708.png" alt="image-20221210203035708" style="zoom:50%;" /> </p><p>后续步骤和其它功能代码提交一致，不再赘述。</p><h2 id="添加购物车"><a href="#添加购物车" class="headerlink" title="添加购物车"></a>添加购物车</h2><h3 id="需求分析和设计-17"><a href="#需求分析和设计-17" class="headerlink" title="需求分析和设计"></a>需求分析和设计</h3><h4 id="产品原型-1"><a href="#产品原型-1" class="headerlink" title="产品原型"></a>产品原型</h4><p>用户可以将菜品或者套餐添加到购物车。对于菜品来说，如果设置了口味信息，则需要选择规格后才能加入购物车;对于套餐来说，可以直接点击<code>+</code>将当前套餐加入购物车。在购物车中可以修改菜品和套餐的数量，也可以清空购物车。</p><div class="img-wrap"><div class="img-bg"><img class="img" src="https://raw.githubusercontent.com/silvan2077/markdown_pic1/main/Picgo/image-20221210203822817.png"/></div></div><h4 id="接口设计-4"><a href="#接口设计-4" class="headerlink" title="接口设计"></a>接口设计</h4><p>设计出对应的添加购物车接口。</p><div class="img-wrap"><div class="img-bg"><img class="img" src="https://raw.githubusercontent.com/silvan2077/markdown_pic1/main/Picgo/image-20221208184342490.png"/></div></div><div class="img-wrap"><div class="img-bg"><img class="img" src="https://raw.githubusercontent.com/silvan2077/markdown_pic1/main/Picgo/image-20221208184354291.png"/></div></div><div class="note info no-icon flat"><p><strong>说明：</strong> 添加购物车时，有可能添加菜品，也有可能添加套餐。故传入参数要么是菜品id，要么是套餐id。</p></div><h4 id="表设计-4"><a href="#表设计-4" class="headerlink" title="表设计"></a>表设计</h4><p>购物车对应的数据表为shopping_cart表，具体表结构如下：</p><div class="table-container"><table><thead><tr><th><strong>字段名</strong></th><th><strong>数据类型</strong></th><th><strong>说明</strong></th><th><strong>备注</strong></th></tr></thead><tbody><tr><td>id</td><td>bigint</td><td>主键</td><td>自增</td></tr><tr><td>name</td><td>varchar(32)</td><td>商品名称</td><td>冗余字段</td></tr><tr><td>image</td><td>varchar(255)</td><td>商品图片路径</td><td>冗余字段</td></tr><tr><td>user_id</td><td>bigint</td><td>用户id</td><td>逻辑外键</td></tr><tr><td>dish_id</td><td>bigint</td><td>菜品id</td><td>逻辑外键</td></tr><tr><td>setmeal_id</td><td>bigint</td><td>套餐id</td><td>逻辑外键</td></tr><tr><td>dish_flavor</td><td>varchar(50)</td><td>菜品口味</td><td></td></tr><tr><td>number</td><td>int</td><td>商品数量</td><td></td></tr><tr><td>amount</td><td>decimal(10,2)</td><td>商品单价</td><td>冗余字段</td></tr><tr><td>create_time</td><td>datetime</td><td>创建时间</td></tr></tbody></table></div><div class="note info no-icon flat"><p><strong>说明：</strong> </p><ul><li>购物车数据是关联用户的，在表结构中，我们需要记录，每一个用户的购物车数据是哪些</li><li>菜品列表展示出来的既有套餐，又有菜品，如果用户选择的是套餐，就保存套餐ID(setmeal_id)，如果用户选择的是菜品，就保存菜品ID(dish_id)</li><li>对同一个菜品/套餐，如果选择多份不需要添加多条记录，增加数量number即可</li></ul></div><h3 id="代码开发-13"><a href="#代码开发-13" class="headerlink" title="代码开发"></a>代码开发</h3><h4 id="DTO设计-1"><a href="#DTO设计-1" class="headerlink" title="DTO设计"></a>DTO设计</h4><p><strong>根据添加购物车接口的参数设计DTO：</strong></p><div class="img-wrap"><div class="img-bg"><img class="img" src="https://raw.githubusercontent.com/silvan2077/markdown_pic1/main/Picgo/image-20221208184938195.png"/></div></div><p>在sky-pojo模块中已经提供了ShoppingCartDTO</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">@Data</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">ShoppingCartDTO</span> <span class="keyword">implements</span> <span class="title class_">Serializable</span> &#123;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">private</span> Long dishId;</span><br><span class="line">    <span class="keyword">private</span> Long setmealId;</span><br><span class="line">    <span class="keyword">private</span> String dishFlavor;</span><br><span class="line"></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h4 id="Controller层-11"><a href="#Controller层-11" class="headerlink" title="Controller层"></a>Controller层</h4><p><strong>根据添加购物车接口创建ShoppingCartController：</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"> * 购物车</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/shoppingCart&quot;)</span></span><br><span class="line"><span class="meta">@Slf4j</span></span><br><span class="line"><span class="meta">@Api(tags = &quot;C端-购物车接口&quot;)</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">ShoppingCartController</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> ShoppingCartService shoppingCartService;</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 class="doctag">@param</span> shoppingCartDTO</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@return</span></span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    <span class="meta">@PostMapping(&quot;/add&quot;)</span></span><br><span class="line">    <span class="meta">@ApiOperation(&quot;添加购物车&quot;)</span></span><br><span class="line">    <span class="keyword">public</span> Result&lt;String&gt; <span class="title function_">add</span><span class="params">(<span class="meta">@RequestBody</span> ShoppingCartDTO shoppingCartDTO)</span>&#123;</span><br><span class="line">        log.info(<span class="string">&quot;添加购物车：&#123;&#125;&quot;</span>, shoppingCartDTO);</span><br><span class="line">        shoppingCartService.addShoppingCart(shoppingCartDTO);<span class="comment">//后绪步骤实现</span></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><h4 id="Service层-8"><a href="#Service层-8" class="headerlink" title="Service层"></a>Service层</h4><p><strong>创建ShoppingCartService接口：</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="keyword">public</span> <span class="keyword">interface</span> <span class="title class_">ShoppingCartService</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 class="doctag">@param</span> shoppingCartDTO</span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    <span class="keyword">void</span> <span class="title function_">addShoppingCart</span><span class="params">(ShoppingCartDTO shoppingCartDTO)</span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p><strong>创建ShoppingCartServiceImpl实现类，并实现add方法：</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></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_">ShoppingCartServiceImpl</span> <span class="keyword">implements</span> <span class="title class_">ShoppingCartService</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> ShoppingCartMapper shoppingCartMapper;</span><br><span class="line">    <span class="meta">@Autowired</span></span><br><span class="line">    <span class="keyword">private</span> DishMapper dishMapper;</span><br><span class="line">    <span class="meta">@Autowired</span></span><br><span class="line">    <span class="keyword">private</span> SetmealMapper setmealMapper;</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">@param</span> shoppingCartDTO</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_">addShoppingCart</span><span class="params">(ShoppingCartDTO shoppingCartDTO)</span> &#123;</span><br><span class="line">        <span class="type">ShoppingCart</span> <span class="variable">shoppingCart</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">ShoppingCart</span>();</span><br><span class="line">        BeanUtils.copyProperties(shoppingCartDTO, shoppingCart);</span><br><span class="line">        <span class="comment">//只能查询自己的购物车数据</span></span><br><span class="line">        shoppingCart.setUserId(BaseContext.getCurrentId());</span><br><span class="line"></span><br><span class="line">        <span class="comment">//判断当前商品是否在购物车中</span></span><br><span class="line">        List&lt;ShoppingCart&gt; shoppingCartList = shoppingCartMapper.list(shoppingCart);</span><br><span class="line"></span><br><span class="line">        <span class="keyword">if</span> (shoppingCartList != <span class="literal">null</span> &amp;&amp; shoppingCartList.size() == <span class="number">1</span>) &#123;</span><br><span class="line">            <span class="comment">//如果已经存在，就更新数量，数量加1</span></span><br><span class="line">            shoppingCart = shoppingCartList.get(<span class="number">0</span>);</span><br><span class="line">            shoppingCart.setNumber(shoppingCart.getNumber() + <span class="number">1</span>);</span><br><span class="line">            shoppingCartMapper.updateNumberById(shoppingCart);</span><br><span class="line">        &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">            <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">Long</span> <span class="variable">dishId</span> <span class="operator">=</span> shoppingCartDTO.getDishId();</span><br><span class="line">            <span class="keyword">if</span> (dishId != <span class="literal">null</span>) &#123;</span><br><span class="line">                <span class="comment">//添加到购物车的是菜品</span></span><br><span class="line">                <span class="type">Dish</span> <span class="variable">dish</span> <span class="operator">=</span> dishMapper.getById(dishId);</span><br><span class="line">                shoppingCart.setName(dish.getName());</span><br><span class="line">                shoppingCart.setImage(dish.getImage());</span><br><span class="line">                shoppingCart.setAmount(dish.getPrice());</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="type">Setmeal</span> <span class="variable">setmeal</span> <span class="operator">=</span> setmealMapper.getById(shoppingCartDTO.getSetmealId());</span><br><span class="line">                shoppingCart.setName(setmeal.getName());</span><br><span class="line">                shoppingCart.setImage(setmeal.getImage());</span><br><span class="line">                shoppingCart.setAmount(setmeal.getPrice());</span><br><span class="line">            &#125;</span><br><span class="line">            shoppingCart.setNumber(<span class="number">1</span>);</span><br><span class="line">            shoppingCart.setCreateTime(LocalDateTime.now());</span><br><span class="line">            shoppingCartMapper.insert(shoppingCart);</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="Mapper层-11"><a href="#Mapper层-11" class="headerlink" title="Mapper层"></a>Mapper层</h4><p><strong>创建ShoppingCartMapper接口:</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></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_">ShoppingCartMapper</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"><span class="comment">     *</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@param</span> shoppingCart</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@return</span></span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    List&lt;ShoppingCart&gt; <span class="title function_">list</span><span class="params">(ShoppingCart shoppingCart)</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="comment">     * <span class="doctag">@param</span> shoppingCart</span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    <span class="meta">@Update(&quot;update shopping_cart set number = #&#123;number&#125; where id = #&#123;id&#125;&quot;)</span></span><br><span class="line">    <span class="keyword">void</span> <span class="title function_">updateNumberById</span><span class="params">(ShoppingCart shoppingCart)</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="comment">     * <span class="doctag">@param</span> shoppingCart</span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    <span class="meta">@Insert(&quot;insert into shopping_cart (name, user_id, dish_id, setmeal_id, dish_flavor, number, amount, image, create_time) &quot; +</span></span><br><span class="line"><span class="meta">            &quot; values (#&#123;name&#125;,#&#123;userId&#125;,#&#123;dishId&#125;,#&#123;setmealId&#125;,#&#123;dishFlavor&#125;,#&#123;number&#125;,#&#123;amount&#125;,#&#123;image&#125;,#&#123;createTime&#125;)&quot;)</span></span><br><span class="line">    <span class="keyword">void</span> <span class="title function_">insert</span><span class="params">(ShoppingCart shoppingCart)</span>;</span><br><span class="line"></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p><strong>创建ShoppingCartMapper.xml：</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><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><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="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 class="string">&quot;http://mybatis.org/dtd/mybatis-3-mapper.dtd&quot;</span> &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.sky.mapper.ShoppingCartMapper&quot;</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;list&quot;</span> <span class="attr">parameterType</span>=<span class="string">&quot;ShoppingCart&quot;</span> <span class="attr">resultType</span>=<span class="string">&quot;ShoppingCart&quot;</span>&gt;</span></span><br><span class="line">        select * from shopping_cart</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;userId != null&quot;</span>&gt;</span></span><br><span class="line">                and user_id = #&#123;userId&#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;dishId != null&quot;</span>&gt;</span></span><br><span class="line">                and dish_id = #&#123;dishId&#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;setmealId != null&quot;</span>&gt;</span></span><br><span class="line">                and setmeal_id = #&#123;setmealId&#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;dishFlavor != null&quot;</span>&gt;</span></span><br><span class="line">                and dish_flavor = #&#123;dishFlavor&#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 create_time desc</span><br><span class="line">    <span class="tag">&lt;/<span class="name">select</span>&gt;</span></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="功能测试-14"><a href="#功能测试-14" class="headerlink" title="功能测试"></a>功能测试</h3><p>在小程序端点击添加菜品，添加菜品后，因为当前还没有实现查看购物车功能，因此可以去数据库的购物车表查看新填的数据<br><div class="img-wrap"><div class="img-bg"><img class="img" src="https://raw.githubusercontent.com/silvan2077/markdown_pic1/main/Picgo/20251116145208.png"/></div></div></p><div class="note warning no-icon flat"><p>如果java程序显示插入一条语句，但是数据库表中没有数据，可能是因为前面微信登录处拦截器包导入错误，导致拦截器不生效而没有解析出user_id,插入字段为null导致的插入失败</p></div><h2 id="查看购物车"><a href="#查看购物车" class="headerlink" title="查看购物车"></a>查看购物车</h2><h3 id="需求分析和设计-18"><a href="#需求分析和设计-18" class="headerlink" title="需求分析和设计"></a>需求分析和设计</h3><h4 id="产品原型-2"><a href="#产品原型-2" class="headerlink" title="产品原型"></a>产品原型</h4><p>当用户添加完菜品和套餐后，可进入到购物车中，查看购物中的菜品和套餐。<br><div class="img-wrap"><div class="img-bg"><img class="img" src="https://raw.githubusercontent.com/silvan2077/markdown_pic1/main/Picgo/image-20221208190038058.png"/></div></div></p><h4 id="接口设计-5"><a href="#接口设计-5" class="headerlink" title="接口设计"></a>接口设计</h4><div class="img-wrap"><div class="img-bg"><img class="img" src="https://raw.githubusercontent.com/silvan2077/markdown_pic1/main/Picgo/image-20221208190102904.png"/></div></div> <div class="img-wrap"><div class="img-bg"><img class="img" src="https://raw.githubusercontent.com/silvan2077/markdown_pic1/main/Picgo/image-20221208190052467.png"/></div></div><h3 id="代码开发-14"><a href="#代码开发-14" class="headerlink" title="代码开发"></a>代码开发</h3><h4 id="Controller层-12"><a href="#Controller层-12" class="headerlink" title="Controller层"></a>Controller层</h4><p><strong>在ShoppingCartController中创建查看购物车的方法：</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">    * 查看购物车</span></span><br><span class="line"><span class="comment">    * <span class="doctag">@return</span></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="meta">@ApiOperation(&quot;查看购物车&quot;)</span></span><br><span class="line">   <span class="keyword">public</span> Result&lt;List&lt;ShoppingCart&gt;&gt; <span class="title function_">list</span><span class="params">()</span>&#123;</span><br><span class="line">       <span class="keyword">return</span> Result.success(shoppingCartService.showShoppingCart());</span><br><span class="line">   &#125;</span><br></pre></td></tr></table></figure><h4 id="Service层-9"><a href="#Service层-9" class="headerlink" title="Service层"></a>Service层</h4><p><strong>在ShoppingCartService接口中声明查看购物车的方法：</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></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">@return</span></span></span><br><span class="line"><span class="comment">    */</span></span><br><span class="line">   List&lt;ShoppingCart&gt; <span class="title function_">showShoppingCart</span><span class="params">()</span>;</span><br></pre></td></tr></table></figure><p><strong>在ShoppingCartServiceImpl中实现查看购物车的方法：</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="comment">/**</span></span><br><span class="line"><span class="comment">    * 查看购物车</span></span><br><span class="line"><span class="comment">    * <span class="doctag">@return</span></span></span><br><span class="line"><span class="comment">    */</span></span><br><span class="line">   <span class="keyword">public</span> List&lt;ShoppingCart&gt; <span class="title function_">showShoppingCart</span><span class="params">()</span> &#123;</span><br><span class="line">       <span class="keyword">return</span> shoppingCartMapper.list(ShoppingCart.</span><br><span class="line">                   builder().</span><br><span class="line">                   userId(BaseContext.getCurrentId()).</span><br><span class="line">                   build());</span><br><span class="line">   &#125;</span><br></pre></td></tr></table></figure><h2 id="清空购物车"><a href="#清空购物车" class="headerlink" title="清空购物车"></a>清空购物车</h2><h3 id="需求分析和设计-19"><a href="#需求分析和设计-19" class="headerlink" title="需求分析和设计"></a>需求分析和设计</h3><p>点击清空按钮时，会把购物车中的数据全部清空。</p><div class="img-wrap"><div class="img-bg"><img class="img" src="https://raw.githubusercontent.com/silvan2077/markdown_pic1/main/Picgo/image-20221210213703715.png"/></div></div><p><strong>接口设计</strong></p><div class="img-wrap"><div class="img-bg"><img class="img" src="https://raw.githubusercontent.com/silvan2077/markdown_pic1/main/Picgo/image-20221208191606894.png"/></div></div><h3 id="代码开发-15"><a href="#代码开发-15" class="headerlink" title="代码开发"></a>代码开发</h3><h4 id="Controller层-13"><a href="#Controller层-13" class="headerlink" title="Controller层"></a>Controller层</h4><p><strong>在ShoppingCartController中创建清空购物车的方法：</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="comment">/**</span></span><br><span class="line"><span class="comment">    * 清空购物车商品</span></span><br><span class="line"><span class="comment">    * <span class="doctag">@return</span></span></span><br><span class="line"><span class="comment">    */</span></span><br><span class="line">   <span class="meta">@DeleteMapping(&quot;/clean&quot;)</span></span><br><span class="line">   <span class="meta">@ApiOperation(&quot;清空购物车商品&quot;)</span></span><br><span class="line">   <span class="keyword">public</span> Result&lt;String&gt; <span class="title function_">clean</span><span class="params">()</span>&#123;</span><br><span class="line">       shoppingCartService.cleanShoppingCart();</span><br><span class="line">       <span class="keyword">return</span> Result.success();</span><br><span class="line">   &#125;</span><br></pre></td></tr></table></figure><h4 id="Service层-10"><a href="#Service层-10" class="headerlink" title="Service层"></a>Service层</h4><p><strong>在ShoppingCartService接口中声明清空购物车的方法：</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></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">void</span> <span class="title function_">cleanShoppingCart</span><span class="params">()</span>;</span><br></pre></td></tr></table></figure><p><strong>在ShoppingCartServiceImpl中实现清空购物车的方法：</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></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_">cleanShoppingCart</span><span class="params">()</span> &#123;</span><br><span class="line">       shoppingCartMapper.deleteByUserId(BaseContext.getCurrentId());</span><br><span class="line">   &#125;</span><br></pre></td></tr></table></figure><h4 id="Mapper层-12"><a href="#Mapper层-12" class="headerlink" title="Mapper层"></a>Mapper层</h4><p><strong>在ShoppingCartMapper接口中创建删除购物车数据的方法：</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">/**</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="comment">    * <span class="doctag">@param</span> userId</span></span><br><span class="line"><span class="comment">    */</span></span><br><span class="line">   <span class="meta">@Delete(&quot;delete from shopping_cart where user_id = #&#123;userId&#125;&quot;)</span></span><br><span class="line">   <span class="keyword">void</span> <span class="title function_">deleteByUserId</span><span class="params">(Long userId)</span>;</span><br></pre></td></tr></table></figure><h3 id="功能测试-15"><a href="#功能测试-15" class="headerlink" title="功能测试"></a>功能测试</h3><p>点击清空</p><div class="img-wrap"><div class="img-bg"><img class="img" src="https://raw.githubusercontent.com/silvan2077/markdown_pic1/main/Picgo/image-20221210214914092.png"/></div></div><p>查看数据库中的数据</p><div class="img-wrap"><div class="img-bg"><img class="img" src="https://raw.githubusercontent.com/silvan2077/markdown_pic1/main/Picgo/image-20221210214950261.png"/></div></div><p>说明当前用户的购物车数据已全部删除。</p><h1 id="day08"><a href="#day08" class="headerlink" title="day08"></a>day08</h1><h2 id="课程内容-4"><a href="#课程内容-4" class="headerlink" title="课程内容"></a>课程内容</h2><ul><li>导入地址簿功能代码</li><li>用户下单</li><li>订单支付</li></ul><p>功能实现：<strong>用户下单</strong>、<strong>订单支付</strong><br><strong>用户下单效果图：</strong></p><div class="img-wrap"><div class="img-bg"><img class="img" src="https://raw.githubusercontent.com/silvan2077/markdown_pic1/main/Picgo/20251116154416.png"/></div></div><h2 id="导入地址簿功能代码"><a href="#导入地址簿功能代码" class="headerlink" title="导入地址簿功能代码"></a>导入地址簿功能代码</h2><h3 id="需求分析和设计-20"><a href="#需求分析和设计-20" class="headerlink" title="需求分析和设计"></a>需求分析和设计</h3><h4 id="产品原型-3"><a href="#产品原型-3" class="headerlink" title="产品原型"></a>产品原型</h4><p>地址簿，指的是消费者用户的地址信息，用户登录成功后可以维护自己的地址信息。同一个用户可以有多个地址信息，但是只能有一个<strong>默认地址</strong>。</p><p><strong>效果图：</strong></p><div class="img-wrap"><div class="img-bg"><img class="img" src="https://raw.githubusercontent.com/silvan2077/markdown_pic1/main/Picgo/20251116154625.png"/></div></div><p>对于地址簿管理，需要实现以下几个功能： </p><ul><li>查询地址列表</li><li>新增地址</li><li>修改地址</li><li>删除地址</li><li>设置默认地址</li><li>查询默认地址</li></ul><h4 id="接口设计-6"><a href="#接口设计-6" class="headerlink" title="接口设计"></a>接口设计</h4><p>根据上述原型图先 <strong>粗粒度</strong> 设计接口，共包含7个接口。</p><p><strong>接口设计：</strong></p><ul><li>新增地址</li><li>查询登录用户所有地址</li><li>查询默认地址</li><li>根据id修改地址</li><li>根据id删除地址</li><li>根据id查询地址</li><li>设置默认地址</li></ul><p>接下来<strong>细粒度</strong> 分析每个接口，明确每个接口的请求方式、请求路径、传入参数和返回值。</p><div class="tabs" id="导入地址簿功能代码-接口设计"><ul class="nav-tabs"><li class="tab active"><button type="button" data-href="#导入地址簿功能代码-接口设计-1">新增地址</button></li><li class="tab"><button type="button" data-href="#导入地址簿功能代码-接口设计-2">查询登录用户所有地址</button></li><li class="tab"><button type="button" data-href="#导入地址簿功能代码-接口设计-3">查询默认地址</button></li><li class="tab"><button type="button" data-href="#导入地址簿功能代码-接口设计-4">修改地址</button></li><li class="tab"><button type="button" data-href="#导入地址簿功能代码-接口设计-5">根据id删除地址</button></li><li class="tab"><button type="button" data-href="#导入地址簿功能代码-接口设计-6">根据id查询地址</button></li><li class="tab"><button type="button" data-href="#导入地址簿功能代码-接口设计-7">设置默认地址</button></li></ul><div class="tab-contents"><div class="tab-item-content active" id="导入地址簿功能代码-接口设计-1"><div class="img-wrap"><div class="img-bg"><img class="img" src="https://raw.githubusercontent.com/silvan2077/markdown_pic1/main/Picgo/image-20221214185000982.png"/></div></div><div class="img-wrap"><div class="img-bg"><img class="img" src="https://raw.githubusercontent.com/silvan2077/markdown_pic1/main/Picgo/image-20221214185013172.png"/></div></div><div class="img-wrap"><div class="img-bg"><img class="img" src="https://raw.githubusercontent.com/silvan2077/markdown_pic1/main/Picgo/image-20221214185028575.png"/></div></div><button type="button" class="tab-to-top" aria-label="scroll to top"><i class="fas fa-arrow-up"></i></button></div><div class="tab-item-content" id="导入地址簿功能代码-接口设计-2"><div class="img-wrap"><div class="img-bg"><img class="img" src="https://raw.githubusercontent.com/silvan2077/markdown_pic1/main/Picgo/image-20221214185112802.png"/></div></div><div class="img-wrap"><div class="img-bg"><img class="img" src="https://raw.githubusercontent.com/silvan2077/markdown_pic1/main/Picgo/image-20221214185124296.png"/></div></div><button type="button" class="tab-to-top" aria-label="scroll to top"><i class="fas fa-arrow-up"></i></button></div><div class="tab-item-content" id="导入地址簿功能代码-接口设计-3"><div class="img-wrap"><div class="img-bg"><img class="img" src="https://raw.githubusercontent.com/silvan2077/markdown_pic1/main/Picgo/image-20221214185209235.png"/></div></div><div class="img-wrap"><div class="img-bg"><img class="img" src="https://raw.githubusercontent.com/silvan2077/markdown_pic1/main/Picgo/image-20221214185221421.png"/></div></div><button type="button" class="tab-to-top" aria-label="scroll to top"><i class="fas fa-arrow-up"></i></button></div><div class="tab-item-content" id="导入地址簿功能代码-接口设计-4"><div class="img-wrap"><div class="img-bg"><img class="img" src="https://raw.githubusercontent.com/silvan2077/markdown_pic1/main/Picgo/image-20221214185309798.png"/></div></div><div class="img-wrap"><div class="img-bg"><img class="img" src="https://raw.githubusercontent.com/silvan2077/markdown_pic1/main/Picgo/image-20221214185321203.png"/></div></div><div class="img-wrap"><div class="img-bg"><img class="img" src="https://raw.githubusercontent.com/silvan2077/markdown_pic1/main/Picgo/image-20221214185334356.png"/></div></div><button type="button" class="tab-to-top" aria-label="scroll to top"><i class="fas fa-arrow-up"></i></button></div><div class="tab-item-content" id="导入地址簿功能代码-接口设计-5"><div class="img-wrap"><div class="img-bg"><img class="img" src="https://raw.githubusercontent.com/silvan2077/markdown_pic1/main/Picgo/image-20221214185443519.png"/></div></div><button type="button" class="tab-to-top" aria-label="scroll to top"><i class="fas fa-arrow-up"></i></button></div><div class="tab-item-content" id="导入地址簿功能代码-接口设计-6"><div class="img-wrap"><div class="img-bg"><img class="img" src="https://raw.githubusercontent.com/silvan2077/markdown_pic1/main/Picgo/image-20221214185534450.png"/></div></div><div class="img-wrap"><div class="img-bg"><img class="img" src="https://raw.githubusercontent.com/silvan2077/markdown_pic1/main/Picgo/image-20221214185545692.png"/></div></div><button type="button" class="tab-to-top" aria-label="scroll to top"><i class="fas fa-arrow-up"></i></button></div><div class="tab-item-content" id="导入地址簿功能代码-接口设计-7"><div class="img-wrap"><div class="img-bg"><img class="img" src="https://raw.githubusercontent.com/silvan2077/markdown_pic1/main/Picgo/image-20221214185631755.png"/></div></div><div class="img-wrap"><div class="img-bg"><img class="img" src="https://raw.githubusercontent.com/silvan2077/markdown_pic1/main/Picgo/image-20221214185622092.png"/></div></div><button type="button" class="tab-to-top" aria-label="scroll to top"><i class="fas fa-arrow-up"></i></button></div></div></div><h4 id="表设计-5"><a href="#表设计-5" class="headerlink" title="表设计"></a>表设计</h4><p>用户的地址信息会存储在address_book表，即地址簿表中。具体表结构如下：</p><div class="table-container"><table><thead><tr><th><strong>字段名</strong></th><th><strong>数据类型</strong></th><th><strong>说明</strong></th><th><strong>备注</strong></th></tr></thead><tbody><tr><td>id</td><td>bigint</td><td>主键</td><td>自增</td></tr><tr><td>user_id</td><td>bigint</td><td>用户id</td><td>逻辑外键</td></tr><tr><td>consignee</td><td>varchar(50)</td><td>收货人</td><td></td></tr><tr><td>sex</td><td>varchar(2)</td><td>性别</td><td></td></tr><tr><td>phone</td><td>varchar(11)</td><td>手机号</td><td></td></tr><tr><td>province_code</td><td>varchar(12)</td><td>省份编码</td><td></td></tr><tr><td>province_name</td><td>varchar(32)</td><td>省份名称</td><td></td></tr><tr><td>city_code</td><td>varchar(12)</td><td>城市编码</td><td></td></tr><tr><td>city_name</td><td>varchar(32)</td><td>城市名称</td><td></td></tr><tr><td>district_code</td><td>varchar(12)</td><td>区县编码</td><td></td></tr><tr><td>district_name</td><td>varchar(32)</td><td>区县名称</td><td></td></tr><tr><td>detail</td><td>varchar(200)</td><td>详细地址信息</td><td>具体到门牌号</td></tr><tr><td>label</td><td>varchar(100)</td><td>标签</td><td>公司、家、学校</td></tr><tr><td>is_default</td><td>tinyint(1)</td><td>是否默认地址</td><td>1是 0否</td></tr></tbody></table></div><p>这里面有一个字段<code>is_default</code>，实际上在设置默认地址时，只需要更新这个字段就可以了。</p><h3 id="代码导入-2"><a href="#代码导入-2" class="headerlink" title="代码导入"></a>代码导入</h3><p>对于这一类的单表增删改查，基本的开发思路都是一样的，本小节的用户地址簿管理的增删改查功能就不再一一实现了，直接导入资料中提供的代码即可<br><div class="img-wrap"><div class="img-bg"><img class="img" src="https://raw.githubusercontent.com/silvan2077/markdown_pic1/main/Picgo/image-20221214190135741.png"/></div></div></p><h4 id="Mapper层-13"><a href="#Mapper层-13" class="headerlink" title="Mapper层"></a>Mapper层</h4><p><strong>创建AddressBookMapper.java</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></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_">AddressBookMapper</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 class="doctag">@param</span> addressBook</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@return</span></span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    List&lt;AddressBook&gt; <span class="title function_">list</span><span class="params">(AddressBook addressBook)</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 class="doctag">@param</span> addressBook</span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    <span class="meta">@Insert(&quot;insert into address_book&quot; +</span></span><br><span class="line"><span class="meta">            &quot;        (user_id, consignee, phone, sex, province_code, province_name, city_code, city_name, district_code,&quot; +</span></span><br><span class="line"><span class="meta">            &quot;         district_name, detail, label, is_default)&quot; +</span></span><br><span class="line"><span class="meta">            &quot;        values (#&#123;userId&#125;, #&#123;consignee&#125;, #&#123;phone&#125;, #&#123;sex&#125;, #&#123;provinceCode&#125;, #&#123;provinceName&#125;, #&#123;cityCode&#125;, #&#123;cityName&#125;,&quot; +</span></span><br><span class="line"><span class="meta">            &quot;                #&#123;districtCode&#125;, #&#123;districtName&#125;, #&#123;detail&#125;, #&#123;label&#125;, #&#123;isDefault&#125;)&quot;)</span></span><br><span class="line">    <span class="keyword">void</span> <span class="title function_">insert</span><span class="params">(AddressBook addressBook)</span>;</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 class="doctag">@param</span> id</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@return</span></span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    <span class="meta">@Select(&quot;select * from address_book where id = #&#123;id&#125;&quot;)</span></span><br><span class="line">    AddressBook <span class="title function_">getById</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">     * 根据id修改</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@param</span> addressBook</span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    <span class="keyword">void</span> <span class="title function_">update</span><span class="params">(AddressBook addressBook)</span>;</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 class="doctag">@param</span> addressBook</span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    <span class="meta">@Update(&quot;update address_book set is_default = #&#123;isDefault&#125; where user_id = #&#123;userId&#125;&quot;)</span></span><br><span class="line">    <span class="keyword">void</span> <span class="title function_">updateIsDefaultByUserId</span><span class="params">(AddressBook addressBook)</span>;</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 class="doctag">@param</span> id</span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    <span class="meta">@Delete(&quot;delete from address_book where id = #&#123;id&#125;&quot;)</span></span><br><span class="line">    <span class="keyword">void</span> <span class="title function_">deleteById</span><span class="params">(Long id)</span>;</span><br><span class="line"></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p><strong>创建AddressBookMapper.xml</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><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><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="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 class="string">&quot;http://mybatis.org/dtd/mybatis-3-mapper.dtd&quot;</span> &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.sky.mapper.AddressBookMapper&quot;</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;list&quot;</span> <span class="attr">parameterType</span>=<span class="string">&quot;AddressBook&quot;</span> <span class="attr">resultType</span>=<span class="string">&quot;AddressBook&quot;</span>&gt;</span></span><br><span class="line">        select * from address_book</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;userId != null&quot;</span>&gt;</span></span><br><span class="line">                and user_id = #&#123;userId&#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">                and 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;isDefault != null&quot;</span>&gt;</span></span><br><span class="line">                and is_default = #&#123;isDefault&#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 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">update</span> <span class="attr">id</span>=<span class="string">&quot;update&quot;</span> <span class="attr">parameterType</span>=<span class="string">&quot;addressBook&quot;</span>&gt;</span></span><br><span class="line">        update address_book</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;consignee != null&quot;</span>&gt;</span></span><br><span class="line">                consignee = #&#123;consignee&#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;sex != null&quot;</span>&gt;</span></span><br><span class="line">                sex = #&#123;sex&#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;detail != null&quot;</span>&gt;</span></span><br><span class="line">                detail = #&#123;detail&#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;label != null&quot;</span>&gt;</span></span><br><span class="line">                label = #&#123;label&#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;isDefault != null&quot;</span>&gt;</span></span><br><span class="line">                is_default = #&#123;isDefault&#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="tag">&lt;/<span class="name">mapper</span>&gt;</span></span><br></pre></td></tr></table></figure><h4 id="Service层-11"><a href="#Service层-11" class="headerlink" title="Service层"></a>Service层</h4><p><strong>创建AddressBookService.java</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="keyword">public</span> <span class="keyword">interface</span> <span class="title class_">AddressBookService</span> &#123;</span><br><span class="line"></span><br><span class="line">    List&lt;AddressBook&gt; <span class="title function_">list</span><span class="params">(AddressBook addressBook)</span>;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">void</span> <span class="title function_">save</span><span class="params">(AddressBook addressBook)</span>;</span><br><span class="line"></span><br><span class="line">    AddressBook <span class="title function_">getById</span><span class="params">(Long id)</span>;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">void</span> <span class="title function_">update</span><span class="params">(AddressBook addressBook)</span>;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">void</span> <span class="title function_">setDefault</span><span class="params">(AddressBook addressBook)</span>;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">void</span> <span class="title function_">deleteById</span><span class="params">(Long id)</span>;</span><br><span class="line"></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p><strong>创建AddressBookServiceImpl.java</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></pre></td><td class="code"><pre><span class="line"><span class="meta">@Service</span></span><br><span class="line"><span class="meta">@Slf4j</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">AddressBookServiceImpl</span> <span class="keyword">implements</span> <span class="title class_">AddressBookService</span> &#123;</span><br><span class="line">    <span class="meta">@Autowired</span></span><br><span class="line">    <span class="keyword">private</span> AddressBookMapper addressBookMapper;</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">@param</span> addressBook</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@return</span></span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    <span class="keyword">public</span> List&lt;AddressBook&gt; <span class="title function_">list</span><span class="params">(AddressBook addressBook)</span> &#123;</span><br><span class="line">        <span class="keyword">return</span> addressBookMapper.list(addressBook);</span><br><span 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 class="doctag">@param</span> addressBook</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_">save</span><span class="params">(AddressBook addressBook)</span> &#123;</span><br><span class="line">        addressBook.setUserId(BaseContext.getCurrentId());</span><br><span class="line">        addressBook.setIsDefault(<span class="number">0</span>);</span><br><span class="line">        addressBookMapper.insert(addressBook);</span><br><span 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="comment">     * <span class="doctag">@param</span> id</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@return</span></span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    <span class="keyword">public</span> AddressBook <span class="title function_">getById</span><span class="params">(Long id)</span> &#123;</span><br><span class="line">        <span class="type">AddressBook</span> <span class="variable">addressBook</span> <span class="operator">=</span> addressBookMapper.getById(id);</span><br><span class="line">        <span class="keyword">return</span> addressBook;</span><br><span 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="comment">     * <span class="doctag">@param</span> addressBook</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_">update</span><span class="params">(AddressBook addressBook)</span> &#123;</span><br><span class="line">        addressBookMapper.update(addressBook);</span><br><span 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 class="doctag">@param</span> addressBook</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_">setDefault</span><span class="params">(AddressBook addressBook)</span> &#123;</span><br><span class="line">        <span class="comment">//1、将当前用户的所有地址修改为非默认地址 update address_book set is_default = ? where user_id = ?</span></span><br><span class="line">        addressBook.setIsDefault(<span class="number">0</span>);</span><br><span class="line">        addressBook.setUserId(BaseContext.getCurrentId());</span><br><span class="line">        addressBookMapper.updateIsDefaultByUserId(addressBook);</span><br><span class="line"></span><br><span class="line">        <span class="comment">//2、将当前地址改为默认地址 update address_book set is_default = ? where id = ?</span></span><br><span class="line">        addressBook.setIsDefault(<span class="number">1</span>);</span><br><span class="line">        addressBookMapper.update(addressBook);</span><br><span 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="comment">     * <span class="doctag">@param</span> id</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_">deleteById</span><span class="params">(Long id)</span> &#123;</span><br><span class="line">        addressBookMapper.deleteById(id);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h4 id="Controller层-14"><a href="#Controller层-14" class="headerlink" title="Controller层"></a>Controller层</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><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></pre></td><td class="code"><pre><span class="line"><span class="meta">@RestController</span></span><br><span class="line"><span class="meta">@RequestMapping(&quot;/user/addressBook&quot;)</span></span><br><span class="line"><span class="meta">@Api(tags = &quot;C端地址簿接口&quot;)</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">AddressBookController</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> AddressBookService addressBookService;</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">@return</span></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="meta">@ApiOperation(&quot;查询当前登录用户的所有地址信息&quot;)</span></span><br><span class="line">    <span class="keyword">public</span> Result&lt;List&lt;AddressBook&gt;&gt; <span class="title function_">list</span><span class="params">()</span> &#123;</span><br><span class="line">        <span class="type">AddressBook</span> <span class="variable">addressBook</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">AddressBook</span>();</span><br><span class="line">        addressBook.setUserId(BaseContext.getCurrentId());</span><br><span class="line">        List&lt;AddressBook&gt; list = addressBookService.list(addressBook);</span><br><span class="line">        <span class="keyword">return</span> Result.success(list);</span><br><span 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 class="doctag">@param</span> addressBook</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@return</span></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="meta">@ApiOperation(&quot;新增地址&quot;)</span></span><br><span class="line">    <span class="keyword">public</span> Result <span class="title function_">save</span><span class="params">(<span class="meta">@RequestBody</span> AddressBook addressBook)</span> &#123;</span><br><span class="line">        addressBookService.save(addressBook);</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="meta">@GetMapping(&quot;/&#123;id&#125;&quot;)</span></span><br><span class="line">    <span class="meta">@ApiOperation(&quot;根据id查询地址&quot;)</span></span><br><span class="line">    <span class="keyword">public</span> Result&lt;AddressBook&gt; <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="type">AddressBook</span> <span class="variable">addressBook</span> <span class="operator">=</span> addressBookService.getById(id);</span><br><span class="line">        <span class="keyword">return</span> Result.success(addressBook);</span><br><span 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="comment">     * <span class="doctag">@param</span> addressBook</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@return</span></span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    <span class="meta">@PutMapping</span></span><br><span class="line">    <span class="meta">@ApiOperation(&quot;根据id修改地址&quot;)</span></span><br><span class="line">    <span class="keyword">public</span> Result <span class="title function_">update</span><span class="params">(<span class="meta">@RequestBody</span> AddressBook addressBook)</span> &#123;</span><br><span class="line">        addressBookService.update(addressBook);</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">     * 设置默认地址</span></span><br><span class="line"><span class="comment">     *</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@param</span> addressBook</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@return</span></span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    <span class="meta">@PutMapping(&quot;/default&quot;)</span></span><br><span class="line">    <span class="meta">@ApiOperation(&quot;设置默认地址&quot;)</span></span><br><span class="line">    <span class="keyword">public</span> Result <span class="title function_">setDefault</span><span class="params">(<span class="meta">@RequestBody</span> AddressBook addressBook)</span> &#123;</span><br><span class="line">        addressBookService.setDefault(addressBook);</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">     * 根据id删除地址</span></span><br><span class="line"><span class="comment">     *</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@param</span> id</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@return</span></span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    <span class="meta">@DeleteMapping</span></span><br><span class="line">    <span class="meta">@ApiOperation(&quot;根据id删除地址&quot;)</span></span><br><span class="line">    <span class="keyword">public</span> Result <span class="title function_">deleteById</span><span class="params">(Long id)</span> &#123;</span><br><span class="line">        addressBookService.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"></span><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">@GetMapping(&quot;default&quot;)</span></span><br><span class="line">    <span class="meta">@ApiOperation(&quot;查询默认地址&quot;)</span></span><br><span class="line">    <span class="keyword">public</span> Result&lt;AddressBook&gt; <span class="title function_">getDefault</span><span class="params">()</span> &#123;</span><br><span class="line">        <span class="comment">//SQL:select * from address_book where user_id = ? and is_default = 1</span></span><br><span class="line">        <span class="type">AddressBook</span> <span class="variable">addressBook</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">AddressBook</span>();</span><br><span class="line">        addressBook.setIsDefault(<span class="number">1</span>);</span><br><span class="line">        addressBook.setUserId(BaseContext.getCurrentId());</span><br><span class="line">        List&lt;AddressBook&gt; list = addressBookService.list(addressBook);</span><br><span class="line"></span><br><span class="line">        <span class="keyword">if</span> (list != <span class="literal">null</span> &amp;&amp; list.size() == <span class="number">1</span>) &#123;</span><br><span class="line">            <span class="keyword">return</span> Result.success(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">return</span> Result.error(<span class="string">&quot;没有查询到默认地址&quot;</span>);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h3 id="功能测试-16"><a href="#功能测试-16" class="headerlink" title="功能测试"></a>功能测试</h3><p>可以通过如下方式进行测试：</p><ul><li>查看控制台sql和数据库中的数据变化</li><li>Swagger接口文档测试</li><li>前后端联调</li></ul><p>这里直接使用<strong>前后端联调</strong>测试：</p><p>启动后台服务，编译小程序</p><p>登录进入首页—&gt;进入个人中心—&gt;进入地址管理<br><div class="img-wrap"><div class="img-bg"><img class="img" src="https://raw.githubusercontent.com/silvan2077/markdown_pic1/main/Picgo/20251116155837.png"/></div></div></p><h2 id="用户下单"><a href="#用户下单" class="headerlink" title="用户下单"></a>用户下单</h2><h3 id="需求分析和设计-21"><a href="#需求分析和设计-21" class="headerlink" title="需求分析和设计"></a>需求分析和设计</h3><h4 id="产品原型-4"><a href="#产品原型-4" class="headerlink" title="产品原型"></a>产品原型</h4><p><strong>用户下单业务说明：</strong><br>在电商系统中，用户通过下单的方式通知商家，用户已经购买了商品，需要商家进行备货和发货。<br>用户下单后会产生订单相关数据，订单数据需要能够体现如下信息：</p><div class="img-wrap"><div class="img-bg"><img class="img" src="https://raw.githubusercontent.com/silvan2077/markdown_pic1/main/Picgo/image-20221214195633802.png"/></div></div><p>用户将菜品或者套餐加入购物车后，可以点击购物车中的 “去结算” 按钮，页面跳转到订单确认页面，点击 “去支付” 按钮则完成下单操作。</p><p><strong>用户点餐业务流程(效果图)：</strong></p><div class="img-wrap"><div class="img-bg"><img class="img" src="https://raw.githubusercontent.com/silvan2077/markdown_pic1/main/Picgo/image-20221214195913467.png"/></div></div><h4 id="接口设计-7"><a href="#接口设计-7" class="headerlink" title="接口设计"></a>接口设计</h4><p><strong>接口分析：</strong></p><div class="tabs" id="用户下单-接口分析"><ul class="nav-tabs"><li class="tab active"><button type="button" data-href="#用户下单-接口分析-1">发送请求</button></li><li class="tab"><button type="button" data-href="#用户下单-接口分析-2">返回数据</button></li></ul><div class="tab-contents"><div class="tab-item-content active" id="用户下单-接口分析-1"><div class="img-wrap"><div class="img-bg"><img class="img" src="https://raw.githubusercontent.com/silvan2077/markdown_pic1/main/Picgo/image-20221214200913654.png"/></div></div><button type="button" class="tab-to-top" aria-label="scroll to top"><i class="fas fa-arrow-up"></i></button></div><div class="tab-item-content" id="用户下单-接口分析-2"><div class="img-wrap"><div class="img-bg"><img class="img" src="https://raw.githubusercontent.com/silvan2077/markdown_pic1/main/Picgo/image-20221214200959943.png"/></div></div><button type="button" class="tab-to-top" aria-label="scroll to top"><i class="fas fa-arrow-up"></i></button></div></div></div><p><strong>接口设计：</strong></p><div class="img-wrap"><div class="img-bg"><img class="img" src="https://raw.githubusercontent.com/silvan2077/markdown_pic1/main/Picgo/image-20221214201211364.png"/></div></div><div class="img-wrap"><div class="img-bg"><img class="img" src="https://raw.githubusercontent.com/silvan2077/markdown_pic1/main/Picgo/image-20221214201220993.png"/></div></div><div class="img-wrap"><div class="img-bg"><img class="img" src="https://raw.githubusercontent.com/silvan2077/markdown_pic1/main/Picgo/image-20221214201236107.png"/></div></div><h4 id="表设计-6"><a href="#表设计-6" class="headerlink" title="表设计"></a>表设计</h4><p>用户下单业务对应的数据表为orders表和order_detail表(一对多关系,一个订单关联多个订单明细)：</p><div class="table-container"><table><thead><tr><th>表名</th><th>含义</th><th>说明</th></tr></thead><tbody><tr><td>orders</td><td>订单表</td><td>主要存储订单的基本信息(如: 订单号、状态、金额、支付方式、下单用户、收件地址等)</td></tr><tr><td>order_detail</td><td>订单明细表</td><td>主要存储订单详情信息(如: 该订单关联的套餐及菜品的信息)</td></tr></tbody></table></div><p>具体的表结构如下: </p><div class="tabs" id="用户下单-表设计"><ul class="nav-tabs"><li class="tab active"><button type="button" data-href="#用户下单-表设计-1">orders</button></li><li class="tab"><button type="button" data-href="#用户下单-表设计-2">order_detail</button></li></ul><div class="tab-contents"><div class="tab-item-content active" id="用户下单-表设计-1"><div class="img-wrap"><div class="img-bg"><img class="img" src="https://raw.githubusercontent.com/silvan2077/markdown_pic1/main/Picgo/20251116161830.png"/></div></div><button type="button" class="tab-to-top" aria-label="scroll to top"><i class="fas fa-arrow-up"></i></button></div><div class="tab-item-content" id="用户下单-表设计-2"><div class="img-wrap"><div class="img-bg"><img class="img" src="https://raw.githubusercontent.com/silvan2077/markdown_pic1/main/Picgo/20251116161936.png"/></div></div><button type="button" class="tab-to-top" aria-label="scroll to top"><i class="fas fa-arrow-up"></i></button></div></div></div><div class="note warning no-icon flat"><p><strong>说明：</strong> 用户提交订单时，需要往订单表orders中插入一条记录，需要往order_detail中插入一条或多条记录。</p></div><h3 id="代码开发-16"><a href="#代码开发-16" class="headerlink" title="代码开发"></a>代码开发</h3><h4 id="DTO设计-2"><a href="#DTO设计-2" class="headerlink" title="DTO设计"></a>DTO设计</h4><p><strong>根据用户下单接口的参数设计DTO：</strong></p><div class="img-wrap"><div class="img-bg"><img class="img" src="https://raw.githubusercontent.com/silvan2077/markdown_pic1/main/Picgo/image-20221214203913803.png"/></div></div><p>在sky-pojo模块，OrdersSubmitDTO.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></pre></td><td class="code"><pre><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_">OrdersSubmitDTO</span> <span class="keyword">implements</span> <span class="title class_">Serializable</span> &#123;</span><br><span class="line">    <span class="comment">//地址簿id</span></span><br><span class="line">    <span class="keyword">private</span> Long addressBookId;</span><br><span class="line">    <span class="comment">//付款方式</span></span><br><span class="line">    <span class="keyword">private</span> <span class="type">int</span> payMethod;</span><br><span class="line">    <span class="comment">//备注</span></span><br><span class="line">    <span class="keyword">private</span> String remark;</span><br><span class="line">    <span class="comment">//预计送达时间</span></span><br><span class="line">    <span class="meta">@JsonFormat(shape = JsonFormat.Shape.STRING, pattern = &quot;yyyy-MM-dd HH:mm:ss&quot;)</span></span><br><span class="line">    <span class="keyword">private</span> LocalDateTime estimatedDeliveryTime;</span><br><span class="line">    <span class="comment">//配送状态  1立即送出  0选择具体时间</span></span><br><span class="line">    <span class="keyword">private</span> Integer deliveryStatus;</span><br><span class="line">    <span class="comment">//餐具数量</span></span><br><span class="line">    <span class="keyword">private</span> Integer tablewareNumber;</span><br><span class="line">    <span class="comment">//餐具数量状态  1按餐量提供  0选择具体数量</span></span><br><span class="line">    <span class="keyword">private</span> Integer tablewareStatus;</span><br><span class="line">    <span class="comment">//打包费</span></span><br><span class="line">    <span class="keyword">private</span> Integer packAmount;</span><br><span class="line">    <span class="comment">//总金额</span></span><br><span class="line">    <span class="keyword">private</span> BigDecimal amount;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h4 id="VO设计-1"><a href="#VO设计-1" class="headerlink" title="VO设计"></a>VO设计</h4><p><strong>根据用户下单接口的返回结果设计VO：</strong><br><div class="img-wrap"><div class="img-bg"><img class="img" src="https://raw.githubusercontent.com/silvan2077/markdown_pic1/main/Picgo/image-20221214204113316.png"/></div></div></p><p>在sky-pojo模块，OrderSubmitVO.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></pre></td><td class="code"><pre><span class="line"><span class="meta">@Data</span></span><br><span class="line"><span class="meta">@Builder</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="keyword">public</span> <span class="keyword">class</span> <span class="title class_">OrderSubmitVO</span> <span class="keyword">implements</span> <span class="title class_">Serializable</span> &#123;</span><br><span class="line">    <span class="comment">//订单id</span></span><br><span class="line">    <span class="keyword">private</span> Long id;</span><br><span class="line">    <span class="comment">//订单号</span></span><br><span class="line">    <span class="keyword">private</span> String orderNumber;</span><br><span class="line">    <span class="comment">//订单金额</span></span><br><span class="line">    <span class="keyword">private</span> BigDecimal orderAmount;</span><br><span class="line">    <span class="comment">//下单时间</span></span><br><span class="line">    <span class="keyword">private</span> LocalDateTime orderTime;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h4 id="Controller层-15"><a href="#Controller层-15" class="headerlink" title="Controller层"></a>Controller层</h4><p><strong>创建OrderController并提供用户下单方法：</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="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(&quot;userOrderController&quot;)</span></span><br><span class="line"><span class="meta">@RequestMapping(&quot;/user/order&quot;)</span></span><br><span class="line"><span class="meta">@Slf4j</span></span><br><span class="line"><span class="meta">@Api(tags = &quot;C端-订单接口&quot;)</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">OrderController</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="comment">     * <span class="doctag">@param</span> ordersSubmitDTO</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@return</span></span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    <span class="meta">@PostMapping(&quot;/submit&quot;)</span></span><br><span class="line">    <span class="meta">@ApiOperation(&quot;用户下单&quot;)</span></span><br><span class="line">    <span class="keyword">public</span> Result&lt;OrderSubmitVO&gt; <span class="title function_">submit</span><span class="params">(<span class="meta">@RequestBody</span> OrdersSubmitDTO ordersSubmitDTO)</span> &#123;</span><br><span class="line">        log.info(<span class="string">&quot;用户下单：&#123;&#125;&quot;</span>, ordersSubmitDTO);</span><br><span class="line">        <span class="type">OrderSubmitVO</span> <span class="variable">orderSubmitVO</span> <span class="operator">=</span> orderService.submitOrder(ordersSubmitDTO);</span><br><span class="line">        <span class="keyword">return</span> Result.success(orderSubmitVO);</span><br><span class="line">    &#125; </span><br><span class="line"></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h4 id="Service层-12"><a href="#Service层-12" class="headerlink" title="Service层"></a>Service层</h4><p><strong>创建OrderService接口，并声明用户下单方法：</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="keyword">public</span> <span class="keyword">interface</span> <span class="title class_">OrderService</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 class="doctag">@param</span> ordersSubmitDTO</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@return</span></span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    OrderSubmitVO <span class="title function_">submitOrder</span><span class="params">(OrdersSubmitDTO ordersSubmitDTO)</span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p><strong>创建OrderServiceImpl实现OrderService接口：</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></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="meta">@Slf4j</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">OrderServiceImpl</span> <span class="keyword">implements</span> <span class="title class_">OrderService</span> &#123;</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 class="meta">@Autowired</span></span><br><span class="line">    <span class="keyword">private</span> OrderDetailMapper orderDetailMapper;</span><br><span class="line">    <span class="meta">@Autowired</span></span><br><span class="line">    <span class="keyword">private</span> ShoppingCartMapper shoppingCartMapper;</span><br><span class="line">    <span class="meta">@Autowired</span></span><br><span class="line">    <span class="keyword">private</span> AddressBookMapper addressBookMapper;</span><br><span class="line"></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">@param</span> ordersSubmitDTO</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@return</span></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> OrderSubmitVO <span class="title function_">submitOrder</span><span class="params">(OrdersSubmitDTO ordersSubmitDTO)</span> &#123;</span><br><span class="line">        <span class="comment">//异常情况的处理（收货地址为空、超出配送范围、购物车为空）</span></span><br><span class="line">        <span class="type">AddressBook</span> <span class="variable">addressBook</span> <span class="operator">=</span> addressBookMapper.getById(ordersSubmitDTO.getAddressBookId());</span><br><span class="line">        <span class="keyword">if</span> (addressBook == <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_">AddressBookBusinessException</span>(MessageConstant.ADDRESS_BOOK_IS_NULL);</span><br><span class="line">        &#125;</span><br><span class="line"></span><br><span class="line">        <span class="type">Long</span> <span class="variable">userId</span> <span class="operator">=</span> BaseContext.getCurrentId();</span><br><span class="line">        <span class="type">ShoppingCart</span> <span class="variable">shoppingCart</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">ShoppingCart</span>();</span><br><span class="line">        shoppingCart.setUserId(userId);</span><br><span class="line"></span><br><span class="line">        <span class="comment">//查询当前用户的购物车数据</span></span><br><span class="line">        List&lt;ShoppingCart&gt; shoppingCartList = shoppingCartMapper.list(shoppingCart);</span><br><span class="line">        <span class="keyword">if</span> (shoppingCartList == <span class="literal">null</span> || shoppingCartList.size() == <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_">ShoppingCartBusinessException</span>(MessageConstant.SHOPPING_CART_IS_NULL);</span><br><span 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">Orders</span> <span class="variable">order</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">Orders</span>();</span><br><span class="line">        BeanUtils.copyProperties(ordersSubmitDTO,order);</span><br><span class="line">        order.setPhone(addressBook.getPhone());</span><br><span class="line">        order.setAddress(addressBook.getDetail());</span><br><span class="line">        order.setConsignee(addressBook.getConsignee());</span><br><span class="line">        order.setNumber(String.valueOf(System.currentTimeMillis()));</span><br><span class="line">        order.setUserId(userId);</span><br><span class="line">        order.setStatus(Orders.PENDING_PAYMENT);</span><br><span class="line">        order.setPayStatus(Orders.UN_PAID);</span><br><span class="line">        order.setOrderTime(LocalDateTime.now());</span><br><span class="line"></span><br><span class="line">        <span class="comment">//向订单表插入1条数据</span></span><br><span class="line">        orderMapper.insert(order);</span><br><span class="line"></span><br><span class="line">        <span class="comment">//订单明细数据</span></span><br><span class="line">        List&lt;OrderDetail&gt; orderDetailList = <span class="keyword">new</span> <span class="title class_">ArrayList</span>&lt;&gt;();</span><br><span class="line">        <span class="keyword">for</span> (ShoppingCart cart : shoppingCartList) &#123;</span><br><span class="line">            <span class="type">OrderDetail</span> <span class="variable">orderDetail</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">OrderDetail</span>();</span><br><span class="line">            BeanUtils.copyProperties(cart, orderDetail);</span><br><span class="line">            orderDetail.setOrderId(order.getId());</span><br><span class="line">            orderDetailList.add(orderDetail);</span><br><span class="line">        &#125;</span><br><span class="line"></span><br><span class="line">        <span class="comment">//向明细表插入n条数据</span></span><br><span class="line">        orderDetailMapper.insertBatch(orderDetailList);</span><br><span class="line"></span><br><span class="line">        <span class="comment">//清理购物车中的数据</span></span><br><span class="line">        shoppingCartMapper.deleteByUserId(userId);</span><br><span class="line"></span><br><span class="line">        <span class="comment">//封装返回结果</span></span><br><span class="line">        <span class="type">OrderSubmitVO</span> <span class="variable">orderSubmitVO</span> <span class="operator">=</span> OrderSubmitVO.builder()</span><br><span class="line">                .id(order.getId())</span><br><span class="line">                .orderNumber(order.getNumber())</span><br><span class="line">                .orderAmount(order.getAmount())</span><br><span class="line">                .orderTime(order.getOrderTime())</span><br><span class="line">                .build();</span><br><span class="line"></span><br><span class="line">        <span class="keyword">return</span> orderSubmitVO;</span><br><span class="line">    &#125;</span><br><span class="line">    </span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h4 id="Mapper层-14"><a href="#Mapper层-14" class="headerlink" title="Mapper层"></a>Mapper层</h4><p><strong>创建OrderMapper接口和对应的xml映射文件：</strong></p><div class="tabs" id="用户下单mapper层"><ul class="nav-tabs"><li class="tab active"><button type="button" data-href="#用户下单mapper层-1">OrderMapper.java</button></li></ul><div class="tab-contents"><div class="tab-item-content active" id="用户下单mapper层-1"><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><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">@Mapper</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">interface</span> <span class="title class_">OrderMapper</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"><span class="comment">     * <span class="doctag">@param</span> order</span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    <span class="keyword">void</span> <span class="title function_">insert</span><span class="params">(Orders order)</span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><button type="button" class="tab-to-top" aria-label="scroll to top"><i class="fas fa-arrow-up"></i></button></div></div></div><p><strong>创建OrderDetailMapper接口和对应的xml映射文件：</strong></p><div class="tabs" id="orderdetailmapper"><ul class="nav-tabs"><li class="tab active"><button type="button" data-href="#orderdetailmapper-1">OrderDetailMapper.java</button></li><li class="tab"><button type="button" data-href="#orderdetailmapper-2">OrderDetailMapper.xml</button></li></ul><div class="tab-contents"><div class="tab-item-content active" id="orderdetailmapper-1"><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><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">@Mapper</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">interface</span> <span class="title class_">OrderDetailMapper</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 class="doctag">@param</span> orderDetails</span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    <span class="keyword">void</span> <span class="title function_">insertBatch</span><span class="params">(List&lt;OrderDetail&gt; orderDetails)</span>;</span><br><span class="line"></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><button type="button" class="tab-to-top" aria-label="scroll to top"><i class="fas fa-arrow-up"></i></button></div><div class="tab-item-content" id="orderdetailmapper-2"><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="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 class="string">&quot;http://mybatis.org/dtd/mybatis-3-mapper.dtd&quot;</span> &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.sky.mapper.OrderDetailMapper&quot;</span>&gt;</span></span><br><span class="line"></span><br><span class="line">    <span class="tag">&lt;<span class="name">insert</span> <span class="attr">id</span>=<span class="string">&quot;insertBatch&quot;</span> <span class="attr">parameterType</span>=<span class="string">&quot;list&quot;</span>&gt;</span></span><br><span class="line">        insert into order_detail</span><br><span class="line">        (name, order_id, dish_id, setmeal_id, dish_flavor, number, amount, image)</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;orderDetails&quot;</span> <span class="attr">item</span>=<span class="string">&quot;od&quot;</span> <span class="attr">separator</span>=<span class="string">&quot;,&quot;</span>&gt;</span></span><br><span class="line">            (#&#123;od.name&#125;,#&#123;od.orderId&#125;,#&#123;od.dishId&#125;,#&#123;od.setmealId&#125;,#&#123;od.dishFlavor&#125;,</span><br><span class="line">             #&#123;od.number&#125;,#&#123;od.amount&#125;,#&#123;od.image&#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="tag">&lt;/<span class="name">mapper</span>&gt;</span></span><br></pre></td></tr></table></figure><button type="button" class="tab-to-top" aria-label="scroll to top"><i class="fas fa-arrow-up"></i></button></div></div></div><h3 id="功能测试-17"><a href="#功能测试-17" class="headerlink" title="功能测试"></a>功能测试</h3><p>登录小程序，完成下单操作</p><p>下单操作时，同时会删除购物车中的数据</p><div class="img-wrap"><div class="img-bg"><img class="img" src="https://raw.githubusercontent.com/silvan2077/markdown_pic1/main/Picgo/image-20221214214128415.png"/></div></div><p><strong>去结算—&gt;去支付</strong></p><div class="img-wrap"><div class="img-bg"><img class="img" src="https://raw.githubusercontent.com/silvan2077/markdown_pic1/main/Picgo/20251116162707.png"/></div></div><div class="tabs" id="用户下单功能测试"><ul class="nav-tabs"><li class="tab active"><button type="button" data-href="#用户下单功能测试-1">orders表</button></li><li class="tab"><button type="button" data-href="#用户下单功能测试-2">order_detail表</button></li><li class="tab"><button type="button" data-href="#用户下单功能测试-3">shopping_cart表</button></li></ul><div class="tab-contents"><div class="tab-item-content active" id="用户下单功能测试-1"><div class="img-wrap"><div class="img-bg"><img class="img" src="https://raw.githubusercontent.com/silvan2077/markdown_pic1/main/Picgo/image-20221214215116451.png"/></div></div><button type="button" class="tab-to-top" aria-label="scroll to top"><i class="fas fa-arrow-up"></i></button></div><div class="tab-item-content" id="用户下单功能测试-2"><div class="img-wrap"><div class="img-bg"><img class="img" src="https://raw.githubusercontent.com/silvan2077/markdown_pic1/main/Picgo/image-20221214215250553.png"/></div></div><button type="button" class="tab-to-top" aria-label="scroll to top"><i class="fas fa-arrow-up"></i></button></div><div class="tab-item-content" id="用户下单功能测试-3"><div class="img-wrap"><div class="img-bg"><img class="img" src="https://raw.githubusercontent.com/silvan2077/markdown_pic1/main/Picgo/image-20221214215428516.png"/></div></div><button type="button" class="tab-to-top" aria-label="scroll to top"><i class="fas fa-arrow-up"></i></button></div></div></div><h2 id="订单支付"><a href="#订单支付" class="headerlink" title="订单支付"></a>订单支付</h2><h3 id="微信支付介绍"><a href="#微信支付介绍" class="headerlink" title="微信支付介绍"></a>微信支付介绍</h3><p>前面已经实现了用户下单，接下来就需要对订单进行支付。在现实生活中经常购买商品并且使用支付功能来付款，在付款的时候可能使用比较多的就是<code>微信支付</code>和<code>支付宝支付</code>了。在苍穹外卖项目中，选择的就是<strong>微信支付</strong>这种支付方式。</p><p>要实现微信支付就需要注册微信支付的一个商户号，这个商户号是<strong>必须要有一家企业并且有正规的营业执照</strong>。只有具备了这些资质之后，才可以去注册商户号，才能开通支付权限。</p><div class="note danger no-icon flat"><p>个人不具备这种资质，无法注册商户号，所以在这里</p></div><p><strong>微信支付产品：</strong></p><p><img src="assets/image-20221214223302651.png" alt="image-20221214223302651" style="zoom:50%;" /> </p><p>本项目选择<strong>小程序支付</strong></p><p>参考：<a href="https://pay.weixin.qq.com/static/product/product_index.shtml">https://pay.weixin.qq.com/static/product/product_index.shtml</a></p><p><strong>微信支付接入流程：</strong></p><p><img src="assets/image-20221214223509246.png" alt="image-20221214223509246" style="zoom:50%;" /> </p><p><strong>微信小程序支付时序图：</strong></p><p><img src="assets/image-20221214223910840.png" alt="image-20221214223910840" style="zoom:50%;" /> </p><p><strong>微信支付相关接口：</strong></p><p><strong>JSAPI下单：</strong>商户系统调用该接口在微信支付服务后台生成预支付交易单(对应时序图的第5步)</p><p><img src="assets/image-20221214224409174.png" alt="image-20221214224409174"></p><p><strong>微信小程序调起支付：</strong>通过JSAPI下单接口获取到发起支付的必要参数prepay_id，然后使用微信支付提供的小程序方法调起小程序支付(对应时序图的第10步)</p><p><img src="assets/image-20221214224551220.png" alt="image-20221214224551220" style="zoom:50%;" /> </p><h3 id="3-2-微信支付准备工作"><a href="#3-2-微信支付准备工作" class="headerlink" title="3.2 微信支付准备工作"></a>3.2 微信支付准备工作</h3><h4 id="3-2-1-如何保证数据安全？"><a href="#3-2-1-如何保证数据安全？" class="headerlink" title="3.2.1 如何保证数据安全？"></a>3.2.1 如何保证数据安全？</h4><p>完成微信支付有两个关键的步骤：</p><p><strong>第一个</strong>就是需要在商户系统当中调用微信后台的一个下单接口，就是生成预支付交易单。</p><p><strong>第二个</strong>就是支付成功之后微信后台会给推送消息。</p><p>这两个接口数据的安全性，要求其实是非常高的。</p><p><strong>解决：</strong>微信提供的方式就是对数据进行加密、解密、签名多种方式。要完成数据加密解密，需要提前准备相应的一些文件，其实就是一些证书。</p><p><strong>获取微信支付平台证书、商户私钥文件：</strong></p><p><img src="assets/image-20221214234038395.png" alt="image-20221214234038395" style="zoom:50%;" /> </p><p>在后绪程序开发过程中，就会使用到这两个文件，需要提前把这两个文件准备好。</p><h4 id="3-2-2-如何调用到商户系统？"><a href="#3-2-2-如何调用到商户系统？" class="headerlink" title="3.2.2 如何调用到商户系统？"></a>3.2.2 如何调用到商户系统？</h4><p>微信后台会调用到商户系统给推送支付的结果，在这里我们就会遇到一个问题，就是微信后台怎么就能调用到我们这个商户系统呢？因为这个调用过程，其实本质上也是一个HTTP请求。</p><p>目前，商户系统它的ip地址就是当前自己电脑的ip地址，只是一个局域网内的ip地址，微信后台无法调用到。</p><p><strong>解决：</strong>内网穿透。通过<strong>cpolar软件</strong>可以获得一个临时域名，而这个临时域名是一个公网ip，这样，微信后台就可以请求到商户系统了。</p><p><strong>cpolar软件的使用：</strong></p><p><strong>1). 下载与安装</strong></p><p>下载地址：<a href="https://dashboard.cpolar.com/get-started">https://dashboard.cpolar.com/get-started</a></p><p><img src="assets/image-20221215184407217.png" alt="image-20221215184407217" style="zoom:50%;" /> </p><p>在资料中已提供，可无需下载。</p><p><img src="assets/image-20221215184446260.png" alt="image-20221215184446260" style="zoom:80%;" /> </p><p>安装过程中，一直下一步即可，不再演示。</p><p><strong>2). cpolar指定authtoken</strong></p><p>复制authtoken：</p><p><img src="assets/image-20221215184746092.png" alt="image-20221215184746092" style="zoom:50%;" /> </p><p>执行命令：</p><p><img src="assets/image-20221215185152869.png" alt="image-20221215185152869" style="zoom:50%;" /> </p><p><strong>3). 获取临时域名</strong></p><p>执行命令：</p><p><img src="assets/image-20221215185749163.png" alt="image-20221215185749163" style="zoom:50%;" /> </p><p>获取域名：</p><p><img src="assets/image-20221215185833157.png" alt="image-20221215185833157" style="zoom:50%;" /> </p><p><strong>4). 验证临时域名有效性</strong></p><p><strong>访问接口文档</strong></p><p>使用localhost:8080访问</p><p><img src="assets/image-20221215190440717.png" alt="image-20221215190440717" style="zoom:50%;" /> </p><p>使用临时域名访问</p><p><img src="assets/image-20221215190525166.png" alt="image-20221215190525166" style="zoom:50%;" /> </p><p>证明临时域名生效。</p><h3 id="3-3-代码导入"><a href="#3-3-代码导入" class="headerlink" title="3.3 代码导入"></a>3.3 代码导入</h3><p>导入资料中的微信支付功能代码即可</p><p><img src="assets/image-20221215192120424.png" alt="image-20221215192120424" style="zoom:50%;" /> </p><h4 id="3-3-1-微信支付相关配置"><a href="#3-3-1-微信支付相关配置" class="headerlink" title="3.3.1 微信支付相关配置"></a>3.3.1 微信支付相关配置</h4><p>application-dev.yml</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></pre></td><td class="code"><pre><span class="line"><span class="attr">sky:</span></span><br><span class="line">  <span class="attr">wechat:</span></span><br><span class="line">    <span class="attr">appid:</span> <span class="string">wxcd2e39f677fd30ba</span></span><br><span class="line">    <span class="attr">secret:</span> <span class="string">84fbfdf5ea288f0c432d829599083637</span></span><br><span class="line">    <span class="attr">mchid :</span> <span class="number">1561414331</span></span><br><span class="line">    <span class="attr">mchSerialNo:</span> <span class="string">4B3B3DC35414AD50B1B755BAF8DE9CC7CF407606</span></span><br><span class="line">    <span class="attr">privateKeyFilePath:</span> <span class="string">D:\apiclient_key.pem</span></span><br><span class="line">    <span class="attr">apiV3Key:</span> <span class="string">CZBK51236435wxpay435434323FFDuv3</span></span><br><span class="line">    <span class="attr">weChatPayCertFilePath:</span> <span class="string">D:\wechatpay_166D96F876F45C7D07CE98952A96EC980368ACFC.pem</span></span><br><span class="line">    <span class="attr">notifyUrl:</span> <span class="string">https://www.weixin.qq.com/wxpay/pay.php</span></span><br><span class="line">    <span class="attr">refundNotifyUrl:</span> <span class="string">https://www.weixin.qq.com/wxpay/pay.php</span></span><br></pre></td></tr></table></figure><p>application.yml</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></pre></td><td class="code"><pre><span class="line"><span class="attr">sky:</span></span><br><span class="line">  <span class="attr">wechat:</span></span><br><span class="line">    <span class="attr">appid:</span> <span class="string">$&#123;sky.wechat.appid&#125;</span></span><br><span class="line">    <span class="attr">secret:</span> <span class="string">$&#123;sky.wechat.secret&#125;</span></span><br><span class="line">    <span class="attr">mchid :</span> <span class="string">$&#123;sky.wechat.mchid&#125;</span></span><br><span class="line">    <span class="attr">mchSerialNo:</span> <span class="string">$&#123;sky.wechat.mchSerialNo&#125;</span></span><br><span class="line">    <span class="attr">privateKeyFilePath:</span> <span class="string">$&#123;sky.wechat.privateKeyFilePath&#125;</span></span><br><span class="line">    <span class="attr">apiV3Key:</span> <span class="string">$&#123;sky.wechat.apiV3Key&#125;</span></span><br><span class="line">    <span class="attr">weChatPayCertFilePath:</span> <span class="string">$&#123;sky.wechat.weChatPayCertFilePath&#125;</span></span><br><span class="line">    <span class="attr">notifyUrl:</span> <span class="string">$&#123;sky.wechat.notifyUrl&#125;</span></span><br><span class="line">    <span class="attr">refundNotifyUrl:</span> <span class="string">$&#123;sky.wechat.refundNotifyUrl&#125;</span></span><br><span class="line"></span><br></pre></td></tr></table></figure><p>WeChatProperties.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></pre></td><td class="code"><pre><span class="line"><span class="keyword">package</span> com.sky.properties;</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> org.springframework.beans.factory.annotation.Value;</span><br><span class="line"><span class="keyword">import</span> org.springframework.boot.context.properties.ConfigurationProperties;</span><br><span class="line"><span class="keyword">import</span> org.springframework.stereotype.Component;</span><br><span class="line"></span><br><span class="line"><span class="meta">@Component</span></span><br><span class="line"><span class="meta">@ConfigurationProperties(prefix = &quot;sky.wechat&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_">WeChatProperties</span> &#123;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">private</span> String appid; <span class="comment">//小程序的appid</span></span><br><span class="line">    <span class="keyword">private</span> String secret; <span class="comment">//小程序的秘钥</span></span><br><span class="line">    <span class="keyword">private</span> String mchid; <span class="comment">//商户号</span></span><br><span class="line">    <span class="keyword">private</span> String mchSerialNo; <span class="comment">//商户API证书的证书序列号</span></span><br><span class="line">    <span class="keyword">private</span> String privateKeyFilePath; <span class="comment">//商户私钥文件</span></span><br><span class="line">    <span class="keyword">private</span> String apiV3Key; <span class="comment">//证书解密的密钥</span></span><br><span class="line">    <span class="keyword">private</span> String weChatPayCertFilePath; <span class="comment">//平台证书</span></span><br><span class="line">    <span class="keyword">private</span> String notifyUrl; <span class="comment">//支付成功的回调地址</span></span><br><span class="line">    <span class="keyword">private</span> String refundNotifyUrl; <span class="comment">//退款成功的回调地址</span></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h4 id="3-3-2-Mapper层"><a href="#3-3-2-Mapper层" class="headerlink" title="3.3.2 Mapper层"></a>3.3.2 Mapper层</h4><p><strong>在OrderMapper.java中添加getByNumberAndUserId和update两个方法</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></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 class="doctag">@param</span> orderNumber</span></span><br><span class="line"><span class="comment">    * <span class="doctag">@param</span> userId</span></span><br><span class="line"><span class="comment">    */</span></span><br><span class="line">   <span class="meta">@Select(&quot;select * from orders where number = #&#123;orderNumber&#125; and user_id= #&#123;userId&#125;&quot;)</span></span><br><span class="line">   Orders <span class="title function_">getByNumberAndUserId</span><span class="params">(String orderNumber, Long userId)</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 class="doctag">@param</span> orders</span></span><br><span class="line"><span class="comment">    */</span></span><br><span class="line">   <span class="keyword">void</span> <span class="title function_">update</span><span class="params">(Orders orders)</span>;</span><br></pre></td></tr></table></figure><p><strong>在OrderMapper.xml中添加</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><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><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="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;com.sky.entity.Orders&quot;</span>&gt;</span></span><br><span class="line">        update orders</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;cancelReason != null and cancelReason!=&#x27;&#x27; &quot;</span>&gt;</span></span><br><span class="line">                cancel_reason=#&#123;cancelReason&#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;rejectionReason != null and rejectionReason!=&#x27;&#x27; &quot;</span>&gt;</span></span><br><span class="line">                rejection_reason=#&#123;rejectionReason&#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;cancelTime != null&quot;</span>&gt;</span></span><br><span class="line">                cancel_time=#&#123;cancelTime&#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;payStatus != null&quot;</span>&gt;</span></span><br><span class="line">                pay_status=#&#123;payStatus&#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;payMethod != null&quot;</span>&gt;</span></span><br><span class="line">                pay_method=#&#123;payMethod&#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;checkoutTime != null&quot;</span>&gt;</span></span><br><span class="line">                checkout_time=#&#123;checkoutTime&#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">if</span> <span class="attr">test</span>=<span class="string">&quot;deliveryTime != null&quot;</span>&gt;</span></span><br><span class="line">                delivery_time = #&#123;deliveryTime&#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></pre></td></tr></table></figure><h4 id="3-3-3-Service层"><a href="#3-3-3-Service层" class="headerlink" title="3.3.3 Service层"></a>3.3.3 Service层</h4><p><strong>在OrderService.java中添加payment和paySuccess两个方法定义</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="comment">/**</span></span><br><span class="line"><span class="comment">    * 订单支付</span></span><br><span class="line"><span class="comment">    * <span class="doctag">@param</span> ordersPaymentDTO</span></span><br><span class="line"><span class="comment">    * <span class="doctag">@return</span></span></span><br><span class="line"><span class="comment">    */</span></span><br><span class="line">   OrderPaymentVO <span class="title function_">payment</span><span class="params">(OrdersPaymentDTO ordersPaymentDTO)</span> <span class="keyword">throws</span> Exception;</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 class="doctag">@param</span> outTradeNo</span></span><br><span class="line"><span class="comment">    */</span></span><br><span class="line">   <span class="keyword">void</span> <span class="title function_">paySuccess</span><span class="params">(String outTradeNo)</span>;</span><br></pre></td></tr></table></figure><p><strong>在OrderServiceImpl.java中实现payment和paySuccess两个方法</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></pre></td><td class="code"><pre><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 class="meta">@Autowired</span></span><br><span class="line">   <span class="keyword">private</span> WeChatPayUtil weChatPayUtil;</span><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">@param</span> ordersPaymentDTO</span></span><br><span class="line"><span class="comment">    * <span class="doctag">@return</span></span></span><br><span class="line"><span class="comment">    */</span></span><br><span class="line">   <span class="keyword">public</span> OrderPaymentVO <span class="title function_">payment</span><span class="params">(OrdersPaymentDTO ordersPaymentDTO)</span> <span class="keyword">throws</span> Exception &#123;</span><br><span class="line">       <span class="comment">// 当前登录用户id</span></span><br><span class="line">       <span class="type">Long</span> <span class="variable">userId</span> <span class="operator">=</span> BaseContext.getCurrentId();</span><br><span class="line">       <span class="type">User</span> <span class="variable">user</span> <span class="operator">=</span> userMapper.getById(userId);</span><br><span class="line"></span><br><span class="line">       <span class="comment">//调用微信支付接口，生成预支付交易单</span></span><br><span class="line">       <span class="type">JSONObject</span> <span class="variable">jsonObject</span> <span class="operator">=</span> weChatPayUtil.pay(</span><br><span class="line">               ordersPaymentDTO.getOrderNumber(), <span class="comment">//商户订单号</span></span><br><span class="line">               <span class="keyword">new</span> <span class="title class_">BigDecimal</span>(<span class="number">0.01</span>), <span class="comment">//支付金额，单位 元</span></span><br><span class="line">               <span class="string">&quot;苍穹外卖订单&quot;</span>, <span class="comment">//商品描述</span></span><br><span class="line">               user.getOpenid() <span class="comment">//微信用户的openid</span></span><br><span class="line">       );</span><br><span class="line"></span><br><span class="line">       <span class="keyword">if</span> (jsonObject.getString(<span class="string">&quot;code&quot;</span>) != <span class="literal">null</span> &amp;&amp; jsonObject.getString(<span class="string">&quot;code&quot;</span>).equals(<span class="string">&quot;ORDERPAID&quot;</span>)) &#123;</span><br><span class="line">           <span class="keyword">throw</span> <span class="keyword">new</span> <span class="title class_">OrderBusinessException</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="type">OrderPaymentVO</span> <span class="variable">vo</span> <span class="operator">=</span> jsonObject.toJavaObject(OrderPaymentVO.class);</span><br><span class="line">       vo.setPackageStr(jsonObject.getString(<span class="string">&quot;package&quot;</span>));</span><br><span class="line"></span><br><span class="line">       <span class="keyword">return</span> vo;</span><br><span 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 class="doctag">@param</span> outTradeNo</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_">paySuccess</span><span class="params">(String outTradeNo)</span> &#123;</span><br><span class="line">       <span class="comment">// 当前登录用户id</span></span><br><span class="line">       <span class="type">Long</span> <span class="variable">userId</span> <span class="operator">=</span> BaseContext.getCurrentId();</span><br><span class="line"></span><br><span class="line">       <span class="comment">// 根据订单号查询当前用户的订单</span></span><br><span class="line">       <span class="type">Orders</span> <span class="variable">ordersDB</span> <span class="operator">=</span> orderMapper.getByNumberAndUserId(outTradeNo, userId);</span><br><span class="line"></span><br><span class="line">       <span class="comment">// 根据订单id更新订单的状态、支付方式、支付状态、结账时间</span></span><br><span class="line">       <span class="type">Orders</span> <span class="variable">orders</span> <span class="operator">=</span> Orders.builder()</span><br><span class="line">               .id(ordersDB.getId())</span><br><span class="line">               .status(Orders.TO_BE_CONFIRMED)</span><br><span class="line">               .payStatus(Orders.PAID)</span><br><span class="line">               .checkoutTime(LocalDateTime.now())</span><br><span class="line">               .build();</span><br><span class="line"></span><br><span class="line">       orderMapper.update(orders);</span><br><span class="line">   &#125;</span><br></pre></td></tr></table></figure><h4 id="3-3-4-Controller层"><a href="#3-3-4-Controller层" class="headerlink" title="3.3.4 Controller层"></a>3.3.4 Controller层</h4><p><strong>在OrderController.java中添加payment方法</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></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">@param</span> ordersPaymentDTO</span></span><br><span class="line"><span class="comment">   * <span class="doctag">@return</span></span></span><br><span class="line"><span class="comment">   */</span></span><br><span class="line">  <span class="meta">@PutMapping(&quot;/payment&quot;)</span></span><br><span class="line">  <span class="meta">@ApiOperation(&quot;订单支付&quot;)</span></span><br><span class="line">  <span class="keyword">public</span> Result&lt;OrderPaymentVO&gt; <span class="title function_">payment</span><span class="params">(<span class="meta">@RequestBody</span> OrdersPaymentDTO ordersPaymentDTO)</span> <span class="keyword">throws</span> Exception &#123;</span><br><span class="line">      log.info(<span class="string">&quot;订单支付：&#123;&#125;&quot;</span>, ordersPaymentDTO);</span><br><span class="line">      <span class="type">OrderPaymentVO</span> <span class="variable">orderPaymentVO</span> <span class="operator">=</span> orderService.payment(ordersPaymentDTO);</span><br><span class="line">      log.info(<span class="string">&quot;生成预支付交易单：&#123;&#125;&quot;</span>, orderPaymentVO);</span><br><span class="line">      <span class="keyword">return</span> Result.success(orderPaymentVO);</span><br><span class="line">  &#125;</span><br></pre></td></tr></table></figure><p>PayNotifyController.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><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></pre></td><td class="code"><pre><span class="line"><span class="keyword">package</span> com.sky.controller.notify;</span><br><span class="line"></span><br><span class="line"><span class="keyword">import</span> com.alibaba.druid.support.json.JSONUtils;</span><br><span class="line"><span class="keyword">import</span> com.alibaba.fastjson.JSON;</span><br><span class="line"><span class="keyword">import</span> com.alibaba.fastjson.JSONObject;</span><br><span class="line"><span class="keyword">import</span> com.sky.annotation.IgnoreToken;</span><br><span class="line"><span class="keyword">import</span> com.sky.properties.WeChatProperties;</span><br><span class="line"><span class="keyword">import</span> com.sky.service.OrderService;</span><br><span class="line"><span class="keyword">import</span> com.wechat.pay.contrib.apache.httpclient.util.AesUtil;</span><br><span class="line"><span class="keyword">import</span> lombok.extern.slf4j.Slf4j;</span><br><span class="line"><span class="keyword">import</span> org.apache.http.entity.ContentType;</span><br><span class="line"><span class="keyword">import</span> org.springframework.beans.factory.annotation.Autowired;</span><br><span class="line"><span class="keyword">import</span> org.springframework.web.bind.annotation.RequestMapping;</span><br><span class="line"><span class="keyword">import</span> org.springframework.web.bind.annotation.RestController;</span><br><span class="line"><span class="keyword">import</span> javax.servlet.http.HttpServletRequest;</span><br><span class="line"><span class="keyword">import</span> javax.servlet.http.HttpServletResponse;</span><br><span class="line"><span class="keyword">import</span> java.io.BufferedReader;</span><br><span class="line"><span class="keyword">import</span> java.nio.charset.StandardCharsets;</span><br><span class="line"><span class="keyword">import</span> java.util.HashMap;</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;/notify&quot;)</span></span><br><span class="line"><span class="meta">@Slf4j</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">PayNotifyController</span> &#123;</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 class="meta">@Autowired</span></span><br><span class="line">    <span class="keyword">private</span> WeChatProperties weChatProperties;</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">@param</span> request</span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    <span class="meta">@RequestMapping(&quot;/paySuccess&quot;)</span></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">paySuccessNotify</span><span class="params">(HttpServletRequest request, HttpServletResponse response)</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">String</span> <span class="variable">body</span> <span class="operator">=</span> readData(request);</span><br><span class="line">        log.info(<span class="string">&quot;支付成功回调：&#123;&#125;&quot;</span>, body);</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">plainText</span> <span class="operator">=</span> decryptData(body);</span><br><span class="line">        log.info(<span class="string">&quot;解密后的文本：&#123;&#125;&quot;</span>, plainText);</span><br><span class="line"></span><br><span class="line">        <span class="type">JSONObject</span> <span class="variable">jsonObject</span> <span class="operator">=</span> JSON.parseObject(plainText);</span><br><span class="line">        <span class="type">String</span> <span class="variable">outTradeNo</span> <span class="operator">=</span> jsonObject.getString(<span class="string">&quot;out_trade_no&quot;</span>);<span class="comment">//商户平台订单号</span></span><br><span class="line">        <span class="type">String</span> <span class="variable">transactionId</span> <span class="operator">=</span> jsonObject.getString(<span class="string">&quot;transaction_id&quot;</span>);<span class="comment">//微信支付交易号</span></span><br><span class="line"></span><br><span class="line">        log.info(<span class="string">&quot;商户平台订单号：&#123;&#125;&quot;</span>, outTradeNo);</span><br><span class="line">        log.info(<span class="string">&quot;微信支付交易号：&#123;&#125;&quot;</span>, transactionId);</span><br><span class="line"></span><br><span class="line">        <span class="comment">//业务处理，修改订单状态、来单提醒</span></span><br><span class="line">        orderService.paySuccess(outTradeNo);</span><br><span class="line"></span><br><span class="line">        <span class="comment">//给微信响应</span></span><br><span class="line">        responseToWeixin(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">     * 读取数据</span></span><br><span class="line"><span class="comment">     *</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@param</span> request</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@return</span></span></span><br><span class="line"><span class="comment">     * <span class="doctag">@throws</span> Exception</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_">readData</span><span class="params">(HttpServletRequest request)</span> <span class="keyword">throws</span> Exception &#123;</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">StringBuilder</span> <span class="variable">result</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">StringBuilder</span>();</span><br><span class="line">        <span class="type">String</span> <span class="variable">line</span> <span class="operator">=</span> <span class="literal">null</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">            <span class="keyword">if</span> (result.length() &gt; <span class="number">0</span>) &#123;</span><br><span class="line">                result.append(<span class="string">&quot;\n&quot;</span>);</span><br><span class="line">            &#125;</span><br><span class="line">            result.append(line);</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">return</span> result.toString();</span><br><span 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 class="doctag">@param</span> body</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@return</span></span></span><br><span class="line"><span class="comment">     * <span class="doctag">@throws</span> Exception</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_">decryptData</span><span class="params">(String body)</span> <span class="keyword">throws</span> Exception &#123;</span><br><span class="line">        <span class="type">JSONObject</span> <span class="variable">resultObject</span> <span class="operator">=</span> JSON.parseObject(body);</span><br><span class="line">        <span class="type">JSONObject</span> <span class="variable">resource</span> <span class="operator">=</span> resultObject.getJSONObject(<span class="string">&quot;resource&quot;</span>);</span><br><span class="line">        <span class="type">String</span> <span class="variable">ciphertext</span> <span class="operator">=</span> resource.getString(<span class="string">&quot;ciphertext&quot;</span>);</span><br><span class="line">        <span class="type">String</span> <span class="variable">nonce</span> <span class="operator">=</span> resource.getString(<span class="string">&quot;nonce&quot;</span>);</span><br><span class="line">        <span class="type">String</span> <span class="variable">associatedData</span> <span class="operator">=</span> resource.getString(<span class="string">&quot;associated_data&quot;</span>);</span><br><span class="line"></span><br><span class="line">        <span class="type">AesUtil</span> <span class="variable">aesUtil</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">AesUtil</span>(weChatProperties.getApiV3Key().getBytes(StandardCharsets.UTF_8));</span><br><span class="line">        <span class="comment">//密文解密</span></span><br><span class="line">        <span class="type">String</span> <span class="variable">plainText</span> <span class="operator">=</span> aesUtil.decryptToString(associatedData.getBytes(StandardCharsets.UTF_8),</span><br><span class="line">                nonce.getBytes(StandardCharsets.UTF_8),</span><br><span class="line">                ciphertext);</span><br><span class="line"></span><br><span class="line">        <span class="keyword">return</span> plainText;</span><br><span 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 class="doctag">@param</span> response</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_">responseToWeixin</span><span class="params">(HttpServletResponse response)</span> <span class="keyword">throws</span> Exception&#123;</span><br><span class="line">        response.setStatus(<span class="number">200</span>);</span><br><span class="line">        HashMap&lt;Object, Object&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;code&quot;</span>, <span class="string">&quot;SUCCESS&quot;</span>);</span><br><span class="line">        map.put(<span class="string">&quot;message&quot;</span>, <span class="string">&quot;SUCCESS&quot;</span>);</span><br><span class="line">        response.setHeader(<span class="string">&quot;Content-type&quot;</span>, ContentType.APPLICATION_JSON.toString());</span><br><span class="line">        response.getOutputStream().write(JSONUtils.toJSONString(map).getBytes(StandardCharsets.UTF_8));</span><br><span class="line">        response.flushBuffer();</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><p>测试过程中，可通过断点方式查看后台每一步执行情况。</p><p><strong>下单：</strong></p><p><img src="assets/image-20221215205122716.png" alt="image-20221215205122716" style="zoom: 80%;" /> </p><p><strong>去支付：</strong></p><p><img src="assets/image-20221215205308701.png" alt="image-20221215205308701" style="zoom:80%;" /> </p><p><strong>确认支付：</strong></p><p><img src="assets/image-20221215205434552.png" alt="image-20221215205434552" style="zoom:80%;" /> </p><p>进行扫码支付即可。</p><h3 id="3-5-代码提交"><a href="#3-5-代码提交" class="headerlink" title="3.5 代码提交"></a>3.5 代码提交</h3><p><img src="assets/image-20221215205746248.png" alt="image-20221215205746248" style="zoom:50%;" /> </p><p> 后续步骤和其它功能代码提交一致，不再赘述。</p><h1 id="day09"><a href="#day09" class="headerlink" title="day09"></a>day09</h1><h2 id="用户端历史订单模块"><a href="#用户端历史订单模块" class="headerlink" title="用户端历史订单模块"></a>用户端历史订单模块</h2><h3 id="查询历史订单"><a href="#查询历史订单" class="headerlink" title="查询历史订单"></a>查询历史订单</h3><h4 id="需求分析和设计-22"><a href="#需求分析和设计-22" class="headerlink" title="需求分析和设计"></a>需求分析和设计</h4><div class="img-wrap"><div class="img-bg"><img class="img" src="https://raw.githubusercontent.com/silvan2077/markdown_pic1/main/Picgo/image-20221128092537535.png"/></div></div><div class="note info no-icon flat"><p>业务规则</p><ul><li>分页查询历史订单</li><li>可以根据订单状态查询</li><li>展示订单数据时，需要展示的数据包括：下单时间、订单状态、订单金额、订单明细（商品名称、图片）</li></ul></div><p>接口设计：详见接口文档<br><div class="img-wrap"><div class="img-bg"><img class="img" src="https://raw.githubusercontent.com/silvan2077/markdown_pic1/main/Picgo/image-20221128103222657.png"/></div></div></p><h4 id="代码实现-5"><a href="#代码实现-5" class="headerlink" title="代码实现"></a>代码实现</h4><p><strong>Controller层</strong><br>在OrderController中添加page方法：<br><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><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="comment"> * 历史订单查询</span></span><br><span class="line"><span class="comment"> *</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@param</span> page</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@param</span> pageSize</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@param</span> status   订单状态 1待付款 2待接单 3已接单 4派送中 5已完成 6已取消</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@return</span></span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="meta">@GetMapping(&quot;/historyOrders&quot;)</span></span><br><span class="line"><span class="meta">@ApiOperation(&quot;历史订单查询&quot;)</span></span><br><span class="line"><span class="keyword">public</span> Result&lt;PageResult&gt; <span class="title function_">page</span><span class="params">(<span class="type">int</span> page, <span class="type">int</span> pageSize, Integer status)</span> &#123;</span><br><span class="line">    <span class="type">PageResult</span> <span class="variable">pageResult</span> <span class="operator">=</span> orderService.pageQuery4User(page, pageSize, status);</span><br><span class="line">    <span class="keyword">return</span> Result.success(pageResult);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><br><strong>Service层</strong><br>在OrderService中添加以下方法：<br><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><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="comment"> * 用户端订单分页查询</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@param</span> page</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@param</span> pageSize</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@param</span> status</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@return</span></span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line">PageResult <span class="title function_">pageQuery4User</span><span class="params">(<span class="type">int</span> page, <span class="type">int</span> pageSize, Integer status)</span>;</span><br></pre></td></tr></table></figure><br>在OrderServiceImpl中实现该方法</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="comment">     * <span class="doctag">@param</span> pageNum</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@param</span> pageSize</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@param</span> status</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@return</span></span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    <span class="keyword">public</span> PageResult <span class="title function_">pageQuery4User</span><span class="params">(<span class="type">int</span> pageNum, <span class="type">int</span> pageSize, Integer status)</span> &#123;</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="type">OrdersPageQueryDTO</span> <span class="variable">ordersPageQueryDTO</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">OrdersPageQueryDTO</span>();</span><br><span class="line">        ordersPageQueryDTO.setUserId(BaseContext.getCurrentId());</span><br><span class="line">        ordersPageQueryDTO.setStatus(status);</span><br><span class="line"></span><br><span class="line">        <span class="comment">// 分页条件查询</span></span><br><span class="line">        Page&lt;Orders&gt; page = orderMapper.pageQuery(ordersPageQueryDTO);</span><br><span class="line"></span><br><span class="line">        List&lt;OrderVO&gt; list = <span class="keyword">new</span> <span class="title class_">ArrayList</span>();</span><br><span class="line"></span><br><span class="line">        <span class="comment">// 查询出订单明细，并封装入OrderVO进行响应</span></span><br><span class="line">        <span class="keyword">if</span> (page != <span class="literal">null</span> &amp;&amp; page.getTotal() &gt; <span class="number">0</span>) &#123;</span><br><span class="line">            <span class="keyword">for</span> (Orders orders : page) &#123;</span><br><span class="line">                <span class="type">Long</span> <span class="variable">orderId</span> <span class="operator">=</span> orders.getId();<span class="comment">// 订单id</span></span><br><span class="line"></span><br><span class="line">                <span class="comment">// 查询订单明细</span></span><br><span class="line">                List&lt;OrderDetail&gt; orderDetails = orderDetailMapper.getByOrderId(orderId);</span><br><span class="line"></span><br><span class="line">                <span class="type">OrderVO</span> <span class="variable">orderVO</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">OrderVO</span>();</span><br><span class="line">                BeanUtils.copyProperties(orders, orderVO);</span><br><span class="line">                orderVO.setOrderDetailList(orderDetails);</span><br><span class="line"></span><br><span class="line">                list.add(orderVO);</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">return</span> <span class="keyword">new</span> <span class="title class_">PageResult</span>(page.getTotal(), list);</span><br><span class="line">    &#125;</span><br></pre></td></tr></table></figure><p><strong>Mapper层</strong></p><p>在OrderMapper中添加方法<br><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><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="comment"> * 分页条件查询并按下单时间排序</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@param</span> ordersPageQueryDTO</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line">Page&lt;Orders&gt; <span class="title function_">pageQuery</span><span class="params">(OrdersPageQueryDTO ordersPageQueryDTO)</span>;</span><br></pre></td></tr></table></figure><br>在映射文件中添加SQL语句</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></pre></td><td class="code"><pre><span class="line"><span class="tag">&lt;<span class="name">select</span> <span class="attr">id</span>=<span class="string">&quot;pageQuery&quot;</span> <span class="attr">resultType</span>=<span class="string">&quot;Orders&quot;</span>&gt;</span></span><br><span class="line">       select * from orders</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;number != null and number!=&#x27;&#x27;&quot;</span>&gt;</span></span><br><span class="line">               and number like concat(&#x27;%&#x27;,#&#123;number&#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;phone != null and phone!=&#x27;&#x27;&quot;</span>&gt;</span></span><br><span class="line">               and phone like concat(&#x27;%&#x27;,#&#123;phone&#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;userId != null&quot;</span>&gt;</span></span><br><span class="line">               and user_id = #&#123;userId&#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">               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;beginTime != null&quot;</span>&gt;</span></span><br><span class="line">               and order_time <span class="symbol">&amp;gt;</span>= #&#123;beginTime&#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;endTime != null&quot;</span>&gt;</span></span><br><span class="line">               and order_time <span class="symbol">&amp;lt;</span>= #&#123;endTime&#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 order_time desc</span><br><span class="line">   <span class="tag">&lt;/<span class="name">select</span>&gt;</span></span><br></pre></td></tr></table></figure><p>在OrderDetailMapper中添加方法：<br><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><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="comment"> * 根据订单id查询订单明细</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@param</span> orderId</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@return</span></span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="meta">@Select(&quot;select * from order_detail where order_id = #&#123;orderId&#125;&quot;)</span></span><br><span class="line">List&lt;OrderDetail&gt; <span class="title function_">getByOrderId</span><span class="params">(Long orderId)</span>;</span><br></pre></td></tr></table></figure></p><h3 id="查询订单详情"><a href="#查询订单详情" class="headerlink" title="查询订单详情"></a>查询订单详情</h3><h4 id="需求分析和设计-23"><a href="#需求分析和设计-23" class="headerlink" title="需求分析和设计"></a>需求分析和设计</h4><div class="img-wrap"><div class="img-bg"><img class="img" src="https://raw.githubusercontent.com/silvan2077/markdown_pic1/main/Picgo/image-20221128102144294.png"/></div></div><p>接口设计：参见接口文档<br><div class="img-wrap"><div class="img-bg"><img class="img" src="https://raw.githubusercontent.com/silvan2077/markdown_pic1/main/Picgo/image-20221128142142811.png"/></div></div></p><h4 id="代码实现-6"><a href="#代码实现-6" class="headerlink" title="代码实现"></a>代码实现</h4><p><strong>controller层</strong><br>在OrderController中添加方法<br><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><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="comment"> * <span class="doctag">@param</span> id</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@return</span></span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="meta">@GetMapping(&quot;/orderDetail/&#123;id&#125;&quot;)</span></span><br><span class="line"><span class="meta">@ApiOperation(&quot;查询订单详情&quot;)</span></span><br><span class="line"><span class="keyword">public</span> Result&lt;OrderVO&gt; <span class="title function_">details</span><span class="params">(<span class="meta">@PathVariable(&quot;id&quot;)</span> Long id)</span> &#123;</span><br><span class="line">    <span class="type">OrderVO</span> <span class="variable">orderVO</span> <span class="operator">=</span> orderService.details(id);</span><br><span class="line">    <span class="keyword">return</span> Result.success(orderVO);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><br><strong>service层</strong><br>在OrderService中添加方法<br><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><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="comment"> * 查询订单详情</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@param</span> id</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@return</span></span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line">OrderVO <span class="title function_">details</span><span class="params">(Long id)</span>;</span><br></pre></td></tr></table></figure><br>在OrderServiceImpl中实现该方法<br><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><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="comment"> * <span class="doctag">@param</span> id</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@return</span></span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="keyword">public</span> OrderVO <span class="title function_">details</span><span class="params">(Long id)</span> &#123;</span><br><span class="line">    <span class="comment">// 根据id查询订单</span></span><br><span class="line">    <span class="type">Orders</span> <span class="variable">orders</span> <span class="operator">=</span> orderMapper.getById(id);</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 查询该订单对应的菜品/套餐明细</span></span><br><span class="line">    List&lt;OrderDetail&gt; orderDetailList = orderDetailMapper.getByOrderId(orders.getId());</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 将该订单及其详情封装到OrderVO并返回</span></span><br><span class="line">    <span class="type">OrderVO</span> <span class="variable">orderVO</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">OrderVO</span>();</span><br><span class="line">    BeanUtils.copyProperties(orders, orderVO);</span><br><span class="line">    orderVO.setOrderDetailList(orderDetailList);</span><br><span class="line"></span><br><span class="line">    <span class="keyword">return</span> orderVO;</span><br><span class="line">&#125;</span><br><span class="line"></span><br></pre></td></tr></table></figure><br><strong>Mapper层</strong><br>在OrderMapper中添加方法<br><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><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="comment"> * 根据id查询订单</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@param</span> id</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="meta">@Select(&quot;select * from orders where id=#&#123;id&#125;&quot;)</span></span><br><span class="line">Orders <span class="title function_">getById</span><span class="params">(Long id)</span>;</span><br></pre></td></tr></table></figure></p><h3 id="取消订单"><a href="#取消订单" class="headerlink" title="取消订单"></a>取消订单</h3><h4 id="需求分析和设计-24"><a href="#需求分析和设计-24" class="headerlink" title="需求分析和设计"></a>需求分析和设计</h4><div class="img-wrap"><div class="img-bg"><img class="img" src="https://raw.githubusercontent.com/silvan2077/markdown_pic1/main/Picgo/image-20221128145444268.png"/></div></div><div class="note info no-icon flat"><p>业务规则：</p><ul><li>待支付和待接单状态下，用户可直接取消订单</li><li>商家已接单状态下，用户取消订单需电话沟通商家</li><li>派送中状态下，用户取消订单需电话沟通商家</li><li>如果在待接单状态下取消订单，需要给用户退款</li><li>取消订单后需要将订单状态修改为“已取消”</li></ul></div><p>接口设计：参见接口文档<br><div class="img-wrap"><div class="img-bg"><img class="img" src="https://raw.githubusercontent.com/silvan2077/markdown_pic1/main/Picgo/image-20221128164410852.png"/></div></div></p><h4 id="代码实现-7"><a href="#代码实现-7" class="headerlink" title="代码实现"></a>代码实现</h4><p><strong>Controller层</strong><br>在OrderController中添加方法<br><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><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="comment"> * <span class="doctag">@return</span></span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="meta">@PutMapping(&quot;/cancel/&#123;id&#125;&quot;)</span></span><br><span class="line"><span class="meta">@ApiOperation(&quot;取消订单&quot;)</span></span><br><span class="line"><span class="keyword">public</span> Result <span class="title function_">cancel</span><span class="params">(<span class="meta">@PathVariable(&quot;id&quot;)</span> Long id)</span> <span class="keyword">throws</span> Exception &#123;</span><br><span class="line">    orderService.userCancelById(id);</span><br><span class="line">    <span class="keyword">return</span> Result.success();</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></p><p><strong>Service层</strong></p><p>在OrderService中添加方法<br><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><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="comment"> * 用户取消订单</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@param</span> id</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="keyword">void</span> <span class="title function_">userCancelById</span><span class="params">(Long id)</span> <span class="keyword">throws</span> Exception;</span><br></pre></td></tr></table></figure></p><p>在OrderServiceImpl中实现该方法<br><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><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="comment">    * <span class="doctag">@param</span> id</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_">userCancelById</span><span class="params">(Long id)</span> <span class="keyword">throws</span> Exception &#123;</span><br><span class="line">       <span class="comment">// 根据id查询订单</span></span><br><span class="line">       <span class="type">Orders</span> <span class="variable">ordersDB</span> <span class="operator">=</span> orderMapper.getById(id);</span><br><span class="line"></span><br><span class="line">       <span class="comment">// 校验订单是否存在</span></span><br><span class="line">       <span class="keyword">if</span> (ordersDB == <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_">OrderBusinessException</span>(MessageConstant.ORDER_NOT_FOUND);</span><br><span class="line">       &#125;</span><br><span class="line"></span><br><span class="line">       <span class="comment">//订单状态 1待付款 2待接单 3已接单 4派送中 5已完成 6已取消</span></span><br><span class="line">       <span class="keyword">if</span> (ordersDB.getStatus() &gt; <span class="number">2</span>) &#123;</span><br><span class="line">           <span class="keyword">throw</span> <span class="keyword">new</span> <span class="title class_">OrderBusinessException</span>(MessageConstant.ORDER_STATUS_ERROR);</span><br><span class="line">       &#125;</span><br><span class="line"></span><br><span class="line">       <span class="type">Orders</span> <span class="variable">orders</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">Orders</span>();</span><br><span class="line">       orders.setId(ordersDB.getId());</span><br><span class="line"></span><br><span class="line">       <span class="comment">// 订单处于待接单状态下取消，需要进行退款</span></span><br><span class="line">       <span class="keyword">if</span> (ordersDB.getStatus().equals(Orders.TO_BE_CONFIRMED)) &#123;</span><br><span class="line">           <span class="comment">// //调用微信支付退款接口</span></span><br><span class="line">           <span class="comment">// weChatPayUtil.refund(</span></span><br><span class="line">           <span class="comment">//         ordersDB.getNumber(), //商户订单号</span></span><br><span class="line">           <span class="comment">//         ordersDB.getNumber(), //商户退款单号</span></span><br><span class="line">           <span class="comment">//         new BigDecimal(0.01),//退款金额，单位 元</span></span><br><span class="line">           <span class="comment">//         new BigDecimal(0.01));//原订单金额</span></span><br><span class="line"></span><br><span class="line">           <span class="comment">//支付状态修改为 退款</span></span><br><span class="line">           orders.setPayStatus(Orders.REFUND);</span><br><span class="line">       &#125;</span><br><span class="line"></span><br><span class="line">       <span class="comment">// 更新订单状态、取消原因、取消时间</span></span><br><span class="line">       orders.setStatus(Orders.CANCELLED);</span><br><span class="line">       orders.setCancelReason(<span class="string">&quot;用户取消&quot;</span>);</span><br><span class="line">       orders.setCancelTime(LocalDateTime.now());</span><br><span class="line">       orderMapper.update(orders);</span><br><span class="line">   &#125;</span><br></pre></td></tr></table></figure></p><h3 id="再来一单"><a href="#再来一单" class="headerlink" title="再来一单"></a>再来一单</h3><h4 id="需求分析和设计-25"><a href="#需求分析和设计-25" class="headerlink" title="需求分析和设计"></a>需求分析和设计</h4><p>在历史订单详情页面，点击再来一单后会将订单数据添加到购物车中，并跳转到购物车页面。<br><div class="img-wrap"><div class="img-bg"><img class="img" src="https://raw.githubusercontent.com/silvan2077/markdown_pic1/main/Picgo/image-20221128173238656.png"/></div></div></p><p>接口设计：参见接口文档<br><div class="img-wrap"><div class="img-bg"><img class="img" src="https://raw.githubusercontent.com/silvan2077/markdown_pic1/main/Picgo/image-20221128173350467.png"/></div></div></p><h4 id="代码实现-8"><a href="#代码实现-8" class="headerlink" title="代码实现"></a>代码实现</h4><p><strong>Controller层</strong><br>在OrderController中添加方法<br><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><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="comment"> * <span class="doctag">@param</span> id</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@return</span></span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="meta">@PostMapping(&quot;/repetition/&#123;id&#125;&quot;)</span></span><br><span class="line"><span class="meta">@ApiOperation(&quot;再来一单&quot;)</span></span><br><span class="line"><span class="keyword">public</span> Result <span class="title function_">repetition</span><span class="params">(<span class="meta">@PathVariable</span> Long id)</span> &#123;</span><br><span class="line">    orderService.repetition(id);</span><br><span class="line">    <span class="keyword">return</span> Result.success();</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><br><strong>Service层</strong><br>在OrderService中添加方法<br><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><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="comment"> * 再来一单</span></span><br><span class="line"><span class="comment"> *</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@param</span> id</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="keyword">void</span> <span class="title function_">repetition</span><span class="params">(Long id)</span>;</span><br></pre></td></tr></table></figure><br>在OrderServiceImpl中实现方法<br><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><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="comment">    * <span class="doctag">@param</span> id</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_">repetition</span><span class="params">(Long id)</span> &#123;</span><br><span class="line">       <span class="comment">// 查询当前用户id</span></span><br><span class="line">       <span class="type">Long</span> <span class="variable">userId</span> <span class="operator">=</span> BaseContext.getCurrentId();</span><br><span class="line"></span><br><span class="line">       <span class="comment">// 根据订单id查询当前订单详情</span></span><br><span class="line">       List&lt;OrderDetail&gt; orderDetailList = orderDetailMapper.getByOrderId(id);</span><br><span class="line"></span><br><span class="line">       <span class="comment">// 将订单详情对象转换为购物车对象</span></span><br><span class="line">       List&lt;ShoppingCart&gt; shoppingCartList = orderDetailList.stream().map(x -&gt; &#123;</span><br><span class="line">           <span class="type">ShoppingCart</span> <span class="variable">shoppingCart</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">ShoppingCart</span>();</span><br><span class="line"></span><br><span class="line">           <span class="comment">// 将原订单详情里面的菜品信息重新复制到购物车对象中</span></span><br><span class="line">           BeanUtils.copyProperties(x, shoppingCart, <span class="string">&quot;id&quot;</span>);</span><br><span class="line">           shoppingCart.setUserId(userId);</span><br><span class="line">           shoppingCart.setCreateTime(LocalDateTime.now());</span><br><span class="line"></span><br><span class="line">           <span class="keyword">return</span> shoppingCart;</span><br><span class="line">       &#125;).collect(Collectors.toList());</span><br><span class="line"></span><br><span class="line">       <span class="comment">// 将购物车对象批量添加到数据库</span></span><br><span class="line">       shoppingCartMapper.insertBatch(shoppingCartList);</span><br><span class="line">   &#125;</span><br></pre></td></tr></table></figure><br><strong>Mapper层</strong><br>在ShoppingCartMapper中添加方法<br><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><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="comment"> * 批量插入购物车数据</span></span><br><span class="line"><span class="comment"> *</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@param</span> shoppingCartList</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="keyword">void</span> <span class="title function_">insertBatch</span><span class="params">(List&lt;ShoppingCart&gt; shoppingCartList)</span>;</span><br></pre></td></tr></table></figure><br>在ShoppingCartMapper的映射文件中添加对应的SQL语句<br><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></pre></td><td class="code"><pre><span class="line"><span class="tag">&lt;<span class="name">insert</span> <span class="attr">id</span>=<span class="string">&quot;insertBatch&quot;</span> <span class="attr">parameterType</span>=<span class="string">&quot;list&quot;</span>&gt;</span></span><br><span class="line">        insert into shopping_cart</span><br><span class="line">        (name, image, user_id, dish_id, setmeal_id, dish_flavor, number, amount, create_time)</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;shoppingCartList&quot;</span> <span class="attr">item</span>=<span class="string">&quot;sc&quot;</span> <span class="attr">separator</span>=<span class="string">&quot;,&quot;</span>&gt;</span></span><br><span class="line">            (#&#123;sc.name&#125;,#&#123;sc.image&#125;,#&#123;sc.userId&#125;,#&#123;sc.dishId&#125;,#&#123;sc.setmealId&#125;,#&#123;sc.dishFlavor&#125;,#&#123;sc.number&#125;,#&#123;sc.amount&#125;,#&#123;sc.createTime&#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></pre></td></tr></table></figure></p><h2 id="商家端订单管理模块"><a href="#商家端订单管理模块" class="headerlink" title="商家端订单管理模块"></a>商家端订单管理模块</h2><h3 id="订单搜索"><a href="#订单搜索" class="headerlink" title="订单搜索"></a>订单搜索</h3><h4 id="需求分析和设计-26"><a href="#需求分析和设计-26" class="headerlink" title="需求分析和设计"></a>需求分析和设计</h4><div class="tabs" id="订单搜索需求分析"><ul class="nav-tabs"><li class="tab active"><button type="button" data-href="#订单搜索需求分析-1">全部订单</button></li><li class="tab"><button type="button" data-href="#订单搜索需求分析-2">待接单</button></li><li class="tab"><button type="button" data-href="#订单搜索需求分析-3">待派送</button></li><li class="tab"><button type="button" data-href="#订单搜索需求分析-4">派送中</button></li><li class="tab"><button type="button" data-href="#订单搜索需求分析-5">已完成</button></li><li class="tab"><button type="button" data-href="#订单搜索需求分析-6">已取消</button></li></ul><div class="tab-contents"><div class="tab-item-content active" id="订单搜索需求分析-1"><div class="img-wrap"><div class="img-bg"><img class="img" src="https://raw.githubusercontent.com/silvan2077/markdown_pic1/main/Picgo/image-20221129092023177.png"/></div></div><button type="button" class="tab-to-top" aria-label="scroll to top"><i class="fas fa-arrow-up"></i></button></div><div class="tab-item-content" id="订单搜索需求分析-2"><div class="img-wrap"><div class="img-bg"><img class="img" src="https://raw.githubusercontent.com/silvan2077/markdown_pic1/main/Picgo/image-20221129114035570.png"/></div></div><button type="button" class="tab-to-top" aria-label="scroll to top"><i class="fas fa-arrow-up"></i></button></div><div class="tab-item-content" id="订单搜索需求分析-3"><div class="img-wrap"><div class="img-bg"><img class="img" src="https://raw.githubusercontent.com/silvan2077/markdown_pic1/main/Picgo/image-20221129114054664.png"/></div></div><button type="button" class="tab-to-top" aria-label="scroll to top"><i class="fas fa-arrow-up"></i></button></div><div class="tab-item-content" id="订单搜索需求分析-4"><div class="img-wrap"><div class="img-bg"><img class="img" src="https://raw.githubusercontent.com/silvan2077/markdown_pic1/main/Picgo/image-20221129114116492.png"/></div></div><button type="button" class="tab-to-top" aria-label="scroll to top"><i class="fas fa-arrow-up"></i></button></div><div class="tab-item-content" id="订单搜索需求分析-5"><div class="img-wrap"><div class="img-bg"><img class="img" src="https://raw.githubusercontent.com/silvan2077/markdown_pic1/main/Picgo/image-20221129114132956.png"/></div></div><button type="button" class="tab-to-top" aria-label="scroll to top"><i class="fas fa-arrow-up"></i></button></div><div class="tab-item-content" id="订单搜索需求分析-6"><div class="img-wrap"><div class="img-bg"><img class="img" src="https://raw.githubusercontent.com/silvan2077/markdown_pic1/main/Picgo/image-20221129114151055.png"/></div></div><button type="button" class="tab-to-top" aria-label="scroll to top"><i class="fas fa-arrow-up"></i></button></div></div></div><div class="note info no-icon flat"><p>业务规则：</p><ul><li>输入订单号/手机号进行搜索，支持模糊搜索</li><li>根据订单状态进行筛选</li><li>下单时间进行时间筛选</li><li>搜索内容为空，提示未找到相关订单</li><li>搜索结果页，展示包含搜索关键词的内容</li><li>分页展示搜索到的订单数据</li></ul></div><p>接口设计：参见接口文档<br><div class="img-wrap"><div class="img-bg"><img class="img" src="https://raw.githubusercontent.com/silvan2077/markdown_pic1/main/Picgo/image-20221129092552620.png"/></div></div></p><h4 id="代码实现-9"><a href="#代码实现-9" class="headerlink" title="代码实现"></a>代码实现</h4><p><strong>Controller层</strong><br>在admin包下创建OrderController<br><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><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"> * 订单管理</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="meta">@RestController(&quot;adminOrderController&quot;)</span></span><br><span class="line"><span class="meta">@RequestMapping(&quot;/admin/order&quot;)</span></span><br><span class="line"><span class="meta">@Slf4j</span></span><br><span class="line"><span class="meta">@Api(tags = &quot;订单管理接口&quot;)</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">OrderController</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="comment">     * <span class="doctag">@param</span> ordersPageQueryDTO</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@return</span></span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    <span class="meta">@GetMapping(&quot;/conditionSearch&quot;)</span></span><br><span class="line">    <span class="meta">@ApiOperation(&quot;订单搜索&quot;)</span></span><br><span class="line">    <span class="keyword">public</span> Result&lt;PageResult&gt; <span class="title function_">conditionSearch</span><span class="params">(OrdersPageQueryDTO ordersPageQueryDTO)</span> &#123;</span><br><span class="line">        <span class="type">PageResult</span> <span class="variable">pageResult</span> <span class="operator">=</span> orderService.conditionSearch(ordersPageQueryDTO);</span><br><span class="line">        <span class="keyword">return</span> Result.success(pageResult);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><br><strong>Service层</strong><br>在OrderService中添加以下方法<br><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><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="comment"> * 条件搜索订单</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@param</span> ordersPageQueryDTO</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@return</span></span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line">PageResult <span class="title function_">conditionSearch</span><span class="params">(OrdersPageQueryDTO ordersPageQueryDTO)</span>;</span><br></pre></td></tr></table></figure><br>在OrderServiceImpl中实现方法<br><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><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="comment"> * 订单搜索</span></span><br><span class="line"><span class="comment"> *</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@param</span> ordersPageQueryDTO</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@return</span></span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="keyword">public</span> PageResult <span class="title function_">conditionSearch</span><span class="params">(OrdersPageQueryDTO ordersPageQueryDTO)</span> &#123;</span><br><span class="line">    PageHelper.startPage(ordersPageQueryDTO.getPage(), ordersPageQueryDTO.getPageSize());</span><br><span class="line"></span><br><span class="line">    Page&lt;Orders&gt; page = orderMapper.pageQuery(ordersPageQueryDTO);</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 部分订单状态，需要额外返回订单菜品信息，将Orders转化为OrderVO</span></span><br><span class="line">    List&lt;OrderVO&gt; orderVOList = getOrderVOList(page);</span><br><span class="line"></span><br><span class="line">    <span class="keyword">return</span> <span class="keyword">new</span> <span class="title class_">PageResult</span>(page.getTotal(), orderVOList);</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="keyword">private</span> List&lt;OrderVO&gt; <span class="title function_">getOrderVOList</span><span class="params">(Page&lt;Orders&gt; page)</span> &#123;</span><br><span class="line">    <span class="comment">// 需要返回订单菜品信息，自定义OrderVO响应结果</span></span><br><span class="line">    List&lt;OrderVO&gt; orderVOList = <span class="keyword">new</span> <span class="title class_">ArrayList</span>&lt;&gt;();</span><br><span class="line"></span><br><span class="line">    List&lt;Orders&gt; ordersList = page.getResult();</span><br><span class="line">    <span class="keyword">if</span> (!CollectionUtils.isEmpty(ordersList)) &#123;</span><br><span class="line">        <span class="keyword">for</span> (Orders orders : ordersList) &#123;</span><br><span class="line">            <span class="comment">// 将共同字段复制到OrderVO</span></span><br><span class="line">            <span class="type">OrderVO</span> <span class="variable">orderVO</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">OrderVO</span>();</span><br><span class="line">            BeanUtils.copyProperties(orders, orderVO);</span><br><span class="line">            <span class="type">String</span> <span class="variable">orderDishes</span> <span class="operator">=</span> getOrderDishesStr(orders);</span><br><span class="line"></span><br><span class="line">            <span class="comment">// 将订单菜品信息封装到orderVO中，并添加到orderVOList</span></span><br><span class="line">            orderVO.setOrderDishes(orderDishes);</span><br><span class="line">            orderVOList.add(orderVO);</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">return</span> orderVOList;</span><br><span 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="comment"> * <span class="doctag">@param</span> orders</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@return</span></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_">getOrderDishesStr</span><span class="params">(Orders orders)</span> &#123;</span><br><span class="line">    <span class="comment">// 查询订单菜品详情信息（订单中的菜品和数量）</span></span><br><span class="line">    List&lt;OrderDetail&gt; orderDetailList = orderDetailMapper.getByOrderId(orders.getId());</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 将每一条订单菜品信息拼接为字符串（格式：宫保鸡丁*3；）</span></span><br><span class="line">    List&lt;String&gt; orderDishList = orderDetailList.stream().map(x -&gt; &#123;</span><br><span class="line">        <span class="type">String</span> <span class="variable">orderDish</span> <span class="operator">=</span> x.getName() + <span class="string">&quot;*&quot;</span> + x.getNumber() + <span class="string">&quot;;&quot;</span>;</span><br><span class="line">        <span class="keyword">return</span> orderDish;</span><br><span class="line">    &#125;).collect(Collectors.toList());</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 将该订单对应的所有菜品信息拼接在一起</span></span><br><span class="line">    <span class="keyword">return</span> String.join(<span class="string">&quot;&quot;</span>, orderDishList);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></p><h3 id="各个状态的订单数量统计"><a href="#各个状态的订单数量统计" class="headerlink" title="各个状态的订单数量统计"></a>各个状态的订单数量统计</h3><h4 id="需求分析和设计-27"><a href="#需求分析和设计-27" class="headerlink" title="需求分析和设计"></a>需求分析和设计</h4><div class="img-wrap"><div class="img-bg"><img class="img" src="https://raw.githubusercontent.com/silvan2077/markdown_pic1/main/Picgo/image-20221129095804419.png"/></div></div><p>接口设计：参见接口文档<br><div class="img-wrap"><div class="img-bg"><img class="img" src="https://raw.githubusercontent.com/silvan2077/markdown_pic1/main/Picgo/image-20221129095912896.png"/></div></div></p><h4 id="代码实现-10"><a href="#代码实现-10" class="headerlink" title="代码实现"></a>代码实现</h4><p><strong>Controller层</strong><br>在admin包下的OrderController类中添加方法<br><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><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="comment"> * <span class="doctag">@return</span></span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="meta">@GetMapping(&quot;/statistics&quot;)</span></span><br><span class="line"><span class="meta">@ApiOperation(&quot;各个状态的订单数量统计&quot;)</span></span><br><span class="line"><span class="keyword">public</span> Result&lt;OrderStatisticsVO&gt; <span class="title function_">statistics</span><span class="params">()</span> &#123;</span><br><span class="line">    <span class="type">OrderStatisticsVO</span> <span class="variable">orderStatisticsVO</span> <span class="operator">=</span> orderService.statistics();</span><br><span class="line">    <span class="keyword">return</span> Result.success(orderStatisticsVO);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></p><p><strong>Service层</strong></p><p>在OrderService中添加以下方法<br><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><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="comment"> * 各个状态的订单数量统计</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@return</span></span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line">OrderStatisticsVO <span class="title function_">statistics</span><span class="params">()</span>;</span><br></pre></td></tr></table></figure></p><p>在OrderServiceImpl中实现方法</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></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">@return</span></span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="keyword">public</span> OrderStatisticsVO <span class="title function_">statistics</span><span class="params">()</span> &#123;</span><br><span class="line">    <span class="comment">// 根据状态，分别查询出待接单、待派送、派送中的订单数量</span></span><br><span class="line">    <span class="type">Integer</span> <span class="variable">toBeConfirmed</span> <span class="operator">=</span> orderMapper.countStatus(Orders.TO_BE_CONFIRMED);</span><br><span class="line">    <span class="type">Integer</span> <span class="variable">confirmed</span> <span class="operator">=</span> orderMapper.countStatus(Orders.CONFIRMED);</span><br><span class="line">    <span class="type">Integer</span> <span class="variable">deliveryInProgress</span> <span class="operator">=</span> orderMapper.countStatus(Orders.DELIVERY_IN_PROGRESS);</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 将查询出的数据封装到orderStatisticsVO中响应</span></span><br><span class="line">    <span class="type">OrderStatisticsVO</span> <span class="variable">orderStatisticsVO</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">OrderStatisticsVO</span>();</span><br><span class="line">    orderStatisticsVO.setToBeConfirmed(toBeConfirmed);</span><br><span class="line">    orderStatisticsVO.setConfirmed(confirmed);</span><br><span class="line">    orderStatisticsVO.setDeliveryInProgress(deliveryInProgress);</span><br><span class="line">    <span class="keyword">return</span> orderStatisticsVO;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p><strong>Mapper层</strong></p><p>在OrderMapper中添加以下方法<br><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><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="comment"> * 根据状态统计订单数量</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@param</span> status</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="meta">@Select(&quot;select count(id) from orders where status = #&#123;status&#125;&quot;)</span></span><br><span class="line">Integer <span class="title function_">countStatus</span><span class="params">(Integer status)</span>;</span><br></pre></td></tr></table></figure></p><h3 id="查询订单详情-1"><a href="#查询订单详情-1" class="headerlink" title="查询订单详情"></a>查询订单详情</h3><h4 id="需求分析和设计-28"><a href="#需求分析和设计-28" class="headerlink" title="需求分析和设计"></a>需求分析和设计</h4><p>产品原型：</p><div class="img-wrap"><div class="img-bg"><img class="img" src="https://raw.githubusercontent.com/silvan2077/markdown_pic1/main/Picgo/image-20221129101712084.png"/></div></div><div class="note info no-icon flat"><p>业务规则：</p><ul><li>订单详情页面需要展示订单基本信息（状态、订单号、下单时间、收货人、电话、收货地址、金额等）</li><li>订单详情页面需要展示订单明细数据（商品名称、数量、单价）</li></ul></div><p>接口设计：参见接口文档<br><div class="img-wrap"><div class="img-bg"><img class="img" src="https://raw.githubusercontent.com/silvan2077/markdown_pic1/main/Picgo/image-20221129101035374.png"/></div></div></p><h4 id="代码实现-11"><a href="#代码实现-11" class="headerlink" title="代码实现"></a>代码实现</h4><p>在OrderController中添加方法<br><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><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="comment"> * <span class="doctag">@param</span> id</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@return</span></span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="meta">@GetMapping(&quot;/details/&#123;id&#125;&quot;)</span></span><br><span class="line"><span class="meta">@ApiOperation(&quot;查询订单详情&quot;)</span></span><br><span class="line"><span class="keyword">public</span> Result&lt;OrderVO&gt; <span class="title function_">details</span><span class="params">(<span class="meta">@PathVariable(&quot;id&quot;)</span> Long id)</span> &#123;</span><br><span class="line">    <span class="type">OrderVO</span> <span class="variable">orderVO</span> <span class="operator">=</span> orderService.details(id);</span><br><span class="line">    <span class="keyword">return</span> Result.success(orderVO);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><br><strong>Service层</strong><br>在OrderService中添加方法</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></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">@param</span> id</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@return</span></span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line">OrderVO <span class="title function_">details</span><span class="params">(Long id)</span>;</span><br></pre></td></tr></table></figure><p>在OrderServiceImpl中实现方法<br><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><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="comment"> * <span class="doctag">@param</span> id</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@return</span></span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="keyword">public</span> OrderVO <span class="title function_">details</span><span class="params">(Long id)</span> &#123;</span><br><span class="line">    <span class="comment">// 根据id查询订单</span></span><br><span class="line">    <span class="type">Orders</span> <span class="variable">orders</span> <span class="operator">=</span> orderMapper.getById(id);</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 查询该订单对应的菜品/套餐明细</span></span><br><span class="line">    List&lt;OrderDetail&gt; orderDetailList = orderDetailMapper.getByOrderId(orders.getId());</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 将该订单及其详情封装到OrderVO并返回</span></span><br><span class="line">    <span class="type">OrderVO</span> <span class="variable">orderVO</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">OrderVO</span>();</span><br><span class="line">    BeanUtils.copyProperties(orders, orderVO);</span><br><span class="line">    orderVO.setOrderDetailList(orderDetailList);</span><br><span class="line"></span><br><span class="line">    <span class="keyword">return</span> orderVO;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><br><strong>Mapper层</strong><br>在OrderMapper中添加方法<br><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><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="comment"> * 根据id查询订单</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@param</span> id</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="meta">@Select(&quot;select * from orders where id=#&#123;id&#125;&quot;)</span></span><br><span class="line">Orders <span class="title function_">getById</span><span class="params">(Long id)</span>;</span><br></pre></td></tr></table></figure></p><h3 id="接单"><a href="#接单" class="headerlink" title="接单"></a>接单</h3><h4 id="需求分析和设计-29"><a href="#需求分析和设计-29" class="headerlink" title="需求分析和设计"></a>需求分析和设计</h4><div class="tabs" id="接单-需求分析"><ul class="nav-tabs"><li class="tab active"><button type="button" data-href="#接单-需求分析-1">订单管理</button></li><li class="tab"><button type="button" data-href="#接单-需求分析-2">页面详情</button></li></ul><div class="tab-contents"><div class="tab-item-content active" id="接单-需求分析-1"><div class="img-wrap"><div class="img-bg"><img class="img" src="https://raw.githubusercontent.com/silvan2077/markdown_pic1/main/Picgo/image-20221129105142623.png"/></div></div><button type="button" class="tab-to-top" aria-label="scroll to top"><i class="fas fa-arrow-up"></i></button></div><div class="tab-item-content" id="接单-需求分析-2"><div class="img-wrap"><div class="img-bg"><img class="img" src="https://raw.githubusercontent.com/silvan2077/markdown_pic1/main/Picgo/image-20221129105116285.png"/></div></div><button type="button" class="tab-to-top" aria-label="scroll to top"><i class="fas fa-arrow-up"></i></button></div></div></div><div class="note info no-icon flat"><p>业务规则：</p><ul><li>商家接单其实就是将订单的状态修改为“已接单”</li></ul></div><p>接口设计：参见接口文档<br><div class="img-wrap"><div class="img-bg"><img class="img" src="https://raw.githubusercontent.com/silvan2077/markdown_pic1/main/Picgo/image-20221129105313172.png"/></div></div></p><h4 id="代码实现-12"><a href="#代码实现-12" class="headerlink" title="代码实现"></a>代码实现</h4><p><strong>Controller层</strong><br>在OrderController中添加方法<br><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><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="comment"> * <span class="doctag">@return</span></span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="meta">@PutMapping(&quot;/confirm&quot;)</span></span><br><span class="line"><span class="meta">@ApiOperation(&quot;接单&quot;)</span></span><br><span class="line"><span class="keyword">public</span> Result <span class="title function_">confirm</span><span class="params">(<span class="meta">@RequestBody</span> OrdersConfirmDTO ordersConfirmDTO)</span> &#123;</span><br><span class="line">    orderService.confirm(ordersConfirmDTO);</span><br><span class="line">    <span class="keyword">return</span> Result.success();</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></p><p><strong>Service层</strong><br>在OrderService中添加方法<br><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><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="comment"> * 接单</span></span><br><span class="line"><span class="comment"> *</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@param</span> ordersConfirmDTO</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="keyword">void</span> <span class="title function_">confirm</span><span class="params">(OrdersConfirmDTO ordersConfirmDTO)</span>;</span><br></pre></td></tr></table></figure></p><p>在OrderServiceImpl中实现方法<br><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><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="comment"> * 接单</span></span><br><span class="line"><span class="comment"> *</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@param</span> ordersConfirmDTO</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_">confirm</span><span class="params">(OrdersConfirmDTO ordersConfirmDTO)</span> &#123;</span><br><span class="line">    <span class="type">Orders</span> <span class="variable">orders</span> <span class="operator">=</span> Orders.builder()</span><br><span class="line">            .id(ordersConfirmDTO.getId())</span><br><span class="line">            .status(Orders.CONFIRMED)</span><br><span class="line">            .build();</span><br><span class="line"></span><br><span class="line">    orderMapper.update(orders);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></p><h3 id="拒单"><a href="#拒单" class="headerlink" title="拒单"></a>拒单</h3><h4 id="需求分析和设计-30"><a href="#需求分析和设计-30" class="headerlink" title="需求分析和设计"></a>需求分析和设计</h4><div class="tabs" id="拒单需求分析"><ul class="nav-tabs"><li class="tab active"><button type="button" data-href="#拒单需求分析-1">订单管理</button></li></ul><div class="tab-contents"><div class="tab-item-content active" id="拒单需求分析-1"><div class="img-wrap"><div class="img-bg"><img class="img" src="https://raw.githubusercontent.com/silvan2077/markdown_pic1/main/Picgo/image-20221129110358976.png"/></div></div><button type="button" class="tab-to-top" aria-label="scroll to top"><i class="fas fa-arrow-up"></i></button></div></div></div><div class="note info no-icon flat"><p>业务规则：</p><ul><li>商家拒单其实就是将订单状态修改为“已取消”</li><li>只有订单处于“待接单”状态时可以执行拒单操作</li><li>商家拒单时需要指定拒单原因</li><li>商家拒单时，如果用户已经完成了支付，需要为用户退款</li></ul></div><p>接口设计：参见接口文档<br><div class="img-wrap"><div class="img-bg"><img class="img" src="https://raw.githubusercontent.com/silvan2077/markdown_pic1/main/Picgo/image-20221129110725031.png"/></div></div></p><h4 id="代码实现-13"><a href="#代码实现-13" class="headerlink" title="代码实现"></a>代码实现</h4><p><strong>Controller层</strong><br>在DishController中创建方法<br><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><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="comment"> * <span class="doctag">@return</span></span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="meta">@PutMapping(&quot;/rejection&quot;)</span></span><br><span class="line"><span class="meta">@ApiOperation(&quot;拒单&quot;)</span></span><br><span class="line"><span class="keyword">public</span> Result <span class="title function_">rejection</span><span class="params">(<span class="meta">@RequestBody</span> OrdersRejectionDTO ordersRejectionDTO)</span> <span class="keyword">throws</span> Exception &#123;</span><br><span class="line">    orderService.rejection(ordersRejectionDTO);</span><br><span class="line">    <span class="keyword">return</span> Result.success();</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></p><p><strong>Service层</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></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">@param</span> ordersRejectionDTO</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="keyword">void</span> <span class="title function_">rejection</span><span class="params">(OrdersRejectionDTO ordersRejectionDTO)</span> <span class="keyword">throws</span> Exception;</span><br></pre></td></tr></table></figure><p>在OrderServiceImpl中实现方法</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></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">@param</span> ordersRejectionDTO</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_">rejection</span><span class="params">(OrdersRejectionDTO ordersRejectionDTO)</span> <span class="keyword">throws</span> Exception &#123;</span><br><span class="line">       <span class="comment">// 根据id查询订单</span></span><br><span class="line">       <span class="type">Orders</span> <span class="variable">ordersDB</span> <span class="operator">=</span> orderMapper.getById(ordersRejectionDTO.getId());</span><br><span class="line"></span><br><span class="line">       <span class="comment">// 订单只有存在且状态为2（待接单）才可以拒单</span></span><br><span class="line">       <span class="keyword">if</span> (ordersDB == <span class="literal">null</span> || !ordersDB.getStatus().equals(Orders.TO_BE_CONFIRMED)) &#123;</span><br><span class="line">           <span class="keyword">throw</span> <span class="keyword">new</span> <span class="title class_">OrderBusinessException</span>(MessageConstant.ORDER_STATUS_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="type">Integer</span> <span class="variable">payStatus</span> <span class="operator">=</span> ordersDB.getPayStatus();</span><br><span class="line">       <span class="type">Orders</span> <span class="variable">orders</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">Orders</span>();</span><br><span class="line">       <span class="keyword">if</span> (payStatus == Orders.PAID) &#123;</span><br><span class="line">           <span class="comment">//用户已支付，需要退款</span></span><br><span class="line">           <span class="comment">// String refund = weChatPayUtil.refund(</span></span><br><span class="line">           <span class="comment">//         ordersDB.getNumber(),</span></span><br><span class="line">           <span class="comment">//         ordersDB.getNumber(),</span></span><br><span class="line">           <span class="comment">//         new BigDecimal(0.01),</span></span><br><span class="line">           <span class="comment">//         new BigDecimal(0.01));</span></span><br><span class="line">           <span class="comment">// log.info(&quot;申请退款：&#123;&#125;&quot;, refund);</span></span><br><span class="line"></span><br><span class="line"></span><br><span class="line">           <span class="comment">// 拒单需要退款，根据订单id更新订单状态、拒单原因、取消时间</span></span><br><span class="line">       </span><br><span class="line">       orders.setId(ordersDB.getId());</span><br><span class="line">       orders.setStatus(Orders.CANCELLED);</span><br><span class="line">       orders.setRejectionReason(ordersRejectionDTO.getRejectionReason());</span><br><span class="line">       orders.setCancelTime(LocalDateTime.now());</span><br><span class="line">       &#125;</span><br><span class="line"></span><br><span class="line">       </span><br><span class="line"></span><br><span class="line">       orderMapper.update(orders);</span><br><span class="line">   &#125;</span><br></pre></td></tr></table></figure><h3 id="取消订单-1"><a href="#取消订单-1" class="headerlink" title="取消订单"></a>取消订单</h3><h4 id="需求分析和设计-31"><a href="#需求分析和设计-31" class="headerlink" title="需求分析和设计"></a>需求分析和设计</h4><p>产品原型：<br><div class="img-wrap"><div class="img-bg"><img class="img" src="https://raw.githubusercontent.com/silvan2077/markdown_pic1/main/Picgo/image-20221129111402099.png"/></div></div></p><div class="note info no-icon flat"><p>业务规则：</p><ul><li>取消订单其实就是将订单状态修改为“已取消”</li><li>商家取消订单时需要指定取消原因</li><li>商家取消订单时，如果用户已经完成了支付，需要为用户退款</li></ul></div><p>接口设计：参见接口文档<br><div class="img-wrap"><div class="img-bg"><img class="img" src="https://raw.githubusercontent.com/silvan2077/markdown_pic1/main/Picgo/image-20221129112201836.png"/></div></div></p><h4 id="代码实现-14"><a href="#代码实现-14" class="headerlink" title="代码实现"></a>代码实现</h4><p><strong>Controller层</strong><br><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><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="comment"> * <span class="doctag">@return</span></span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="meta">@PutMapping(&quot;/cancel&quot;)</span></span><br><span class="line"><span class="meta">@ApiOperation(&quot;取消订单&quot;)</span></span><br><span class="line"><span class="keyword">public</span> Result <span class="title function_">cancel</span><span class="params">(<span class="meta">@RequestBody</span> OrdersCancelDTO ordersCancelDTO)</span> <span class="keyword">throws</span> Exception &#123;</span><br><span class="line">    orderService.cancel(ordersCancelDTO);</span><br><span class="line">    <span class="keyword">return</span> Result.success();</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></p><p><strong>Service层</strong><br>在OrderService中添加方法<br><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><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="comment"> * 商家取消订单</span></span><br><span class="line"><span class="comment"> *</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@param</span> ordersCancelDTO</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="keyword">void</span> <span class="title function_">cancel</span><span class="params">(OrdersCancelDTO ordersCancelDTO)</span> <span class="keyword">throws</span> Exception;</span><br></pre></td></tr></table></figure><br>在OrderServiceImpl中实现方法</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 class="doctag">@param</span> ordersCancelDTO</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_">cancel</span><span class="params">(OrdersCancelDTO ordersCancelDTO)</span> <span class="keyword">throws</span> Exception &#123;</span><br><span class="line">       <span class="comment">// 根据id查询订单</span></span><br><span class="line">       <span class="type">Orders</span> <span class="variable">ordersDB</span> <span class="operator">=</span> orderMapper.getById(ordersCancelDTO.getId());</span><br><span class="line"></span><br><span class="line">       <span class="comment">// 管理端取消订单需要退款，根据订单id更新订单状态、取消原因、取消时间</span></span><br><span class="line">       <span class="type">Orders</span> <span class="variable">orders</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">Orders</span>();</span><br><span class="line">       <span class="comment">//支付状态</span></span><br><span class="line">       <span class="type">Integer</span> <span class="variable">payStatus</span> <span class="operator">=</span> ordersDB.getPayStatus();</span><br><span class="line">       <span class="keyword">if</span> (payStatus == <span class="number">1</span>) &#123;</span><br><span class="line">           <span class="comment">//用户已支付，需要退款</span></span><br><span class="line">           <span class="comment">// String refund = weChatPayUtil.refund(</span></span><br><span class="line">           <span class="comment">//         ordersDB.getNumber(),</span></span><br><span class="line">           <span class="comment">//         ordersDB.getNumber(),</span></span><br><span class="line">           <span class="comment">//         new BigDecimal(0.01),</span></span><br><span class="line">           <span class="comment">//         new BigDecimal(0.01));</span></span><br><span class="line">           <span class="comment">// log.info(&quot;申请退款：&#123;&#125;&quot;, refund);</span></span><br><span class="line"></span><br><span class="line"></span><br><span class="line">           orders.setId(ordersCancelDTO.getId());</span><br><span class="line">           orders.setStatus(Orders.CANCELLED);</span><br><span class="line">           orders.setCancelReason(ordersCancelDTO.getCancelReason());</span><br><span class="line">           orders.setCancelTime(LocalDateTime.now());</span><br><span class="line">       &#125;</span><br><span class="line"></span><br><span class="line">       </span><br><span class="line">       </span><br><span class="line">       orderMapper.update(orders);</span><br><span class="line">   &#125;</span><br></pre></td></tr></table></figure><h3 id="派送订单"><a href="#派送订单" class="headerlink" title="派送订单"></a>派送订单</h3><h4 id="需求分析和设计-32"><a href="#需求分析和设计-32" class="headerlink" title="需求分析和设计"></a>需求分析和设计</h4><p>产品原型：</p><div class="img-wrap"><div class="img-bg"><img class="img" src="https://raw.githubusercontent.com/silvan2077/markdown_pic1/main/Picgo/image-20221129113257201.png"/></div></div><div class="note info no-icon flat"><p>业务规则：</p><ul><li>派送订单其实就是将订单状态修改为“派送中”</li><li>只有状态为“待派送”的订单可以执行派送订单操作</li></ul></div><p>接口设计：参见接口文档<br><div class="img-wrap"><div class="img-bg"><img class="img" src="https://raw.githubusercontent.com/silvan2077/markdown_pic1/main/Picgo/image-20221129113449124.png"/></div></div></p><h4 id="代码实现-15"><a href="#代码实现-15" class="headerlink" title="代码实现"></a>代码实现</h4><p><strong>Controller层</strong><br>在OrderController中添加方法<br><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><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="comment"> * <span class="doctag">@return</span></span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="meta">@PutMapping(&quot;/delivery/&#123;id&#125;&quot;)</span></span><br><span class="line"><span class="meta">@ApiOperation(&quot;派送订单&quot;)</span></span><br><span class="line"><span class="keyword">public</span> Result <span class="title function_">delivery</span><span class="params">(<span class="meta">@PathVariable(&quot;id&quot;)</span> Long id)</span> &#123;</span><br><span class="line">    orderService.delivery(id);</span><br><span class="line">    <span class="keyword">return</span> Result.success();</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></p><p><strong>Service层</strong><br>在OrderService中添加方法<br><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><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="comment"> * 派送订单</span></span><br><span class="line"><span class="comment"> *</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@param</span> id</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="keyword">void</span> <span class="title function_">delivery</span><span class="params">(Long id)</span>;</span><br></pre></td></tr></table></figure></p><p>在OrderServiceImpl中实现方法<br><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><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="comment"> * <span class="doctag">@param</span> id</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_">delivery</span><span class="params">(Long id)</span> &#123;</span><br><span class="line">    <span class="comment">// 根据id查询订单</span></span><br><span class="line">    <span class="type">Orders</span> <span class="variable">ordersDB</span> <span class="operator">=</span> orderMapper.getById(id);</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> (ordersDB == <span class="literal">null</span> || !ordersDB.getStatus().equals(Orders.CONFIRMED)) &#123;</span><br><span class="line">        <span class="keyword">throw</span> <span class="keyword">new</span> <span class="title class_">OrderBusinessException</span>(MessageConstant.ORDER_STATUS_ERROR);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="type">Orders</span> <span class="variable">orders</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">Orders</span>();</span><br><span class="line">    orders.setId(ordersDB.getId());</span><br><span class="line">    <span class="comment">// 更新订单状态,状态转为派送中</span></span><br><span class="line">    orders.setStatus(Orders.DELIVERY_IN_PROGRESS);</span><br><span class="line"></span><br><span class="line">    orderMapper.update(orders);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></p><h3 id="完成订单"><a href="#完成订单" class="headerlink" title="完成订单"></a>完成订单</h3><h4 id="需求分析和设计-33"><a href="#需求分析和设计-33" class="headerlink" title="需求分析和设计"></a>需求分析和设计</h4><div class="img-wrap"><div class="img-bg"><img class="img" src="https://raw.githubusercontent.com/silvan2077/markdown_pic1/main/Picgo/image-20221129112554051.png"/></div></div><div class="note info no-icon flat"><p>业务规则：</p><ul><li>完成订单其实就是将订单状态修改为“已完成”</li><li>只有状态为“派送中”的订单可以执行订单完成操作</li></ul></div><p>接口设计：参见接口文档<br><div class="img-wrap"><div class="img-bg"><img class="img" src="https://raw.githubusercontent.com/silvan2077/markdown_pic1/main/Picgo/image-20221129113622784.png"/></div></div></p><h4 id="代码实现-16"><a href="#代码实现-16" class="headerlink" title="代码实现"></a>代码实现</h4><p><strong>Controller层</strong><br>在OrderController中添加方法<br><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><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="comment"> * <span class="doctag">@return</span></span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="meta">@PutMapping(&quot;/complete/&#123;id&#125;&quot;)</span></span><br><span class="line"><span class="meta">@ApiOperation(&quot;完成订单&quot;)</span></span><br><span class="line"><span class="keyword">public</span> Result <span class="title function_">complete</span><span class="params">(<span class="meta">@PathVariable(&quot;id&quot;)</span> Long id)</span> &#123;</span><br><span class="line">    orderService.complete(id);</span><br><span class="line">    <span class="keyword">return</span> Result.success();</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><br><strong>Service层</strong><br>在OrderService中添加方法<br><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><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="comment"> * 完成订单</span></span><br><span class="line"><span class="comment"> *</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@param</span> id</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="keyword">void</span> <span class="title function_">complete</span><span class="params">(Long id)</span>;</span><br></pre></td></tr></table></figure></p><p>在OrderServiceImpl中实现方法<br><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><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"> * 完成订单</span></span><br><span class="line"><span class="comment"> *</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@param</span> id</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_">complete</span><span class="params">(Long id)</span> &#123;</span><br><span class="line">    <span class="comment">// 根据id查询订单</span></span><br><span class="line">    <span class="type">Orders</span> <span class="variable">ordersDB</span> <span class="operator">=</span> orderMapper.getById(id);</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 校验订单是否存在，并且状态为4</span></span><br><span class="line">    <span class="keyword">if</span> (ordersDB == <span class="literal">null</span> || !ordersDB.getStatus().equals(Orders.DELIVERY_IN_PROGRESS)) &#123;</span><br><span class="line">        <span class="keyword">throw</span> <span class="keyword">new</span> <span class="title class_">OrderBusinessException</span>(MessageConstant.ORDER_STATUS_ERROR);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="type">Orders</span> <span class="variable">orders</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">Orders</span>();</span><br><span class="line">    orders.setId(ordersDB.getId());</span><br><span class="line">    <span class="comment">// 更新订单状态,状态转为完成</span></span><br><span class="line">    orders.setStatus(Orders.COMPLETED);</span><br><span class="line">    orders.setDeliveryTime(LocalDateTime.now());</span><br><span class="line"></span><br><span class="line">    orderMapper.update(orders);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></p><h2 id="校验收货地址是否超出配送范围"><a href="#校验收货地址是否超出配送范围" class="headerlink" title="校验收货地址是否超出配送范围"></a>校验收货地址是否超出配送范围</h2><h2 id="1-环境准备"><a href="#1-环境准备" class="headerlink" title="1. 环境准备"></a>1. 环境准备</h2><p>注册账号：<a href="https://passport.baidu.com/v2/?reg&amp;tt=1671699340600&amp;overseas=&amp;gid=CF954C2-A3D2-417F-9FE6-B0F249ED7E33&amp;tpl=pp&amp;u=https%3A%2F%2Flbsyun.baidu.com%2Findex.php%3Ftitle%3D%E9%A6%96%E9%A1%B5">https://passport.baidu.com/v2/?reg&amp;tt=1671699340600&amp;overseas=&amp;gid=CF954C2-A3D2-417F-9FE6-B0F249ED7E33&amp;tpl=pp&amp;u=https%3A%2F%2Flbsyun.baidu.com%2Findex.php%3Ftitle%3D%E9%A6%96%E9%A1%B5</a></p><p>登录百度地图开放平台：<a href="https://lbsyun.baidu.com/">https://lbsyun.baidu.com/</a></p><p>进入控制台，创建应用，获取AK：</p><p><img src="assets/image-20221222170049729.png" alt="image-20221222170049729"></p><p><img src="assets/image-20221222170256927.png" alt="image-20221222170256927"></p><p>相关接口:</p><p><a href="https://lbsyun.baidu.com/index.php?title=webapi/guide/webservice-geocoding">https://lbsyun.baidu.com/index.php?title=webapi/guide/webservice-geocoding</a></p><p><a href="https://lbsyun.baidu.com/index.php?title=webapi/directionlite-v1">https://lbsyun.baidu.com/index.php?title=webapi/directionlite-v1</a></p><h2 id="2-代码开发"><a href="#2-代码开发" class="headerlink" title="2. 代码开发"></a>2. 代码开发</h2><h3 id="2-1-application-yml"><a href="#2-1-application-yml" class="headerlink" title="2.1 application.yml"></a>2.1 application.yml</h3><p>配置外卖商家店铺地址和百度地图的AK：</p><p><img src="assets/image-20221222170819582.png" alt="image-20221222170819582"></p><h3 id="2-2-OrderServiceImpl"><a href="#2-2-OrderServiceImpl" class="headerlink" title="2.2 OrderServiceImpl"></a>2.2 OrderServiceImpl</h3><p>改造OrderServiceImpl，注入上面的配置项：</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></pre></td><td class="code"><pre><span class="line"><span class="meta">@Value(&quot;$&#123;sky.shop.address&#125;&quot;)</span></span><br><span class="line"><span class="keyword">private</span> String shopAddress;</span><br><span class="line"></span><br><span class="line"><span class="meta">@Value(&quot;$&#123;sky.baidu.ak&#125;&quot;)</span></span><br><span class="line"><span class="keyword">private</span> String ak;</span><br></pre></td></tr></table></figure><p>在OrderServiceImpl中提供校验方法：</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></span><br><span class="line"><span class="comment">     * <span class="doctag">@param</span> address</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_">checkOutOfRange</span><span class="params">(String address)</span> &#123;</span><br><span class="line">        <span class="type">Map</span> <span class="variable">map</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">HashMap</span>();</span><br><span class="line">        map.put(<span class="string">&quot;address&quot;</span>,shopAddress);</span><br><span class="line">        map.put(<span class="string">&quot;output&quot;</span>,<span class="string">&quot;json&quot;</span>);</span><br><span class="line">        map.put(<span class="string">&quot;ak&quot;</span>,ak);</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">shopCoordinate</span> <span class="operator">=</span> HttpClientUtil.doGet(<span class="string">&quot;https://api.map.baidu.com/geocoding/v3&quot;</span>, map);</span><br><span class="line"></span><br><span class="line">        <span class="type">JSONObject</span> <span class="variable">jsonObject</span> <span class="operator">=</span> JSON.parseObject(shopCoordinate);</span><br><span class="line">        <span class="keyword">if</span>(!jsonObject.getString(<span class="string">&quot;status&quot;</span>).equals(<span class="string">&quot;0&quot;</span>))&#123;</span><br><span class="line">            <span class="keyword">throw</span> <span class="keyword">new</span> <span class="title class_">OrderBusinessException</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="type">JSONObject</span> <span class="variable">location</span> <span class="operator">=</span> jsonObject.getJSONObject(<span class="string">&quot;result&quot;</span>).getJSONObject(<span class="string">&quot;location&quot;</span>);</span><br><span class="line">        <span class="type">String</span> <span class="variable">lat</span> <span class="operator">=</span> location.getString(<span class="string">&quot;lat&quot;</span>);</span><br><span class="line">        <span class="type">String</span> <span class="variable">lng</span> <span class="operator">=</span> location.getString(<span class="string">&quot;lng&quot;</span>);</span><br><span class="line">        <span class="comment">//店铺经纬度坐标</span></span><br><span class="line">        <span class="type">String</span> <span class="variable">shopLngLat</span> <span class="operator">=</span> lat + <span class="string">&quot;,&quot;</span> + lng;</span><br><span class="line"></span><br><span class="line">        map.put(<span class="string">&quot;address&quot;</span>,address);</span><br><span class="line">        <span class="comment">//获取用户收货地址的经纬度坐标</span></span><br><span class="line">        <span class="type">String</span> <span class="variable">userCoordinate</span> <span class="operator">=</span> HttpClientUtil.doGet(<span class="string">&quot;https://api.map.baidu.com/geocoding/v3&quot;</span>, map);</span><br><span class="line"></span><br><span class="line">        jsonObject = JSON.parseObject(userCoordinate);</span><br><span class="line">        <span class="keyword">if</span>(!jsonObject.getString(<span class="string">&quot;status&quot;</span>).equals(<span class="string">&quot;0&quot;</span>))&#123;</span><br><span class="line">            <span class="keyword">throw</span> <span class="keyword">new</span> <span class="title class_">OrderBusinessException</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">        location = jsonObject.getJSONObject(<span class="string">&quot;result&quot;</span>).getJSONObject(<span class="string">&quot;location&quot;</span>);</span><br><span class="line">        lat = location.getString(<span class="string">&quot;lat&quot;</span>);</span><br><span class="line">        lng = location.getString(<span class="string">&quot;lng&quot;</span>);</span><br><span class="line">        <span class="comment">//用户收货地址经纬度坐标</span></span><br><span class="line">        <span class="type">String</span> <span class="variable">userLngLat</span> <span class="operator">=</span> lat + <span class="string">&quot;,&quot;</span> + lng;</span><br><span class="line"></span><br><span class="line">        map.put(<span class="string">&quot;origin&quot;</span>,shopLngLat);</span><br><span class="line">        map.put(<span class="string">&quot;destination&quot;</span>,userLngLat);</span><br><span class="line">        map.put(<span class="string">&quot;steps_info&quot;</span>,<span class="string">&quot;0&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">json</span> <span class="operator">=</span> HttpClientUtil.doGet(<span class="string">&quot;https://api.map.baidu.com/directionlite/v1/driving&quot;</span>, map);</span><br><span class="line"></span><br><span class="line">        jsonObject = JSON.parseObject(json);</span><br><span class="line">        <span class="keyword">if</span>(!jsonObject.getString(<span class="string">&quot;status&quot;</span>).equals(<span class="string">&quot;0&quot;</span>))&#123;</span><br><span class="line">            <span class="keyword">throw</span> <span class="keyword">new</span> <span class="title class_">OrderBusinessException</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="type">JSONObject</span> <span class="variable">result</span> <span class="operator">=</span> jsonObject.getJSONObject(<span class="string">&quot;result&quot;</span>);</span><br><span class="line">        <span class="type">JSONArray</span> <span class="variable">jsonArray</span> <span class="operator">=</span> (JSONArray) result.get(<span class="string">&quot;routes&quot;</span>);</span><br><span class="line">        <span class="type">Integer</span> <span class="variable">distance</span> <span class="operator">=</span> (Integer) ((JSONObject) jsonArray.get(<span class="number">0</span>)).get(<span class="string">&quot;distance&quot;</span>);</span><br><span class="line"></span><br><span class="line">        <span class="keyword">if</span>(distance &gt; <span class="number">5000</span>)&#123;</span><br><span class="line">            <span class="comment">//配送距离超过5000米</span></span><br><span class="line">            <span class="keyword">throw</span> <span class="keyword">new</span> <span class="title class_">OrderBusinessException</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><p>在OrderServiceImpl的submitOrder方法中调用上面的校验方法：</p><p><img src="assets/image-20221222171444981.png" alt="image-20221222171444981"></p><h1 id="day10"><a href="#day10" class="headerlink" title="day10"></a>day10</h1><h2 id="课程内容-5"><a href="#课程内容-5" class="headerlink" title="课程内容"></a>课程内容</h2><ul><li>Spring Task</li><li>订单状态定时处理</li><li>WebSocket</li><li>来单提醒</li><li>客户催单</li></ul><p>功能实现：<strong>订单状态定时处理</strong>、<strong>来单提醒</strong>和<strong>客户催单</strong></p><p><strong>订单状态定时处理：</strong><br><div class="img-wrap"><div class="img-bg"><img class="img" src="https://raw.githubusercontent.com/silvan2077/markdown_pic1/main/Picgo/image-20221218204021760.png"/></div></div></p><p><strong>来单提醒</strong><br><div class="img-wrap"><div class="img-bg"><img class="img" src="https://raw.githubusercontent.com/silvan2077/markdown_pic1/main/Picgo/image-20221218204119663.png"/></div></div></p><p><strong>客户催单：</strong><br><div class="img-wrap"><div class="img-bg"><img class="img" src="https://raw.githubusercontent.com/silvan2077/markdown_pic1/main/Picgo/image-20221218204202847.png"/></div></div></p><h2 id="Spring-Task"><a href="#Spring-Task" class="headerlink" title="Spring Task"></a>Spring Task</h2><h3 id="介绍-4"><a href="#介绍-4" class="headerlink" title="介绍"></a>介绍</h3><p><code>Spring Task</code> 是Spring框架提供的<code>任务调度工具</code>，可以按照约定的时间自动执行某个代码逻辑。</p><p><strong>定位：</strong> 定时任务框架<br><strong>作用：</strong> 定时自动执行某段Java代码</p><p>Java程序中使用SpringTask的几个应用场景：<br><div class="tabs" id="springtask"><ul class="nav-tabs"><li class="tab active"><button type="button" data-href="#springtask-1">信用卡每月还款提醒</button></li><li class="tab"><button type="button" data-href="#springtask-2">银行贷款每月还款提醒</button></li><li class="tab"><button type="button" data-href="#springtask-3">火车票售票系统处理未支付订单</button></li><li class="tab"><button type="button" data-href="#springtask-4">入职纪念日为用户发送通知</button></li></ul><div class="tab-contents"><div class="tab-item-content active" id="springtask-1"><div class="img-wrap"><div class="img-bg"><img class="img" src="https://raw.githubusercontent.com/silvan2077/markdown_pic1/main/Picgo/image-20221218183213088.png"/></div></div><button type="button" class="tab-to-top" aria-label="scroll to top"><i class="fas fa-arrow-up"></i></button></div><div class="tab-item-content" id="springtask-2"><div class="img-wrap"><div class="img-bg"><img class="img" src="https://raw.githubusercontent.com/silvan2077/markdown_pic1/main/Picgo/image-20221218183410430.png"/></div></div><button type="button" class="tab-to-top" aria-label="scroll to top"><i class="fas fa-arrow-up"></i></button></div><div class="tab-item-content" id="springtask-3"><div class="img-wrap"><div class="img-bg"><img class="img" src="https://raw.githubusercontent.com/silvan2077/markdown_pic1/main/Picgo/image-20221218183614351.png"/></div></div><button type="button" class="tab-to-top" aria-label="scroll to top"><i class="fas fa-arrow-up"></i></button></div><div class="tab-item-content" id="springtask-4"><div class="img-wrap"><div class="img-bg"><img class="img" src="https://raw.githubusercontent.com/silvan2077/markdown_pic1/main/Picgo/image-20221218183655186.png"/></div></div><button type="button" class="tab-to-top" aria-label="scroll to top"><i class="fas fa-arrow-up"></i></button></div></div></div></p><div class="note warning no-icon flat"><p><strong>只要是任何需要定时处理的场景都可以使用SpringTask</strong></p></div><h3 id="cron表达式"><a href="#cron表达式" class="headerlink" title="cron表达式"></a>cron表达式</h3><p><strong>cron表达式</strong> 其实就是一个字符串，通过cron表达式可以<strong>定义任务触发的时间</strong></p><p><strong>构成规则：</strong> 分为6或7个域，由空格分隔开，每个域代表一个含义</p><p>每个域的含义分别为：秒、分钟、小时、日、月、周、年(可选)</p><p><strong>举例：</strong></p><p>2022年10月12日上午9点整 对应的cron表达式为：<strong>0 0 9 12 10 ? 2022</strong></p><div class="img-wrap"><div class="img-bg"><img class="img" src="https://raw.githubusercontent.com/silvan2077/markdown_pic1/main/Picgo/image-20221218184412491.png"/></div></div><div class="note warning no-icon flat"><p><strong>说明：</strong> 一般<strong>日</strong>和<strong>周</strong>的值不同时设置，其中一个设置，另一个用？表示。</p></div><p><strong>比如：</strong> 描述2月份的最后一天，最后一天具体是几号呢？可能是28号，也有可能是29号，此时就不能写具体数字。可以通过一些特殊字符来描述这些信息。这些具体的细节，我们就不用自己去手写，因为cron表达式有许多在线生成器。</p><p>cron表达式在线生成器：<a href="https://cron.qqe2.com/">https://cron.qqe2.com/</a></p><div class="img-wrap"><div class="img-bg"><img class="img" src="https://raw.githubusercontent.com/silvan2077/markdown_pic1/main/Picgo/image-20221218184959888.png"/></div></div><div class="note info no-icon flat"><p>直接输入自己的需求就能够生成相应的cron表达式，因此一般不用自己去写cron表达式，简单了解即可。</p></div><p><strong>通配符：</strong></p><ul><li><p><code>\*</code> 表示所有值； </p></li><li><p><code>?</code> 表示未说明的值，即不关心它为何值； </p></li><li><p><code>-</code> 表示一个指定的范围； </p></li><li><p><code>,</code> 表示附加一个可能值； </p></li><li><p><code>/</code> 符号前表示开始时间，符号后表示每次递增的值；</p></li></ul><p><strong>cron表达式案例：</strong></p><ul><li><p><em>/5 </em> <em> </em> * ? 每隔5秒执行一次</p></li><li><p>0 <em>/1 </em> <em> </em> ? 每隔1分钟执行一次</p></li><li><p>0 0 5-15 <em> </em> ? 每天5-15点整点触发</p></li><li><p>0 0/3 <em> </em> * ? 每三分钟触发一次</p></li><li><p>0 0-5 14 <em> </em> ? 在每天下午2点到下午2:05期间的每1分钟触发 </p></li><li><p>0 0/5 14 <em> </em> ? 在每天下午2点到下午2:55期间的每5分钟触发</p></li><li><p>0 0/5 14,18 <em> </em> ? 在每天下午2点到2:55期间和下午6点到6:55期间的每5分钟触发</p></li><li><p>0 0/30 9-17 <em> </em> ? 朝九晚五工作时间内每半小时</p></li><li><p>0 0 10,14,16 <em> </em> ? 每天上午10点，下午2点，4点 </p></li></ul><h3 id="入门案例-2"><a href="#入门案例-2" class="headerlink" title="入门案例"></a>入门案例</h3><h4 id="Spring-Task使用步骤"><a href="#Spring-Task使用步骤" class="headerlink" title="Spring Task使用步骤"></a>Spring Task使用步骤</h4><ol><li>导入maven坐标 spring-context（已存在）</li></ol><div class="img-wrap"><div class="img-bg"><img class="img" src="https://raw.githubusercontent.com/silvan2077/markdown_pic1/main/Picgo/image-20221218193251182.png"/></div></div><ol><li><p>启动类添加注解 <code>@EnableScheduling</code> 开启任务调度</p></li><li><p>自定义定时任务类</p></li></ol><h4 id="代码开发-17"><a href="#代码开发-17" class="headerlink" title="代码开发"></a>代码开发</h4><p><strong>编写定时任务类：</strong></p><p>进入sky-server模块中</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="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">@Slf4j</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">MyTask</span> &#123;</span><br><span class="line"></span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     * 定时任务 每隔5秒触发一次</span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    <span class="meta">@Scheduled(cron = &quot;0/5 * * * * ?&quot;)</span></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">executeTask</span><span class="params">()</span>&#123;</span><br><span class="line">        log.info(<span class="string">&quot;定时任务开始执行：&#123;&#125;&quot;</span>,<span class="keyword">new</span> <span class="title class_">Date</span>());</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>@EnableScheduling</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></pre></td><td class="code"><pre><span class="line"><span class="meta">@SpringBootApplication</span></span><br><span class="line"><span class="meta">@EnableTransactionManagement</span> <span class="comment">//开启注解方式的事务管理</span></span><br><span class="line"><span class="meta">@Slf4j</span></span><br><span class="line"><span class="meta">@EnableCaching</span></span><br><span class="line"><span class="meta">@EnableScheduling</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">SkyApplication</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(SkyApplication.class, args);</span><br><span class="line">        log.info(<span class="string">&quot;server started&quot;</span>);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h4 id="功能测试-18"><a href="#功能测试-18" class="headerlink" title="功能测试"></a>功能测试</h4><p>启动服务，查看日志</p><div class="img-wrap"><div class="img-bg"><img class="img" src="https://raw.githubusercontent.com/silvan2077/markdown_pic1/main/Picgo/20251116201714.png"/></div></div><p>任务每隔5秒执行一次。</p><h2 id="订单状态定时处理"><a href="#订单状态定时处理" class="headerlink" title="订单状态定时处理"></a>订单状态定时处理</h2><h3 id="需求分析-3"><a href="#需求分析-3" class="headerlink" title="需求分析"></a>需求分析</h3><p>用户下单后可能存在的情况：</p><ul><li>下单后未支付，订单一直处于<code>待支付</code>状态</li><li>用户收货后管理端未点击完成按钮，订单一直处于<code>派送中</code>状态<div class="img-wrap"><div class="img-bg"><img class="img" src="https://raw.githubusercontent.com/silvan2077/markdown_pic1/main/Picgo/20251116202523.png"/></div></div></li></ul><p>上面两种情况都需要通过<strong>定时任务</strong>来修改订单状态，具体逻辑为：</p><ul><li>通过定时任务每分钟检查一次是否存在支付超时订单（下单后超过15分钟仍未支付则判定为支付超时订单），如果存在则修改订单状态为<code>已取消</code></li><li>通过定时任务每天凌晨1点检查一次是否存在<code>派送中</code>的订单，如果存在则修改订单状态为<code>已完成</code></li></ul><h3 id="代码开发-18"><a href="#代码开发-18" class="headerlink" title="代码开发"></a>代码开发</h3><ol><li>自定义定时任务类OrderTask（待完善）：<br>新建Task包，并创建定时任务类<code>OrderTask</code><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><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><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">@Slf4j</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">OrderTask</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> 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">@Scheduled(cron = &quot;0 * * * * ?&quot;)</span></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">processTimeoutOrder</span><span class="params">()</span>&#123;</span><br><span class="line">        log.info(<span class="string">&quot;处理支付超时订单：&#123;&#125;&quot;</span>, <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">     * 处理“派送中”状态的订单</span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    <span class="meta">@Scheduled(cron = &quot;0 0 1 * * ?&quot;)</span></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">processDeliveryOrder</span><span class="params">()</span>&#123;</span><br><span class="line">        log.info(<span class="string">&quot;处理派送中订单：&#123;&#125;&quot;</span>, <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">&#125;</span><br></pre></td></tr></table></figure></li></ol><ol><li>在OrderMapper接口中扩展方法:</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></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">@param</span> status</span></span><br><span class="line"><span class="comment">    * <span class="doctag">@param</span> orderTime</span></span><br><span class="line"><span class="comment">    */</span></span><br><span class="line">   <span class="meta">@Select(&quot;select * from orders where status = #&#123;status&#125; and order_time &lt; #&#123;orderTime&#125;&quot;)</span></span><br><span class="line">   List&lt;Orders&gt; <span class="title function_">getByStatusAndOrdertimeLT</span><span class="params">(Integer status, LocalDateTime orderTime)</span>;</span><br></pre></td></tr></table></figure><ol><li>完善定时任务类的<code>processTimeoutOrder</code>方法：</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><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">@Scheduled(cron = &quot;0 * * * * ?&quot;)</span></span><br><span class="line">   <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">processTimeoutOrder</span><span class="params">()</span>&#123;</span><br><span class="line">       log.info(<span class="string">&quot;处理支付超时订单：&#123;&#125;&quot;</span>, <span class="keyword">new</span> <span class="title class_">Date</span>());</span><br><span class="line"></span><br><span class="line">       <span class="type">LocalDateTime</span> <span class="variable">time</span> <span class="operator">=</span> LocalDateTime.now().plusMinutes(-<span class="number">15</span>);</span><br><span class="line"></span><br><span class="line">       <span class="comment">// select * from orders where status = 1 and order_time &lt; 当前时间-15分钟</span></span><br><span class="line">       List&lt;Orders&gt; ordersList = orderMapper.getByStatusAndOrdertimeLT(Orders.PENDING_PAYMENT, time);</span><br><span class="line">       <span class="keyword">if</span>(ordersList != <span class="literal">null</span> &amp;&amp; ordersList.size() &gt; <span class="number">0</span>)&#123;</span><br><span class="line">           ordersList.forEach(order -&gt; &#123;</span><br><span class="line">               order.setStatus(Orders.CANCELLED);</span><br><span class="line">               order.setCancelReason(<span class="string">&quot;支付超时，自动取消&quot;</span>);</span><br><span class="line">               order.setCancelTime(LocalDateTime.now());</span><br><span class="line">               orderMapper.update(order);</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><ol><li>完善定时任务类的processDeliveryOrder方法：</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></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">@Scheduled(cron = &quot;0 0 1 * * ?&quot;)</span></span><br><span class="line">   <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">processDeliveryOrder</span><span class="params">()</span>&#123;</span><br><span class="line">       log.info(<span class="string">&quot;处理派送中订单：&#123;&#125;&quot;</span>, <span class="keyword">new</span> <span class="title class_">Date</span>());</span><br><span class="line">       <span class="comment">// select * from orders where status = 4 and order_time &lt; 当前时间-1小时</span></span><br><span class="line">       <span class="type">LocalDateTime</span> <span class="variable">time</span> <span class="operator">=</span> LocalDateTime.now().plusMinutes(-<span class="number">60</span>);</span><br><span class="line">       List&lt;Orders&gt; ordersList = orderMapper.getByStatusAndOrdertimeLT(Orders.DELIVERY_IN_PROGRESS, time);</span><br><span class="line"></span><br><span class="line">       <span class="keyword">if</span>(ordersList != <span class="literal">null</span> &amp;&amp; ordersList.size() &gt; <span class="number">0</span>)&#123;</span><br><span class="line">           ordersList.forEach(order -&gt; &#123;</span><br><span class="line">               order.setStatus(Orders.COMPLETED);</span><br><span class="line">               orderMapper.update(order);</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="功能测试-19"><a href="#功能测试-19" class="headerlink" title="功能测试"></a>功能测试</h3><p>可以通过如下方式进行测试：</p><ul><li>查看控制台sql</li><li>查看数据库中数据变化</li></ul><p><strong>支付超时的订单测试：</strong></p><p><strong>1. 查看订单表</strong></p><p>有一条订单，状态为1。订单状态 1待付款 2待接单 3已接单 4派送中 5已完成 6已取消</p><div class="img-wrap"><div class="img-bg"><img class="img" src="https://raw.githubusercontent.com/silvan2077/markdown_pic1/main/Picgo/image-20221218202334773.png"/></div></div><p><strong>2. 开启定时任务</strong></p><p>启动服务，观察控制台日志。处理支付超时订单任务每隔1分钟执行一次。<br><div class="img-wrap"><div class="img-bg"><img class="img" src="https://raw.githubusercontent.com/silvan2077/markdown_pic1/main/Picgo/image-20221218203045089.png"/></div></div></p><p><strong>3. 再次查看订单表</strong></p><p>状态已更改为6，已取消。<br><div class="img-wrap"><div class="img-bg"><img class="img" src="https://raw.githubusercontent.com/silvan2077/markdown_pic1/main/Picgo/image-20221218203146535.png"/></div></div></p><p>证明定时任务已生效。</p><div class="note warning no-icon flat"><p><strong>处理“派送中”状态的订单任务</strong>的测试步骤和上述一致。可适当修改cron表达式，改变任务执行频率，方便测试。</p></div><h2 id="WebSocket"><a href="#WebSocket" class="headerlink" title="WebSocket"></a>WebSocket</h2><h3 id="介绍-5"><a href="#介绍-5" class="headerlink" title="介绍"></a>介绍</h3><p><code>WebSocket</code> 是基于 <code>TCP</code> 的一种新的<code>网络协议</code>。它实现了浏览器与服务器全双工通信——浏览器和服务器只需要完成一次握手，两者之间就可以创建<strong>持久性</strong>的连接， 并进行<strong>双向</strong>数据传输。</p><p><strong>HTTP协议和WebSocket协议对比：</strong></p><ul><li>HTTP是<strong>短连接</strong></li><li>WebSocket是<strong>长连接</strong></li><li>HTTP通信是<strong>单向</strong>的，基于请求响应模式</li><li>WebSocket支持<strong>双向</strong>通信</li><li>HTTP和WebSocket底层都是TCP连接</li></ul><div class="img-wrap"><div class="img-bg"><img class="img" src="https://raw.githubusercontent.com/silvan2077/markdown_pic1/main/Picgo/20251116203134.png"/></div></div><div class="note info no-icon flat"><p><strong>思考：</strong> 既然WebSocket支持双向通信，功能看似比HTTP强大，那么我们是不是可以基于WebSocket开发所有的业务功能？</p></div><p><strong>WebSocket缺点：</strong></p><ul><li>服务器长期维护长连接需要一定的成本</li><li>各个浏览器支持程度不一</li><li>WebSocket 是长连接，受网络限制比较大，需要处理好重连</li></ul><p><strong>结论：</strong> WebSocket并不能完全取代HTTP，它只适合在特定的场景下使用</p><p><strong>WebSocket应用场景：</strong></p><div class="tabs" id="websocket应用场景"><ul class="nav-tabs"><li class="tab active"><button type="button" data-href="#websocket应用场景-1">视频弹幕</button></li><li class="tab"><button type="button" data-href="#websocket应用场景-2">网页聊天</button></li><li class="tab"><button type="button" data-href="#websocket应用场景-3">体育实况更新</button></li><li class="tab"><button type="button" data-href="#websocket应用场景-4">股票基金报价实时更新</button></li></ul><div class="tab-contents"><div class="tab-item-content active" id="websocket应用场景-1"><div class="img-wrap"><div class="img-bg"><img class="img" src="https://raw.githubusercontent.com/silvan2077/markdown_pic1/main/Picgo/image-20221222184616570.png"/></div></div><button type="button" class="tab-to-top" aria-label="scroll to top"><i class="fas fa-arrow-up"></i></button></div><div class="tab-item-content" id="websocket应用场景-2"><div class="img-wrap"><div class="img-bg"><img class="img" src="https://raw.githubusercontent.com/silvan2077/markdown_pic1/main/Picgo/image-20221222184641675.png"/></div></div><button type="button" class="tab-to-top" aria-label="scroll to top"><i class="fas fa-arrow-up"></i></button></div><div class="tab-item-content" id="websocket应用场景-3"><div class="img-wrap"><div class="img-bg"><img class="img" src="https://raw.githubusercontent.com/silvan2077/markdown_pic1/main/Picgo/image-20221222184641675.png"/></div></div><button type="button" class="tab-to-top" aria-label="scroll to top"><i class="fas fa-arrow-up"></i></button></div><div class="tab-item-content" id="websocket应用场景-4"><div class="img-wrap"><div class="img-bg"><img class="img" src="https://raw.githubusercontent.com/silvan2077/markdown_pic1/main/Picgo/image-20221222184714092.png"/></div></div><button type="button" class="tab-to-top" aria-label="scroll to top"><i class="fas fa-arrow-up"></i></button></div></div></div><h3 id="入门案例-3"><a href="#入门案例-3" class="headerlink" title="入门案例"></a>入门案例</h3><h4 id="案例分析"><a href="#案例分析" class="headerlink" title="案例分析"></a>案例分析</h4><p><strong>需求：</strong> 实现浏览器与服务器全双工通信。浏览器既可以向服务器发送消息，服务器也可主动向浏览器推送消息。</p><p><strong>效果展示：</strong></p><div class="img-wrap"><div class="img-bg"><img class="img" src="https://raw.githubusercontent.com/silvan2077/markdown_pic1/main/Picgo/image-20221222190401414.png"/></div></div><p><strong>实现步骤：</strong></p><ol><li><p>直接使用websocket.html页面作为WebSocket客户端</p></li><li><p>导入WebSocket的maven坐标</p></li><li><p>导入WebSocket服务端组件WebSocketServer，用于和客户端通信</p></li><li><p>导入配置类WebSocketConfiguration，注册WebSocket的服务端组件</p></li><li><p>导入定时任务类WebSocketTask，定时向客户端推送数据</p></li></ol><h4 id="代码开发-19"><a href="#代码开发-19" class="headerlink" title="代码开发"></a>代码开发</h4><ol><li>定义websocket.html页面(资料中已提供)</li></ol><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><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;!DOCTYPE <span class="keyword">HTML</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;<span class="name">html</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>WebSocket Demo<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">input</span> <span class="attr">id</span>=<span class="string">&quot;text&quot;</span> <span class="attr">type</span>=<span class="string">&quot;text&quot;</span> /&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">button</span> <span class="attr">onclick</span>=<span class="string">&quot;send()&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">button</span> <span class="attr">onclick</span>=<span class="string">&quot;closeWebSocket()&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> <span class="attr">id</span>=<span class="string">&quot;message&quot;</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 class="tag">&lt;/<span class="name">body</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;<span class="name">script</span> <span class="attr">type</span>=<span class="string">&quot;text/javascript&quot;</span>&gt;</span><span class="language-javascript"></span></span><br><span class="line"><span class="language-javascript">    <span class="keyword">var</span> websocket = <span class="literal">null</span>;</span></span><br><span class="line"><span class="language-javascript">    <span class="keyword">var</span> clientId = <span class="title class_">Math</span>.<span class="title function_">random</span>().<span class="title function_">toString</span>(<span class="number">36</span>).<span class="title function_">substr</span>(<span class="number">2</span>);</span></span><br><span class="line"><span class="language-javascript"></span></span><br><span class="line"><span class="language-javascript">    <span class="comment">//判断当前浏览器是否支持WebSocket</span></span></span><br><span class="line"><span class="language-javascript">    <span class="keyword">if</span>(<span class="string">&#x27;WebSocket&#x27;</span> <span class="keyword">in</span> <span class="variable language_">window</span>)&#123;</span></span><br><span class="line"><span class="language-javascript">        <span class="comment">//连接WebSocket节点</span></span></span><br><span class="line"><span class="language-javascript">        websocket = <span class="keyword">new</span> <span class="title class_">WebSocket</span>(<span class="string">&quot;ws://localhost:8080/ws/&quot;</span>+clientId);</span></span><br><span class="line"><span class="language-javascript">    &#125;</span></span><br><span class="line"><span class="language-javascript">    <span class="keyword">else</span>&#123;</span></span><br><span class="line"><span class="language-javascript">        <span class="title function_">alert</span>(<span class="string">&#x27;Not support websocket&#x27;</span>)</span></span><br><span class="line"><span class="language-javascript">    &#125;</span></span><br><span class="line"><span class="language-javascript"></span></span><br><span class="line"><span class="language-javascript">    <span class="comment">//连接发生错误的回调方法</span></span></span><br><span class="line"><span class="language-javascript">    websocket.<span class="property">onerror</span> = <span class="keyword">function</span>(<span class="params"></span>)&#123;</span></span><br><span class="line"><span class="language-javascript">        <span class="title function_">setMessageInnerHTML</span>(<span class="string">&quot;error&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><br><span class="line"><span class="language-javascript">    <span class="comment">//连接成功建立的回调方法</span></span></span><br><span class="line"><span class="language-javascript">    websocket.<span class="property">onopen</span> = <span class="keyword">function</span>(<span class="params"></span>)&#123;</span></span><br><span class="line"><span class="language-javascript">        <span class="title function_">setMessageInnerHTML</span>(<span class="string">&quot;连接成功&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><br><span class="line"><span class="language-javascript">    <span class="comment">//接收到消息的回调方法</span></span></span><br><span class="line"><span class="language-javascript">    websocket.<span class="property">onmessage</span> = <span class="keyword">function</span>(<span class="params">event</span>)&#123;</span></span><br><span class="line"><span class="language-javascript">        <span class="title function_">setMessageInnerHTML</span>(event.<span class="property">data</span>);</span></span><br><span class="line"><span class="language-javascript">    &#125;</span></span><br><span class="line"><span class="language-javascript"></span></span><br><span class="line"><span class="language-javascript">    <span class="comment">//连接关闭的回调方法</span></span></span><br><span class="line"><span class="language-javascript">    websocket.<span class="property">onclose</span> = <span class="keyword">function</span>(<span class="params"></span>)&#123;</span></span><br><span class="line"><span class="language-javascript">        <span class="title function_">setMessageInnerHTML</span>(<span class="string">&quot;close&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><br><span class="line"><span class="language-javascript">    <span class="comment">//监听窗口关闭事件，当窗口关闭时，主动去关闭websocket连接，防止连接还没断开就关闭窗口，server端会抛异常。</span></span></span><br><span class="line"><span class="language-javascript">    <span class="variable language_">window</span>.<span class="property">onbeforeunload</span> = <span class="keyword">function</span>(<span class="params"></span>)&#123;</span></span><br><span class="line"><span class="language-javascript">        websocket.<span class="title function_">close</span>();</span></span><br><span class="line"><span class="language-javascript">    &#125;</span></span><br><span class="line"><span class="language-javascript"></span></span><br><span class="line"><span class="language-javascript">    <span class="comment">//将消息显示在网页上</span></span></span><br><span class="line"><span class="language-javascript">    <span class="keyword">function</span> <span class="title function_">setMessageInnerHTML</span>(<span class="params">innerHTML</span>)&#123;</span></span><br><span class="line"><span class="language-javascript">        <span class="variable language_">document</span>.<span class="title function_">getElementById</span>(<span class="string">&#x27;message&#x27;</span>).<span class="property">innerHTML</span> += innerHTML + <span class="string">&#x27;&lt;br/&gt;&#x27;</span>;</span></span><br><span class="line"><span class="language-javascript">    &#125;</span></span><br><span class="line"><span class="language-javascript"></span></span><br><span class="line"><span class="language-javascript">    <span class="comment">//发送消息</span></span></span><br><span class="line"><span class="language-javascript">    <span class="keyword">function</span> <span class="title function_">send</span>(<span class="params"></span>)&#123;</span></span><br><span class="line"><span class="language-javascript">        <span class="keyword">var</span> message = <span class="variable language_">document</span>.<span class="title function_">getElementById</span>(<span class="string">&#x27;text&#x27;</span>).<span class="property">value</span>;</span></span><br><span class="line"><span class="language-javascript">        websocket.<span class="title function_">send</span>(message);</span></span><br><span class="line"><span class="language-javascript">    &#125;</span></span><br><span class="line"><span class="language-javascript"></span></span><br><span class="line"><span class="language-javascript"><span class="comment">//关闭连接</span></span></span><br><span class="line"><span class="language-javascript">    <span class="keyword">function</span> <span class="title function_">closeWebSocket</span>(<span class="params"></span>) &#123;</span></span><br><span class="line"><span class="language-javascript">        websocket.<span class="title function_">close</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">html</span>&gt;</span></span><br></pre></td></tr></table></figure><ol><li>导入maven坐标</li></ol><p>在sky-server模块pom.xml中已定义</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.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-websocket<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><ol><li>定义WebSocket服务端组件(资料中已提供)</li></ol><p>直接导入到sky-server模块即可</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></pre></td><td class="code"><pre><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * WebSocket服务</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">@ServerEndpoint(&quot;/ws/&#123;sid&#125;&quot;)</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">WebSocketServer</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> Map&lt;String, Session&gt; sessionMap = <span class="keyword">new</span> <span class="title class_">HashMap</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">@OnOpen</span></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">onOpen</span><span class="params">(Session session, <span class="meta">@PathParam(&quot;sid&quot;)</span> String sid)</span> &#123;</span><br><span class="line">        System.out.println(<span class="string">&quot;客户端：&quot;</span> + sid + <span class="string">&quot;建立连接&quot;</span>);</span><br><span class="line">        sessionMap.put(sid, session);</span><br><span 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 class="doctag">@param</span> message 客户端发送过来的消息</span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    <span class="meta">@OnMessage</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">(String message, <span class="meta">@PathParam(&quot;sid&quot;)</span> String sid)</span> &#123;</span><br><span class="line">        System.out.println(<span class="string">&quot;收到来自客户端：&quot;</span> + sid + <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="comment">     * 连接关闭调用的方法</span></span><br><span class="line"><span class="comment">     *</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@param</span> sid</span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    <span class="meta">@OnClose</span></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">onClose</span><span class="params">(<span class="meta">@PathParam(&quot;sid&quot;)</span> String sid)</span> &#123;</span><br><span class="line">        System.out.println(<span class="string">&quot;连接断开:&quot;</span> + sid);</span><br><span class="line">        sessionMap.remove(sid);</span><br><span 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 class="doctag">@param</span> message</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_">sendToAllClient</span><span class="params">(String message)</span> &#123;</span><br><span class="line">        Collection&lt;Session&gt; sessions = sessionMap.values();</span><br><span class="line">        <span class="keyword">for</span> (Session session : sessions) &#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">                session.getBasicRemote().sendText(message);</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"></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><ol><li>定义配置类，注册WebSocket的服务端组件(从资料中直接导入即可)</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></pre></td><td class="code"><pre><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * WebSocket配置类，用于注册WebSocket的Bean</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_">WebSocketConfiguration</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> ServerEndpointExporter <span class="title function_">serverEndpointExporter</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_">ServerEndpointExporter</span>();</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><ol><li>定义定时任务类，定时向客户端推送数据(从资料中直接导入即可)</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></pre></td><td class="code"><pre><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_">WebSocketTask</span> &#123;</span><br><span class="line">    <span class="meta">@Autowired</span></span><br><span class="line">    <span class="keyword">private</span> WebSocketServer webSocketServer;</span><br><span class="line"></span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     * 通过WebSocket每隔5秒向客户端发送消息</span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    <span class="meta">@Scheduled(cron = &quot;0/5 * * * * ?&quot;)</span></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">sendMessageToClient</span><span class="params">()</span> &#123;</span><br><span class="line">        webSocketServer.sendToAllClient(<span class="string">&quot;这是来自服务端的消息：&quot;</span> + DateTimeFormatter.ofPattern(<span class="string">&quot;HH:mm:ss&quot;</span>).format(LocalDateTime.now()));</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h4 id="功能测试-20"><a href="#功能测试-20" class="headerlink" title="功能测试"></a>功能测试</h4><p>启动服务，打开websocket.html页面</p><p><strong>浏览器向服务器发送数据：</strong></p><p><img src="assets/image-20221222192759049.png" alt="image-20221222192759049" style="zoom:50%;" /> </p><p><strong>服务器向浏览器间隔5秒推送数据：</strong></p><p><img src="assets/image-20221222192926954.png" alt="image-20221222192926954" style="zoom:50%;" /> </p><h2 id="来单提醒"><a href="#来单提醒" class="headerlink" title="来单提醒"></a>来单提醒</h2><h3 id="需求分析和设计-34"><a href="#需求分析和设计-34" class="headerlink" title="需求分析和设计"></a>需求分析和设计</h3><p>用户下单并且支付成功后，需要第一时间通知外卖商家。通知的形式有如下两种：</p><ul><li>语音播报 </li><li>弹出提示框</li></ul><div class="note info no-icon flat"><p><strong>设计思路：</strong></p><ul><li>通过WebSocket实现管理端页面和服务端保持长连接状态</li><li>当客户支付后，调用WebSocket的相关API实现服务端向客户端推送消息</li><li>客户端浏览器解析服务端推送的消息，判断是来单提醒还是客户催单，进行相应的消息提示和语音播报</li><li>约定服务端发送给客户端浏览器的数据格式为JSON，字段包括：type，orderId，content<ul><li>type 为消息类型，1为来单提醒 2为客户催单</li><li>orderId 为订单id</li><li>content 为消息内容</li></ul></li></ul></div><h3 id="代码开发-20"><a href="#代码开发-20" class="headerlink" title="代码开发"></a>代码开发</h3><p><strong>在OrderServiceImpl中注入WebSocketServer对象，修改paySuccess方法，加入如下代码：</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></pre></td><td class="code"><pre><span class="line"><span class="meta">@Autowired</span></span><br><span class="line">   <span class="keyword">private</span> WebSocketServer webSocketServer;</span><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">@param</span> outTradeNo</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_">paySuccess</span><span class="params">(String outTradeNo)</span> &#123;</span><br><span class="line">       <span class="comment">// 当前登录用户id</span></span><br><span class="line">       <span class="type">Long</span> <span class="variable">userId</span> <span class="operator">=</span> BaseContext.getCurrentId();</span><br><span class="line"></span><br><span class="line">       <span class="comment">// 根据订单号查询当前用户的订单</span></span><br><span class="line">       <span class="type">Orders</span> <span class="variable">ordersDB</span> <span class="operator">=</span> orderMapper.getByNumberAndUserId(outTradeNo, userId);</span><br><span class="line"></span><br><span class="line">       <span class="comment">// 根据订单id更新订单的状态、支付方式、支付状态、结账时间</span></span><br><span class="line">       <span class="type">Orders</span> <span class="variable">orders</span> <span class="operator">=</span> Orders.builder()</span><br><span class="line">               .id(ordersDB.getId())</span><br><span class="line">               .status(Orders.TO_BE_CONFIRMED)</span><br><span class="line">               .payStatus(Orders.PAID)</span><br><span class="line">               .checkoutTime(LocalDateTime.now())</span><br><span class="line">               .build();</span><br><span class="line"></span><br><span class="line">       orderMapper.update(orders);</span><br><span class="line"><span class="comment">//////////////////////////////////////////////</span></span><br><span class="line">       <span class="type">Map</span> <span class="variable">map</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">HashMap</span>();</span><br><span class="line">       map.put(<span class="string">&quot;type&quot;</span>, <span class="number">1</span>);<span class="comment">//消息类型，1表示来单提醒</span></span><br><span class="line">       map.put(<span class="string">&quot;orderId&quot;</span>, orders.getId());</span><br><span class="line">       map.put(<span class="string">&quot;content&quot;</span>, <span class="string">&quot;订单号：&quot;</span> + outTradeNo);</span><br><span class="line"></span><br><span class="line">       <span class="comment">//通过WebSocket实现来单提醒，向客户端浏览器推送消息</span></span><br><span class="line">       webSocketServer.sendToAllClient(JSON.toJSONString(map));</span><br><span class="line">       <span class="comment">///////////////////////////////////////////////////</span></span><br><span class="line">   &#125;</span><br></pre></td></tr></table></figure><h3 id="功能测试-21"><a href="#功能测试-21" class="headerlink" title="功能测试"></a>功能测试</h3><p>可以通过如下方式进行测试：</p><ul><li>查看浏览器调试工具数据交互过程</li><li>前后端联调</li></ul><p><strong>1. 登录管理端后台</strong></p><p>登录成功后，浏览器与服务器建立长连接</p><p><img src="assets/image-20221222200842731.png" alt="image-20221222200842731" style="zoom:50%;" /> </p><p>查看控制台日志</p><p><img src="assets/image-20221222200941497.png" alt="image-20221222200941497" style="zoom:50%;" /> </p><p><strong>2. 小程序端下单支付</strong></p><p>修改回调地址，利用内网穿透获取域名</p><p><img src="assets/image-20221222201350616.png" alt="image-20221222201350616" style="zoom:50%;" /> </p><p>下单支付</p><p><img src="assets/image-20221222201718622.png" alt="image-20221222201718622" style="zoom:50%;" /> <img src="assets/image-20221222201754866.png" alt="image-20221222201754866" style="zoom:50%;" /> <img src="assets/image-20221222201826173.png" alt="image-20221222201826173" style="zoom:50%;" /> <img src="assets/image-20221222202101677.png" alt="image-20221222202101677" style="zoom:50%;" /></p><p><strong>3. 查看来单提醒</strong></p><p>支付成功后，后台收到来单提醒，并有语音播报</p><p><img src="assets/image-20221222202310953.png" alt="image-20221222202310953" style="zoom:50%;" /> </p><h2 id="客户催单"><a href="#客户催单" class="headerlink" title="客户催单"></a>客户催单</h2><h3 id="需求分析和设计-35"><a href="#需求分析和设计-35" class="headerlink" title="需求分析和设计"></a>需求分析和设计</h3><p>用户在小程序中点击催单按钮后，需要第一时间通知外卖商家。通知的形式有如下两种：</p><ul><li>语音播报 </li><li>弹出提示框</li></ul><div class="note info no-icon flat"><p><strong>设计思路：</strong></p><ul><li>通过WebSocket实现管理端页面和服务端保持长连接状态</li><li>当用户点击催单按钮后，调用WebSocket的相关API实现服务端向客户端推送消息</li><li>客户端浏览器解析服务端推送的消息，判断是来单提醒还是客户催单，进行相应的消息提示和语音播报<br>约定服务端发送给客户端浏览器的数据格式为JSON，字段包括：type，orderId，content<ul><li>type 为消息类型，1为来单提醒 2为客户催单</li><li>orderId 为订单id</li><li>content 为消息内容</li></ul></li></ul></div><p>当用户点击催单按钮时，向服务端发送请求。</p><p><strong>接口设计(催单)：</strong><br><div class="img-wrap"><div class="img-bg"><img class="img" src="https://raw.githubusercontent.com/silvan2077/markdown_pic1/main/Picgo/image-20221222204415339.png"/></div></div></p><div class="img-wrap"><div class="img-bg"><img class="img" src="https://raw.githubusercontent.com/silvan2077/markdown_pic1/main/Picgo/image-20221222204434174.png"/></div></div><h3 id="代码开发-21"><a href="#代码开发-21" class="headerlink" title="代码开发"></a>代码开发</h3><h4 id="Controller层-16"><a href="#Controller层-16" class="headerlink" title="Controller层"></a>Controller层</h4><p><strong>根据用户催单的接口定义，在user包下的OrderController中创建催单方法：</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="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">@param</span> id</span></span><br><span class="line"><span class="comment">    * <span class="doctag">@return</span></span></span><br><span class="line"><span class="comment">    */</span></span><br><span class="line">   <span class="meta">@GetMapping(&quot;/reminder/&#123;id&#125;&quot;)</span></span><br><span class="line">   <span class="meta">@ApiOperation(&quot;用户催单&quot;)</span></span><br><span class="line">   <span class="keyword">public</span> Result <span class="title function_">reminder</span><span class="params">(<span class="meta">@PathVariable(&quot;id&quot;)</span> Long id)</span> &#123;</span><br><span class="line">       orderService.reminder(id);</span><br><span class="line">       <span class="keyword">return</span> Result.success();</span><br><span class="line">   &#125;</span><br></pre></td></tr></table></figure><h4 id="Service层-13"><a href="#Service层-13" class="headerlink" title="Service层"></a>Service层</h4><p><strong>在OrderService接口中声明reminder方法：</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></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">@param</span> id</span></span><br><span class="line"><span class="comment">    */</span></span><br><span class="line">   <span class="keyword">void</span> <span class="title function_">reminder</span><span class="params">(Long id)</span>;</span><br></pre></td></tr></table></figure><p><strong>在OrderServiceImpl中实现reminder方法：</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></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">@param</span> id</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_">reminder</span><span class="params">(Long id)</span> &#123;</span><br><span class="line">       <span class="comment">// 查询订单是否存在</span></span><br><span class="line">       <span class="type">Orders</span> <span class="variable">orders</span> <span class="operator">=</span> orderMapper.getById(id);</span><br><span class="line">       <span class="keyword">if</span> (orders == <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_">OrderBusinessException</span>(MessageConstant.ORDER_NOT_FOUND);</span><br><span class="line">       &#125;</span><br><span class="line"></span><br><span class="line">       <span class="comment">//基于WebSocket实现催单</span></span><br><span class="line">       <span class="type">Map</span> <span class="variable">map</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">HashMap</span>();</span><br><span class="line">       map.put(<span class="string">&quot;type&quot;</span>, <span class="number">2</span>);<span class="comment">//2代表用户催单</span></span><br><span class="line">       map.put(<span class="string">&quot;orderId&quot;</span>, id);</span><br><span class="line">       map.put(<span class="string">&quot;content&quot;</span>, <span class="string">&quot;订单号：&quot;</span> + orders.getNumber());</span><br><span class="line">       webSocketServer.sendToAllClient(JSON.toJSONString(map));</span><br><span class="line">   &#125;</span><br></pre></td></tr></table></figure><h4 id="Mapper层-15"><a href="#Mapper层-15" class="headerlink" title="Mapper层"></a>Mapper层</h4><p><strong>在OrderMapper中添加getById：</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></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 class="doctag">@param</span> id</span></span><br><span class="line"><span class="comment">    */</span></span><br><span class="line">   <span class="meta">@Select(&quot;select * from orders where id=#&#123;id&#125;&quot;)</span></span><br><span class="line">   Orders <span class="title function_">getById</span><span class="params">(Long id)</span>;</span><br></pre></td></tr></table></figure><h3 id="功能测试-22"><a href="#功能测试-22" class="headerlink" title="功能测试"></a>功能测试</h3><p>可以通过如下方式进行测试：</p><ul><li>查看浏览器调试工具数据交互过程</li><li>前后端联调</li></ul><p><strong>1. 登录管理端后台</strong></p><p>登录成功后，浏览器与服务器建立长连接</p><div class="img-wrap"><div class="img-bg"><img class="img" src="https://raw.githubusercontent.com/silvan2077/markdown_pic1/main/Picgo/image-20221222200842731.png"/></div></div><p>查看控制台日志</p><div class="img-wrap"><div class="img-bg"><img class="img" src="https://raw.githubusercontent.com/silvan2077/markdown_pic1/main/Picgo/image-20221222200941497.png"/></div></div><p><strong>2. 用户进行催单</strong></p><p>用户可在订单列表或者订单详情，进行催单<br><div class="img-wrap"><div class="img-bg"><img class="img" src="https://raw.githubusercontent.com/silvan2077/markdown_pic1/main/Picgo/image-20221222210932942.png"/></div></div></p><p><strong>3. 查看催单提醒</strong></p><p>既有催单弹窗，同时语音播报<br><div class="img-wrap"><div class="img-bg"><img class="img" src="https://raw.githubusercontent.com/silvan2077/markdown_pic1/main/Picgo/image-20221222211238000.png"/></div></div></p><h1 id="day11"><a href="#day11" class="headerlink" title="day11"></a>day11</h1><h2 id="课程内容-6"><a href="#课程内容-6" class="headerlink" title="课程内容"></a>课程内容</h2><ul><li>Apache ECharts</li><li>营业额统计</li><li>用户统计</li><li>订单统计</li><li>销量排名Top10</li></ul><p>功能实现：<strong>数据统计</strong></p><p><strong>数据统计效果图：</strong></p><div class="img-wrap"><div class="img-bg"><img class="img" src="https://raw.githubusercontent.com/silvan2077/markdown_pic1/main/Picgo/image-20230101152725417.png"/></div></div><h2 id="Apache-ECharts"><a href="#Apache-ECharts" class="headerlink" title="Apache ECharts"></a>Apache ECharts</h2><h3 id="介绍-6"><a href="#介绍-6" class="headerlink" title="介绍"></a>介绍</h3><p><code>Apache ECharts</code> 是一款基于 <code>Javascript</code> 的数据可视化图表库，提供直观，生动，可交互，可个性化定制的数据可视化图表。<br>官网地址：<a href="https://echarts.apache.org/zh/index.html">https://echarts.apache.org/zh/index.html</a></p><div class="img-wrap"><div class="img-bg"><img class="img" src="https://raw.githubusercontent.com/silvan2077/markdown_pic1/main/Picgo/image-20230101153041348.png"/></div></div><p><strong>常见效果展示：</strong></p><div class="tabs" id="apacheecharts"><ul class="nav-tabs"><li class="tab active"><button type="button" data-href="#apacheecharts-1">柱形图</button></li><li class="tab"><button type="button" data-href="#apacheecharts-2">饼形图</button></li><li class="tab"><button type="button" data-href="#apacheecharts-3">折线图</button></li></ul><div class="tab-contents"><div class="tab-item-content active" id="apacheecharts-1"><div class="img-wrap"><div class="img-bg"><img class="img" src="https://raw.githubusercontent.com/silvan2077/markdown_pic1/main/Picgo/image-20230101153748714.png"/></div></div><button type="button" class="tab-to-top" aria-label="scroll to top"><i class="fas fa-arrow-up"></i></button></div><div class="tab-item-content" id="apacheecharts-2"><div class="img-wrap"><div class="img-bg"><img class="img" src="https://raw.githubusercontent.com/silvan2077/markdown_pic1/main/Picgo/image-20230101153230868.png"/></div></div><button type="button" class="tab-to-top" aria-label="scroll to top"><i class="fas fa-arrow-up"></i></button></div><div class="tab-item-content" id="apacheecharts-3"><div class="img-wrap"><div class="img-bg"><img class="img" src="https://raw.githubusercontent.com/silvan2077/markdown_pic1/main/Picgo/image-20230101153824086.png"/></div></div><button type="button" class="tab-to-top" aria-label="scroll to top"><i class="fas fa-arrow-up"></i></button></div></div></div><p><strong>总结：</strong> 不管是哪种形式的图形，最本质的东西实际上是数据，这只是对数据的一种可视化展示。</p><h3 id="入门案例-4"><a href="#入门案例-4" class="headerlink" title="入门案例"></a>入门案例</h3><p>Apache Echarts官方提供的快速入门：<a href="https://echarts.apache.org/handbook/zh/get-started/">https://echarts.apache.org/handbook/zh/get-started/</a></p><p><strong>效果展示：</strong><br><div class="img-wrap"><div class="img-bg"><img class="img" src="https://raw.githubusercontent.com/silvan2077/markdown_pic1/main/Picgo/image-20230101155524477.png"/></div></div></p><p><strong>实现步骤：</strong></p><ol><li><p>引入echarts.js 文件(当天资料已提供)</p></li><li><p>为 ECharts 准备一个设置宽高的 DOM</p></li><li><p>初始化echarts实例</p></li><li><p>指定图表的配置项和数据</p></li><li><p>使用指定的配置项和数据显示图表</p></li></ol><p><strong>代码开发：</strong></p><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><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">&lt;!DOCTYPE <span class="keyword">html</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;<span class="name">html</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>ECharts<span class="tag">&lt;/<span class="name">title</span>&gt;</span></span><br><span class="line">    <span class="comment">&lt;!-- 引入刚刚下载的 ECharts 文件 --&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">script</span> <span class="attr">src</span>=<span class="string">&quot;echarts.js&quot;</span>&gt;</span><span class="tag">&lt;/<span class="name">script</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="comment">&lt;!-- 为 ECharts 准备一个定义了宽高的 DOM --&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">div</span> <span class="attr">id</span>=<span class="string">&quot;main&quot;</span> <span class="attr">style</span>=<span class="string">&quot;width: 600px;height:400px;&quot;</span>&gt;</span><span class="tag">&lt;/<span class="name">div</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">script</span> <span class="attr">type</span>=<span class="string">&quot;text/javascript&quot;</span>&gt;</span><span class="language-javascript"></span></span><br><span class="line"><span class="language-javascript">      <span class="comment">// 基于准备好的dom，初始化echarts实例</span></span></span><br><span class="line"><span class="language-javascript">      <span class="keyword">var</span> myChart = echarts.<span class="title function_">init</span>(<span class="variable language_">document</span>.<span class="title function_">getElementById</span>(<span class="string">&#x27;main&#x27;</span>));</span></span><br><span class="line"><span class="language-javascript"></span></span><br><span class="line"><span class="language-javascript">      <span class="comment">// 指定图表的配置项和数据</span></span></span><br><span class="line"><span class="language-javascript">      <span class="keyword">var</span> option = &#123;</span></span><br><span class="line"><span class="language-javascript">        <span class="attr">title</span>: &#123;</span></span><br><span class="line"><span class="language-javascript">          <span class="attr">text</span>: <span class="string">&#x27;ECharts 入门示例&#x27;</span></span></span><br><span class="line"><span class="language-javascript">        &#125;,</span></span><br><span class="line"><span class="language-javascript">        <span class="attr">tooltip</span>: &#123;&#125;,</span></span><br><span class="line"><span class="language-javascript">        <span class="attr">legend</span>: &#123;</span></span><br><span class="line"><span class="language-javascript">          <span class="attr">data</span>: [<span class="string">&#x27;销量&#x27;</span>]</span></span><br><span class="line"><span class="language-javascript">        &#125;,</span></span><br><span class="line"><span class="language-javascript">        <span class="attr">xAxis</span>: &#123;</span></span><br><span class="line"><span class="language-javascript">          <span class="attr">data</span>: [<span class="string">&#x27;衬衫&#x27;</span>, <span class="string">&#x27;羊毛衫&#x27;</span>, <span class="string">&#x27;雪纺衫&#x27;</span>, <span class="string">&#x27;裤子&#x27;</span>, <span class="string">&#x27;高跟鞋&#x27;</span>, <span class="string">&#x27;袜子&#x27;</span>]</span></span><br><span class="line"><span class="language-javascript">        &#125;,</span></span><br><span class="line"><span class="language-javascript">        <span class="attr">yAxis</span>: &#123;&#125;,</span></span><br><span class="line"><span class="language-javascript">        <span class="attr">series</span>: [</span></span><br><span class="line"><span class="language-javascript">          &#123;</span></span><br><span class="line"><span class="language-javascript">            <span class="attr">name</span>: <span class="string">&#x27;销量&#x27;</span>,</span></span><br><span class="line"><span class="language-javascript">            <span class="attr">type</span>: <span class="string">&#x27;bar&#x27;</span>,</span></span><br><span class="line"><span class="language-javascript">            <span class="attr">data</span>: [<span class="number">5</span>, <span class="number">20</span>, <span class="number">36</span>, <span class="number">10</span>, <span class="number">10</span>, <span class="number">20</span>]</span></span><br><span class="line"><span class="language-javascript">          &#125;</span></span><br><span class="line"><span class="language-javascript">        ]</span></span><br><span class="line"><span class="language-javascript">      &#125;;</span></span><br><span class="line"><span class="language-javascript"></span></span><br><span class="line"><span class="language-javascript">      <span class="comment">// 使用刚指定的配置项和数据显示图表。</span></span></span><br><span class="line"><span class="language-javascript">      myChart.<span class="title function_">setOption</span>(option);</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><p><strong>测试：</strong>(当天资料中已提供)<br><div class="img-wrap"><div class="img-bg"><img class="img" src="https://raw.githubusercontent.com/silvan2077/markdown_pic1/main/Picgo/image-20230101160244104.png"/></div></div></p><p>使用浏览器方式打开即可。</p><p><strong>总结：</strong> 使用Echarts，重点在于研究当前图表所需的数据格式。通常是需要后端提供符合格式要求的动态数据，然后响应给前端来展示图表。</p><h2 id="营业额统计"><a href="#营业额统计" class="headerlink" title="营业额统计"></a>营业额统计</h2><h3 id="需求分析和设计-36"><a href="#需求分析和设计-36" class="headerlink" title="需求分析和设计"></a>需求分析和设计</h3><h4 id="产品原型-5"><a href="#产品原型-5" class="headerlink" title="产品原型"></a>产品原型</h4><p>营业额统计是基于<code>折现图</code>来展现，并且按照天来展示的。实际上，就是某一个时间范围之内的每一天的营业额。同时，不管光标放在哪个点上，那么它就会把具体的数值展示出来。并且还需要注意日期并不是固定写死的，是由上边时间选择器来决定。比如选择是近7天、或者是近30日，或者是本周，就会把相应这个时间段之内的每一天日期通过横坐标展示。</p><p><strong>原型图：</strong></p><div class="img-wrap"><div class="img-bg"><img class="img" src="https://raw.githubusercontent.com/silvan2077/markdown_pic1/main/Picgo/image-20230101160747433.png"/></div></div><div class="note info no-icon flat"><p><strong>业务规则：</strong></p><ul><li>营业额指订单状态为已完成的订单金额合计</li><li>基于可视化报表的折线图展示营业额数据，X轴为日期，Y轴为营业额</li><li>根据时间选择区间，展示每天的营业额数据</li></ul></div><h4 id="接口设计-8"><a href="#接口设计-8" class="headerlink" title="接口设计"></a>接口设计</h4><p>通过上述原型图，设计出对应的接口。</p><div class="img-wrap"><div class="img-bg"><img class="img" src="https://raw.githubusercontent.com/silvan2077/markdown_pic1/main/Picgo/image-20230101160801758.png"/></div></div><div class="img-wrap"><div class="img-bg"><img class="img" src="https://raw.githubusercontent.com/silvan2077/markdown_pic1/main/Picgo/image-20230101160812029.png"/></div></div><div class="note warning no-icon flat"><p><strong>注意：</strong> 具体返回数据一般由前端来决定，前端展示图表，具体折现图对应数据是什么格式，是有固定的要求的。所以说，后端需要去适应前端，它需要什么格式的数据，我们就给它返回什么格式的数据。</p></div><h3 id="代码开发-22"><a href="#代码开发-22" class="headerlink" title="代码开发"></a>代码开发</h3><h4 id="VO设计-2"><a href="#VO设计-2" class="headerlink" title="VO设计"></a>VO设计</h4><p><strong>根据接口定义设计对应的VO：</strong></p><div class="img-wrap"><div class="img-bg"><img class="img" src="https://raw.githubusercontent.com/silvan2077/markdown_pic1/main/Picgo/image-20230101164058056.png"/></div></div><p>在sky-pojo模块，TurnoverReportVO.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></pre></td><td class="code"><pre><span class="line"><span class="meta">@Data</span></span><br><span class="line"><span class="meta">@Builder</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="keyword">public</span> <span class="keyword">class</span> <span class="title class_">TurnoverReportVO</span> <span class="keyword">implements</span> <span class="title class_">Serializable</span> &#123;</span><br><span class="line"></span><br><span class="line">    <span class="comment">//日期，以逗号分隔，例如：2022-10-01,2022-10-02,2022-10-03</span></span><br><span class="line">    <span class="keyword">private</span> String dateList;</span><br><span class="line"></span><br><span class="line">    <span class="comment">//营业额，以逗号分隔，例如：406.0,1520.0,75.0</span></span><br><span class="line">    <span class="keyword">private</span> String turnoverList;</span><br><span class="line"></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h4 id="Controller层-17"><a href="#Controller层-17" class="headerlink" title="Controller层"></a>Controller层</h4><p><strong>根据接口定义创建ReportController：</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="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;/admin/report&quot;)</span></span><br><span class="line"><span class="meta">@Slf4j</span></span><br><span class="line"><span class="meta">@Api(tags = &quot;统计报表相关接口&quot;)</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">ReportController</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> ReportService reportService;</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">@param</span> begin</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@param</span> end</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@return</span></span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    <span class="meta">@GetMapping(&quot;/turnoverStatistics&quot;)</span></span><br><span class="line">    <span class="meta">@ApiOperation(&quot;营业额数据统计&quot;)</span></span><br><span class="line">    <span class="keyword">public</span> Result&lt;TurnoverReportVO&gt; <span class="title function_">turnoverStatistics</span><span class="params">(</span></span><br><span class="line"><span class="params">            <span class="meta">@DateTimeFormat(pattern = &quot;yyyy-MM-dd&quot;)</span></span></span><br><span class="line"><span class="params">                    LocalDate begin,</span></span><br><span class="line"><span class="params">            <span class="meta">@DateTimeFormat(pattern = &quot;yyyy-MM-dd&quot;)</span></span></span><br><span class="line"><span class="params">                    LocalDate end)</span> &#123;</span><br><span class="line">        <span class="keyword">return</span> Result.success(reportService.getTurnover(begin, end));</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h4 id="Service层-14"><a href="#Service层-14" class="headerlink" title="Service层"></a>Service层</h4><p><strong>创建ReportService接口，声明getTurnover方法：</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">interface</span> <span class="title class_">ReportService</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 class="doctag">@param</span> beginTime</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@param</span> endTime</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@return</span></span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    TurnoverReportVO <span class="title function_">getTurnover</span><span class="params">(LocalDate beginTime, LocalDate endTime)</span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p><strong>创建ReportServiceImpl实现类，实现getTurnover方法:</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">@Service</span></span><br><span class="line"><span class="meta">@Slf4j</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">ReportServiceImpl</span> <span class="keyword">implements</span> <span class="title class_">ReportService</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> 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 class="doctag">@param</span> begin</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@param</span> end</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@return</span></span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    <span class="keyword">public</span> TurnoverReportVO <span class="title function_">getTurnover</span><span class="params">(LocalDate begin, LocalDate end)</span> &#123;</span><br><span class="line">        List&lt;LocalDate&gt; dateList = <span class="keyword">new</span> <span class="title class_">ArrayList</span>&lt;&gt;();</span><br><span class="line">        dateList.add(begin);</span><br><span class="line"></span><br><span class="line">        <span class="keyword">while</span> (!begin.equals(end))&#123;</span><br><span class="line">            begin = begin.plusDays(<span class="number">1</span>);<span class="comment">//日期计算，获得指定日期后1天的日期</span></span><br><span class="line">            dateList.add(begin);</span><br><span class="line">        &#125;</span><br><span class="line">        </span><br><span class="line">       List&lt;Double&gt; turnoverList = <span class="keyword">new</span> <span class="title class_">ArrayList</span>&lt;&gt;();</span><br><span class="line">        <span class="keyword">for</span> (LocalDate date : dateList) &#123;</span><br><span class="line">            <span class="type">LocalDateTime</span> <span class="variable">beginTime</span> <span class="operator">=</span> LocalDateTime.of(date, LocalTime.MIN);</span><br><span class="line">            <span class="type">LocalDateTime</span> <span class="variable">endTime</span> <span class="operator">=</span> LocalDateTime.of(date, LocalTime.MAX);</span><br><span class="line">            <span class="type">Map</span> <span class="variable">map</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">HashMap</span>();</span><br><span class="line">        map.put(<span class="string">&quot;status&quot;</span>, Orders.COMPLETED);</span><br><span class="line">        map.put(<span class="string">&quot;begin&quot;</span>,beginTime);</span><br><span class="line">        map.put(<span class="string">&quot;end&quot;</span>, endTime);</span><br><span class="line">            <span class="type">Double</span> <span class="variable">turnover</span> <span class="operator">=</span> orderMapper.sumByMap(map); </span><br><span class="line">            turnover = turnover == <span class="literal">null</span> ? <span class="number">0.0</span> : turnover;</span><br><span class="line">            turnoverList.add(turnover);</span><br><span 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> TurnoverReportVO.builder()</span><br><span class="line">                .dateList(StringUtils.join(dateList,<span class="string">&quot;,&quot;</span>))</span><br><span class="line">                .turnoverList(StringUtils.join(turnoverList,<span class="string">&quot;,&quot;</span>))</span><br><span class="line">                .build();</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h4 id="Mapper层-16"><a href="#Mapper层-16" class="headerlink" title="Mapper层"></a>Mapper层</h4><p><strong>在OrderMapper接口声明sumByMap方法：</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></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">@param</span> map</span></span><br><span class="line"><span class="comment">    */</span></span><br><span class="line">   Double <span class="title function_">sumByMap</span><span class="params">(Map map)</span>;</span><br></pre></td></tr></table></figure><p><strong>在OrderMapper.xml文件中编写动态SQL：</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><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="tag">&lt;<span class="name">select</span> <span class="attr">id</span>=<span class="string">&quot;sumByMap&quot;</span> <span class="attr">resultType</span>=<span class="string">&quot;java.lang.Double&quot;</span>&gt;</span></span><br><span class="line">        select sum(amount) from orders</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">                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;begin != null&quot;</span>&gt;</span></span><br><span class="line">                and order_time <span class="symbol">&amp;gt;</span>= #&#123;begin&#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;end != null&quot;</span>&gt;</span></span><br><span class="line">                and order_time <span class="symbol">&amp;lt;</span>= #&#123;end&#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 class="tag">&lt;/<span class="name">select</span>&gt;</span></span><br></pre></td></tr></table></figure><h3 id="功能测试-23"><a href="#功能测试-23" class="headerlink" title="功能测试"></a>功能测试</h3><p>可以通过如下方式进行测试：</p><ul><li>接口文档测试</li><li>前后端联调测试</li></ul><p>启动服务器，启动nginx，直接采用前后端联调测试。</p><p>进入数据统计模块</p><p><strong>1. 查看近7日营业额统计</strong></p><p><img src="assets/image-20230101172807757.png" alt="image-20230101172807757" style="zoom:50%;" /> </p><p>进入开发者模式，查看返回数据</p><p><img src="assets/image-20230101173031357.png" alt="image-20230101173031357" style="zoom:80%;" /> </p><p><strong>2. 查看近30日营业额统计</strong></p><p><img src="assets/image-20230101173201667.png" alt="image-20230101173201667" style="zoom:50%;" /> </p><p>进入开发者模式，查看返回数据</p><p><img src="assets/image-20230101173304127.png" alt="image-20230101173304127" style="zoom:80%;" /> </p><p>也可通过断点方式启动，查看每步执行情况。</p><h2 id="用户统计"><a href="#用户统计" class="headerlink" title="用户统计"></a>用户统计</h2><h3 id="需求分析和设计-37"><a href="#需求分析和设计-37" class="headerlink" title="需求分析和设计"></a>需求分析和设计</h3><h4 id="产品原型-6"><a href="#产品原型-6" class="headerlink" title="产品原型"></a>产品原型</h4><p>所谓用户统计，实际上统计的是用户的数量。通过折线图来展示，上面这根蓝色线代表的是用户总量，下边这根绿色线代表的是新增用户数量，是具体到每一天。所以说用户统计主要统计<strong>两个数据</strong>，一个是<strong>总的用户数量</strong>，另外一个是<strong>新增用户数量</strong>。</p><p><strong>原型图：</strong></p><p><img src="assets/image-20230102213727736.png" alt="image-20230102213727736" style="zoom:50%;" /> </p><p><strong>业务规则：</strong></p><ul><li>基于可视化报表的折线图展示用户数据，X轴为日期，Y轴为用户数</li><li>根据时间选择区间，展示每天的用户总量和新增用户量数据</li></ul><h4 id="接口设计-9"><a href="#接口设计-9" class="headerlink" title="接口设计"></a>接口设计</h4><p>根据上述原型图设计接口。</p><p><img src="assets/image-20230102213809414.png" alt="image-20230102213809414" style="zoom:50%;" /> <img src="assets/image-20230102213818334.png" alt="image-20230102213818334" style="zoom:50%;" /> </p><h3 id="代码开发-23"><a href="#代码开发-23" class="headerlink" title="代码开发"></a>代码开发</h3><h4 id="VO设计-3"><a href="#VO设计-3" class="headerlink" title="VO设计"></a>VO设计</h4><p><strong>根据用户统计接口的返回结果设计VO：</strong></p><p><img src="assets/image-20230102211004237.png" alt="image-20230102211004237" style="zoom:50%;" /> </p><p>在sky-pojo模块，UserReportVO.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></pre></td><td class="code"><pre><span class="line"><span class="meta">@Data</span></span><br><span class="line"><span class="meta">@Builder</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="keyword">public</span> <span class="keyword">class</span> <span class="title class_">UserReportVO</span> <span class="keyword">implements</span> <span class="title class_">Serializable</span> &#123;</span><br><span class="line"></span><br><span class="line">    <span class="comment">//日期，以逗号分隔，例如：2022-10-01,2022-10-02,2022-10-03</span></span><br><span class="line">    <span class="keyword">private</span> String dateList;</span><br><span class="line"></span><br><span class="line">    <span class="comment">//用户总量，以逗号分隔，例如：200,210,220</span></span><br><span class="line">    <span class="keyword">private</span> String totalUserList;</span><br><span class="line"></span><br><span class="line">    <span class="comment">//新增用户，以逗号分隔，例如：20,21,10</span></span><br><span class="line">    <span class="keyword">private</span> String newUserList;</span><br><span class="line"></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h4 id="Controller层-18"><a href="#Controller层-18" class="headerlink" title="Controller层"></a>Controller层</h4><p><strong>根据接口定义，在ReportController中创建userStatistics方法：</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></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">@param</span> begin</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@param</span> end</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@return</span></span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    <span class="meta">@GetMapping(&quot;/userStatistics&quot;)</span></span><br><span class="line">    <span class="meta">@ApiOperation(&quot;用户数据统计&quot;)</span></span><br><span class="line">    <span class="keyword">public</span> Result&lt;UserReportVO&gt; <span class="title function_">userStatistics</span><span class="params">(</span></span><br><span class="line"><span class="params">            <span class="meta">@DateTimeFormat(pattern = &quot;yyyy-MM-dd&quot;)</span> LocalDate begin,</span></span><br><span class="line"><span class="params">            <span class="meta">@DateTimeFormat(pattern = &quot;yyyy-MM-dd&quot;)</span> LocalDate end)</span>&#123;</span><br><span class="line"></span><br><span class="line">        <span class="keyword">return</span> Result.success(reportService.getUserStatistics(begin,end));            </span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h4 id="Service层-15"><a href="#Service层-15" class="headerlink" title="Service层"></a>Service层</h4><p><strong>在ReportService接口中声明getUserStatistics方法：</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">/**</span></span><br><span class="line"><span class="comment">    * 根据时间区间统计用户数量</span></span><br><span class="line"><span class="comment">    * <span class="doctag">@param</span> begin</span></span><br><span class="line"><span class="comment">    * <span class="doctag">@param</span> end</span></span><br><span class="line"><span class="comment">    * <span class="doctag">@return</span></span></span><br><span class="line"><span class="comment">    */</span></span><br><span class="line">   UserReportVO <span class="title function_">getUserStatistics</span><span class="params">(LocalDate begin, LocalDate end)</span>;</span><br></pre></td></tr></table></figure><p><strong>在ReportServiceImpl实现类中实现getUserStatistics方法：</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="meta">@Override</span></span><br><span class="line">   <span class="keyword">public</span> UserReportVO <span class="title function_">getUserStatistics</span><span class="params">(LocalDate begin, LocalDate end)</span> &#123;</span><br><span class="line">       List&lt;LocalDate&gt; dateList = <span class="keyword">new</span> <span class="title class_">ArrayList</span>&lt;&gt;();</span><br><span class="line">       dateList.add(begin);</span><br><span class="line"></span><br><span class="line">       <span class="keyword">while</span> (!begin.equals(end))&#123;</span><br><span class="line">           begin = begin.plusDays(<span class="number">1</span>);</span><br><span class="line">           dateList.add(begin);</span><br><span class="line">       &#125;</span><br><span class="line">       List&lt;Integer&gt; newUserList = <span class="keyword">new</span> <span class="title class_">ArrayList</span>&lt;&gt;(); <span class="comment">//新增用户数</span></span><br><span class="line">       List&lt;Integer&gt; totalUserList = <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">for</span> (LocalDate date : dateList) &#123;</span><br><span class="line">           <span class="type">LocalDateTime</span> <span class="variable">beginTime</span> <span class="operator">=</span> LocalDateTime.of(date, LocalTime.MIN);</span><br><span class="line">           <span class="type">LocalDateTime</span> <span class="variable">endTime</span> <span class="operator">=</span> LocalDateTime.of(date, LocalTime.MAX);</span><br><span class="line">           <span class="comment">//新增用户数量 select count(id) from user where create_time &gt; ? and create_time &lt; ?</span></span><br><span class="line">           <span class="type">Integer</span> <span class="variable">newUser</span> <span class="operator">=</span> getUserCount(beginTime, endTime);</span><br><span class="line">           <span class="comment">//总用户数量 select count(id) from user where  create_time &lt; ?</span></span><br><span class="line">           <span class="type">Integer</span> <span class="variable">totalUser</span> <span class="operator">=</span> getUserCount(<span class="literal">null</span>, endTime);</span><br><span class="line"></span><br><span class="line">           newUserList.add(newUser);</span><br><span class="line">           totalUserList.add(totalUser);</span><br><span class="line">       &#125;</span><br><span class="line"></span><br><span class="line">       <span class="keyword">return</span> UserReportVO.builder()</span><br><span class="line">               .dateList(StringUtils.join(dateList,<span class="string">&quot;,&quot;</span>))</span><br><span class="line">               .newUserList(StringUtils.join(newUserList,<span class="string">&quot;,&quot;</span>))</span><br><span class="line">               .totalUserList(StringUtils.join(totalUserList,<span class="string">&quot;,&quot;</span>))</span><br><span class="line">               .build();</span><br><span class="line">   &#125;</span><br></pre></td></tr></table></figure><p><strong>在ReportServiceImpl实现类中创建私有方法getUserCount：</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="comment">/**</span></span><br><span class="line"><span class="comment">    * 根据时间区间统计用户数量</span></span><br><span class="line"><span class="comment">    * <span class="doctag">@param</span> beginTime</span></span><br><span class="line"><span class="comment">    * <span class="doctag">@param</span> endTime</span></span><br><span class="line"><span class="comment">    * <span class="doctag">@return</span></span></span><br><span class="line"><span class="comment">    */</span></span><br><span class="line">   <span class="keyword">private</span> Integer <span class="title function_">getUserCount</span><span class="params">(LocalDateTime beginTime, LocalDateTime endTime)</span> &#123;</span><br><span class="line">       <span class="type">Map</span> <span class="variable">map</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">HashMap</span>();</span><br><span class="line">       map.put(<span class="string">&quot;begin&quot;</span>,beginTime);</span><br><span class="line">       map.put(<span class="string">&quot;end&quot;</span>, endTime);</span><br><span class="line">       <span class="keyword">return</span> userMapper.countByMap(map);</span><br><span class="line">   &#125;</span><br></pre></td></tr></table></figure><h4 id="Mapper层-17"><a href="#Mapper层-17" class="headerlink" title="Mapper层"></a>Mapper层</h4><p><strong>在UserMapper接口中声明countByMap方法：</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></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">@param</span> map</span></span><br><span class="line"><span class="comment">    * <span class="doctag">@return</span></span></span><br><span class="line"><span class="comment">    */</span></span><br><span class="line">   Integer <span class="title function_">countByMap</span><span class="params">(Map map)</span>;</span><br></pre></td></tr></table></figure><p><strong>在UserMapper.xml文件中编写动态SQL：</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></pre></td><td class="code"><pre><span class="line">&lt;select id=<span class="string">&quot;countByMap&quot;</span> resultType=<span class="string">&quot;java.lang.Integer&quot;</span>&gt;</span><br><span class="line">        select <span class="title function_">count</span><span class="params">(id)</span> from user</span><br><span class="line">        &lt;where&gt;</span><br><span class="line">            &lt;<span class="keyword">if</span> test=<span class="string">&quot;begin != null&quot;</span>&gt;</span><br><span class="line">                and create_time &amp;gt;= #&#123;begin&#125;</span><br><span class="line">            &lt;/<span class="keyword">if</span>&gt;</span><br><span class="line">            &lt;<span class="keyword">if</span> test=<span class="string">&quot;end != null&quot;</span>&gt;</span><br><span class="line">                and create_time &amp;lt;= #&#123;end&#125;</span><br><span class="line">            &lt;/<span class="keyword">if</span>&gt;</span><br><span class="line">        &lt;/where&gt;</span><br><span class="line">&lt;/select&gt;</span><br></pre></td></tr></table></figure><h3 id="功能测试-24"><a href="#功能测试-24" class="headerlink" title="功能测试"></a>功能测试</h3><p>可以通过如下方式进行测试：</p><ul><li>接口文档测试</li><li>前后端联调测试</li></ul><p>进入数据统计模块</p><p><strong>1. 查看近7日用户统计</strong></p><p><img src="assets/image-20230107191339668.png" alt="image-20230107191339668" style="zoom:50%;" /> </p><p>进入开发者模式，查看返回数据</p><p><img src="assets/image-20230107191532175.png" alt="image-20230107191532175" style="zoom:50%;" /> </p><p><strong>2. 查看近30日用户统计</strong></p><p><img src="assets/image-20230107191613369.png" alt="image-20230107191613369" style="zoom:50%;" /> </p><p>进入开发者模式，查看返回数据</p><p><img src="assets/image-20230107191707568.png" alt="image-20230107191707568" style="zoom:50%;" /> </p><p>也可通过断点方式启动，查看每步执行情况。</p><h2 id="订单统计"><a href="#订单统计" class="headerlink" title="订单统计"></a>订单统计</h2><h3 id="需求分析和设计-38"><a href="#需求分析和设计-38" class="headerlink" title="需求分析和设计"></a>需求分析和设计</h3><h4 id="产品原型-7"><a href="#产品原型-7" class="headerlink" title="产品原型"></a>产品原型</h4><p>订单统计通过一个折现图来展现，折线图上有两根线，这根蓝色的线代表的是订单总数，而下边这根绿色的线代表的是有效订单数，指的就是状态是已完成的订单就属于有效订单，分别反映的是每一天的数据。上面还有3个数字，分别是订单总数、有效订单、订单完成率，它指的是整个时间区间之内总的数据。</p><p><strong>原型图：</strong></p><p><img src="assets/image-20230107192859270.png" alt="image-20230107192859270" style="zoom:50%;" /> </p><p><strong>业务规则：</strong></p><ul><li>有效订单指状态为 “已完成” 的订单</li><li>基于可视化报表的折线图展示订单数据，X轴为日期，Y轴为订单数量</li><li>根据时间选择区间，展示每天的订单总数和有效订单数</li><li>展示所选时间区间内的有效订单数、总订单数、订单完成率，订单完成率 = 有效订单数 / 总订单数 * 100%</li></ul><h4 id="接口设计-10"><a href="#接口设计-10" class="headerlink" title="接口设计"></a>接口设计</h4><p>根据上述原型图设计接口。</p><p><img src="assets/image-20230107192942872.png" alt="image-20230107192942872" style="zoom:50%;" /> <img src="assets/image-20230107192952958.png" alt="image-20230107192952958" style="zoom:50%;" /> </p><h3 id="代码开发-24"><a href="#代码开发-24" class="headerlink" title="代码开发"></a>代码开发</h3><h4 id="VO设计-4"><a href="#VO设计-4" class="headerlink" title="VO设计"></a>VO设计</h4><p><strong>根据订单统计接口的返回结果设计VO：</strong></p><p><img src="assets/image-20230107195325915.png" alt="image-20230107195325915" style="zoom:50%;" /> </p><p>在sky-pojo模块，OrderReportVO.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></pre></td><td class="code"><pre><span class="line"><span class="meta">@Data</span></span><br><span class="line"><span class="meta">@Builder</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="keyword">public</span> <span class="keyword">class</span> <span class="title class_">OrderReportVO</span> <span class="keyword">implements</span> <span class="title class_">Serializable</span> &#123;</span><br><span class="line"></span><br><span class="line">    <span class="comment">//日期，以逗号分隔，例如：2022-10-01,2022-10-02,2022-10-03</span></span><br><span class="line">    <span class="keyword">private</span> String dateList;</span><br><span class="line"></span><br><span class="line">    <span class="comment">//每日订单数，以逗号分隔，例如：260,210,215</span></span><br><span class="line">    <span class="keyword">private</span> String orderCountList;</span><br><span class="line"></span><br><span class="line">    <span class="comment">//每日有效订单数，以逗号分隔，例如：20,21,10</span></span><br><span class="line">    <span class="keyword">private</span> String validOrderCountList;</span><br><span class="line"></span><br><span class="line">    <span class="comment">//订单总数</span></span><br><span class="line">    <span class="keyword">private</span> Integer totalOrderCount;</span><br><span class="line"></span><br><span class="line">    <span class="comment">//有效订单数</span></span><br><span class="line">    <span class="keyword">private</span> Integer validOrderCount;</span><br><span class="line"></span><br><span class="line">    <span class="comment">//订单完成率</span></span><br><span class="line">    <span class="keyword">private</span> Double orderCompletionRate;</span><br><span class="line"></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h4 id="Controller层-19"><a href="#Controller层-19" class="headerlink" title="Controller层"></a>Controller层</h4><p><strong>在ReportController中根据订单统计接口创建orderStatistics方法：</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="comment">/**</span></span><br><span class="line"><span class="comment">     * 订单数据统计</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@param</span> begin</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@param</span> end</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@return</span></span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    <span class="meta">@GetMapping(&quot;/ordersStatistics&quot;)</span></span><br><span class="line">    <span class="meta">@ApiOperation(&quot;用户数据统计&quot;)</span></span><br><span class="line">    <span class="keyword">public</span> Result&lt;OrderReportVO&gt; <span class="title function_">orderStatistics</span><span class="params">(</span></span><br><span class="line"><span class="params">            <span class="meta">@DateTimeFormat(pattern = &quot;yyyy-MM-dd&quot;)</span></span></span><br><span class="line"><span class="params">                    LocalDate begin,</span></span><br><span class="line"><span class="params">            <span class="meta">@DateTimeFormat(pattern = &quot;yyyy-MM-dd&quot;)</span></span></span><br><span class="line"><span class="params">                    LocalDate end)</span>&#123;</span><br><span class="line"></span><br><span class="line">        <span class="keyword">return</span> Result.success(reportService.getOrderStatistics(begin,end));</span><br><span class="line">    &#125;</span><br></pre></td></tr></table></figure><h4 id="Service层-16"><a href="#Service层-16" class="headerlink" title="Service层"></a>Service层</h4><p><strong>在ReportService接口中声明getOrderStatistics方法：</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">/**</span></span><br><span class="line"><span class="comment">* 根据时间区间统计订单数量</span></span><br><span class="line"><span class="comment">* <span class="doctag">@param</span> begin </span></span><br><span class="line"><span class="comment">* <span class="doctag">@param</span> end</span></span><br><span class="line"><span class="comment">* <span class="doctag">@return</span> </span></span><br><span class="line"><span class="comment">*/</span></span><br><span class="line">OrderReportVO <span class="title function_">getOrderStatistics</span><span class="params">(LocalDate begin, LocalDate end)</span>;</span><br></pre></td></tr></table></figure><p><strong>在ReportServiceImpl实现类中实现getOrderStatistics方法：</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></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">@param</span> begin </span></span><br><span class="line"><span class="comment">* <span class="doctag">@param</span> end</span></span><br><span class="line"><span class="comment">* <span class="doctag">@return</span> </span></span><br><span class="line"><span class="comment">*/</span></span><br><span class="line"><span class="keyword">public</span> OrderReportVO <span class="title function_">getOrderStatistics</span><span class="params">(LocalDate begin, LocalDate end)</span>&#123;</span><br><span class="line">List&lt;LocalDate&gt; dateList = <span class="keyword">new</span> <span class="title class_">ArrayList</span>&lt;&gt;();</span><br><span class="line">    dateList.add(begin);</span><br><span class="line"></span><br><span class="line">    <span class="keyword">while</span> (!begin.equals(end))&#123;</span><br><span class="line">          begin = begin.plusDays(<span class="number">1</span>);</span><br><span class="line">          dateList.add(begin);</span><br><span class="line">     &#125;</span><br><span class="line">    <span class="comment">//每天订单总数集合</span></span><br><span class="line">     List&lt;Integer&gt; orderCountList = <span class="keyword">new</span> <span class="title class_">ArrayList</span>&lt;&gt;();</span><br><span class="line">    <span class="comment">//每天有效订单数集合</span></span><br><span class="line">    List&lt;Integer&gt; validOrderCountList = <span class="keyword">new</span> <span class="title class_">ArrayList</span>&lt;&gt;();</span><br><span class="line">    <span class="keyword">for</span> (LocalDate date : dateList) &#123;</span><br><span class="line">         <span class="type">LocalDateTime</span> <span class="variable">beginTime</span> <span class="operator">=</span> LocalDateTime.of(date, LocalTime.MIN);</span><br><span class="line">         <span class="type">LocalDateTime</span> <span class="variable">endTime</span> <span class="operator">=</span> LocalDateTime.of(date, LocalTime.MAX);</span><br><span class="line">   <span class="comment">//查询每天的总订单数 select count(id) from orders where order_time &gt; ? and order_time &lt; ?</span></span><br><span class="line">         <span class="type">Integer</span> <span class="variable">orderCount</span> <span class="operator">=</span> getOrderCount(beginTime, endTime, <span class="literal">null</span>);</span><br><span class="line"></span><br><span class="line">  <span class="comment">//查询每天的有效订单数 select count(id) from orders where order_time &gt; ? and order_time &lt; ? and status = ?</span></span><br><span class="line">         <span class="type">Integer</span> <span class="variable">validOrderCount</span> <span class="operator">=</span> getOrderCount(beginTime, endTime, Orders.COMPLETED);</span><br><span class="line"></span><br><span class="line">         orderCountList.add(orderCount);</span><br><span class="line">         validOrderCountList.add(validOrderCount);</span><br><span 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">Integer</span> <span class="variable">totalOrderCount</span> <span class="operator">=</span> orderCountList.stream().reduce(Integer::sum).get();</span><br><span class="line">    <span class="comment">//时间区间内的总有效订单数</span></span><br><span class="line">    <span class="type">Integer</span> <span class="variable">validOrderCount</span> <span class="operator">=</span> validOrderCountList.stream().reduce(Integer::sum).get();</span><br><span class="line">    <span class="comment">//订单完成率</span></span><br><span class="line">    <span class="type">Double</span> <span class="variable">orderCompletionRate</span> <span class="operator">=</span> <span class="number">0.0</span>;</span><br><span class="line">    <span class="keyword">if</span>(totalOrderCount != <span class="number">0</span>)&#123;</span><br><span class="line">         orderCompletionRate = validOrderCount.doubleValue() / totalOrderCount;</span><br><span class="line">     &#125;</span><br><span class="line">    <span class="keyword">return</span> OrderReportVO.builder()</span><br><span class="line">                .dateList(StringUtils.join(dateList, <span class="string">&quot;,&quot;</span>))</span><br><span class="line">                .orderCountList(StringUtils.join(orderCountList, <span class="string">&quot;,&quot;</span>))</span><br><span class="line">                .validOrderCountList(StringUtils.join(validOrderCountList, <span class="string">&quot;,&quot;</span>))</span><br><span class="line">                .totalOrderCount(totalOrderCount)</span><br><span class="line">                .validOrderCount(validOrderCount)</span><br><span class="line">                .orderCompletionRate(orderCompletionRate)</span><br><span class="line">                .build();</span><br><span class="line">    </span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p><strong>在ReportServiceImpl实现类中提供私有方法getOrderCount：</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></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">@param</span> beginTime</span></span><br><span class="line"><span class="comment">* <span class="doctag">@param</span> endTime</span></span><br><span class="line"><span class="comment">* <span class="doctag">@param</span> status</span></span><br><span class="line"><span class="comment">* <span class="doctag">@return</span></span></span><br><span class="line"><span class="comment">*/</span></span><br><span class="line"><span class="keyword">private</span> Integer <span class="title function_">getOrderCount</span><span class="params">(LocalDateTime beginTime, LocalDateTime endTime, Integer status)</span> &#123;</span><br><span class="line"><span class="type">Map</span> <span class="variable">map</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">HashMap</span>();</span><br><span class="line">map.put(<span class="string">&quot;status&quot;</span>, status);</span><br><span class="line">map.put(<span class="string">&quot;begin&quot;</span>,beginTime);</span><br><span class="line">map.put(<span class="string">&quot;end&quot;</span>, endTime);</span><br><span class="line"><span class="keyword">return</span> orderMapper.countByMap(map);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h4 id="Mapper层-18"><a href="#Mapper层-18" class="headerlink" title="Mapper层"></a>Mapper层</h4><p><strong>在OrderMapper接口中声明countByMap方法：</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></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">@param</span> map</span></span><br><span class="line"><span class="comment">*/</span></span><br><span class="line">Integer <span class="title function_">countByMap</span><span class="params">(Map map)</span>;</span><br></pre></td></tr></table></figure><p><strong>在OrderMapper.xml文件中编写动态SQL：</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></pre></td><td class="code"><pre><span class="line">&lt;select id=<span class="string">&quot;countByMap&quot;</span> resultType=<span class="string">&quot;java.lang.Integer&quot;</span>&gt;</span><br><span class="line">        select <span class="title function_">count</span><span class="params">(id)</span> from orders</span><br><span class="line">        &lt;where&gt;</span><br><span class="line">            &lt;<span class="keyword">if</span> test=<span class="string">&quot;status != null&quot;</span>&gt;</span><br><span class="line">                <span class="type">and</span> <span class="variable">status</span> <span class="operator">=</span> #&#123;status&#125;</span><br><span class="line">            &lt;/<span class="keyword">if</span>&gt;</span><br><span class="line">            &lt;<span class="keyword">if</span> test=<span class="string">&quot;begin != null&quot;</span>&gt;</span><br><span class="line">                and order_time &amp;gt;= #&#123;begin&#125;</span><br><span class="line">            &lt;/<span class="keyword">if</span>&gt;</span><br><span class="line">            &lt;<span class="keyword">if</span> test=<span class="string">&quot;end != null&quot;</span>&gt;</span><br><span class="line">                and order_time &amp;lt;= #&#123;end&#125;</span><br><span class="line">            &lt;/<span class="keyword">if</span>&gt;</span><br><span class="line">        &lt;/where&gt;</span><br><span class="line">&lt;/select&gt;</span><br></pre></td></tr></table></figure><h3 id="功能测试-25"><a href="#功能测试-25" class="headerlink" title="功能测试"></a>功能测试</h3><p>可以通过如下方式进行测试：</p><ul><li>接口文档测试</li><li>前后端联调</li></ul><p>重启服务，直接采用前后端联调测试。</p><p>进入数据统计模块</p><p><strong>1). 查看近7日订单统计</strong></p><p><img src="assets/image-20230107202854533.png" alt="image-20230107202854533" style="zoom:50%;" /> </p><p>进入开发者模式，查看返回数据</p><p><img src="assets/image-20230107202953128.png" alt="image-20230107202953128" style="zoom:50%;" /> </p><p><strong>2). 查看近30日订单统计</strong></p><p><img src="assets/image-20230107203025165.png" alt="image-20230107203025165" style="zoom:50%;" /> </p><p>进入开发者模式，查看返回数据</p><p><img src="assets/image-20230107203127308.png" alt="image-20230107203127308" style="zoom:50%;" /> </p><p>也可通过断点方式启动，查看每步执行情况。</p><h2 id="销量排名Top10"><a href="#销量排名Top10" class="headerlink" title="销量排名Top10"></a>销量排名Top10</h2><h3 id="需求分析和设计-39"><a href="#需求分析和设计-39" class="headerlink" title="需求分析和设计"></a>需求分析和设计</h3><h4 id="产品原型-8"><a href="#产品原型-8" class="headerlink" title="产品原型"></a>产品原型</h4><p>所谓销量排名，销量指的是商品销售的数量。项目当中的商品主要包含两类：一个是<strong>套餐</strong>，一个是<strong>菜品</strong>，所以销量排名其实指的就是菜品和套餐销售的数量排名。通过柱形图来展示销量排名，这些销量是按照降序来排列，并且只需要统计销量排名前十的商品。</p><p><strong>原型图：</strong></p><p><img src="assets/image-20230107203622747.png" alt="image-20230107203622747" style="zoom:50%;" /> </p><p><strong>业务规则：</strong></p><ul><li>根据时间选择区间，展示销量前10的商品（包括菜品和套餐）</li><li>基于可视化报表的柱状图降序展示商品销量</li><li>此处的销量为商品销售的份数</li></ul><h4 id="接口设计-11"><a href="#接口设计-11" class="headerlink" title="接口设计"></a>接口设计</h4><p>根据上述原型图设计接口。</p><p><img src="assets/image-20230107203720606.png" alt="image-20230107203720606" style="zoom:50%;" /> <img src="assets/image-20230107203730681.png" alt="image-20230107203730681" style="zoom:50%;" /> </p><h3 id="代码开发-25"><a href="#代码开发-25" class="headerlink" title="代码开发"></a>代码开发</h3><h4 id="VO设计-5"><a href="#VO设计-5" class="headerlink" title="VO设计"></a>VO设计</h4><p><strong>根据销量排名接口的返回结果设计VO：</strong></p><p><img src="assets/image-20230107204028895.png" alt="image-20230107204028895" style="zoom:50%;" /> </p><p>在sky-pojo模块，SalesTop10ReportVO.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></pre></td><td class="code"><pre><span class="line"><span class="meta">@Data</span></span><br><span class="line"><span class="meta">@Builder</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="keyword">public</span> <span class="keyword">class</span> <span class="title class_">SalesTop10ReportVO</span> <span class="keyword">implements</span> <span class="title class_">Serializable</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 nameList;</span><br><span class="line"></span><br><span class="line">    <span class="comment">//销量列表，以逗号分隔，例如：260,215,200</span></span><br><span class="line">    <span class="keyword">private</span> String numberList;</span><br><span class="line"></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h4 id="Controller层-20"><a href="#Controller层-20" class="headerlink" title="Controller层"></a>Controller层</h4><p><strong>在ReportController中根据销量排名接口创建top10方法：</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></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">@param</span> begin</span></span><br><span class="line"><span class="comment">* <span class="doctag">@param</span> end</span></span><br><span class="line"><span class="comment">* <span class="doctag">@return</span></span></span><br><span class="line"><span class="comment">*/</span></span><br><span class="line"><span class="meta">@GetMapping(&quot;/top10&quot;)</span></span><br><span class="line"><span class="meta">@ApiOperation(&quot;销量排名统计&quot;)</span></span><br><span class="line"><span class="keyword">public</span> Result&lt;SalesTop10ReportVO&gt; <span class="title function_">top10</span><span class="params">(</span></span><br><span class="line"><span class="params">    <span class="meta">@DateTimeFormat(pattern = &quot;yyyy-MM-dd&quot;)</span> LocalDate begin,</span></span><br><span class="line"><span class="params">    <span class="meta">@DateTimeFormat(pattern = &quot;yyyy-MM-dd&quot;)</span> LocalDate end)</span>&#123;</span><br><span class="line"><span class="keyword">return</span> Result.success(reportService.getSalesTop10(begin,end));</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h4 id="5-2-3-Service层接口"><a href="#5-2-3-Service层接口" class="headerlink" title="5.2.3 Service层接口"></a>5.2.3 Service层接口</h4><p><strong>在ReportService接口中声明getSalesTop10方法：</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">/**</span></span><br><span class="line"><span class="comment">* 查询指定时间区间内的销量排名top10 </span></span><br><span class="line"><span class="comment">* <span class="doctag">@param</span> begin</span></span><br><span class="line"><span class="comment">* <span class="doctag">@param</span> end</span></span><br><span class="line"><span class="comment">* <span class="doctag">@return</span></span></span><br><span class="line"><span class="comment">*/</span></span><br><span class="line">SalesTop10ReportVO <span class="title function_">getSalesTop10</span><span class="params">(LocalDate begin, LocalDate end)</span>;</span><br></pre></td></tr></table></figure><h4 id="Service层实现类-2"><a href="#Service层实现类-2" class="headerlink" title="Service层实现类"></a>Service层实现类</h4><p><strong>在ReportServiceImpl实现类中实现getSalesTop10方法：</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></pre></td><td class="code"><pre><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment">     * 查询指定时间区间内的销量排名top10</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@param</span> begin</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@param</span> end</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@return</span></span></span><br><span class="line"><span class="comment">     * */</span></span><br><span class="line">    <span class="keyword">public</span> SalesTop10ReportVO <span class="title function_">getSalesTop10</span><span class="params">(LocalDate begin, LocalDate end)</span>&#123;</span><br><span class="line">        <span class="type">LocalDateTime</span> <span class="variable">beginTime</span> <span class="operator">=</span> LocalDateTime.of(begin, LocalTime.MIN);</span><br><span class="line">        <span class="type">LocalDateTime</span> <span class="variable">endTime</span> <span class="operator">=</span> LocalDateTime.of(end, LocalTime.MAX);</span><br><span class="line">        List&lt;GoodsSalesDTO&gt; goodsSalesDTOList = orderMapper.getSalesTop10(beginTime, endTime);</span><br><span class="line"></span><br><span class="line">        <span class="type">String</span> <span class="variable">nameList</span> <span class="operator">=</span> StringUtils.join(goodsSalesDTOList.stream().map(GoodsSalesDTO::getName).collect(Collectors.toList()),<span class="string">&quot;,&quot;</span>);</span><br><span class="line">        <span class="type">String</span> <span class="variable">numberList</span> <span class="operator">=</span> StringUtils.join(goodsSalesDTOList.stream().map(GoodsSalesDTO::getNumber).collect(Collectors.toList()),<span class="string">&quot;,&quot;</span>);</span><br><span class="line"></span><br><span class="line">        <span class="keyword">return</span> SalesTop10ReportVO.builder()</span><br><span class="line">                .nameList(nameList)</span><br><span class="line">                .numberList(numberList)</span><br><span class="line">                .build();</span><br><span class="line">    &#125;</span><br></pre></td></tr></table></figure><h4 id="Mapper层-19"><a href="#Mapper层-19" class="headerlink" title="Mapper层"></a>Mapper层</h4><p><strong>在OrderMapper接口中声明getSalesTop10方法：</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></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">@param</span> begin</span></span><br><span class="line"><span class="comment">* <span class="doctag">@param</span> end</span></span><br><span class="line"><span class="comment">*/</span></span><br><span class="line">List&lt;GoodsSalesDTO&gt; <span class="title function_">getSalesTop10</span><span class="params">(LocalDateTime begin, LocalDateTime end)</span>;</span><br></pre></td></tr></table></figure><p><strong>在OrderMapper.xml文件中编写动态SQL：</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><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="tag">&lt;<span class="name">select</span> <span class="attr">id</span>=<span class="string">&quot;getSalesTop10&quot;</span> <span class="attr">resultType</span>=<span class="string">&quot;com.sky.dto.GoodsSalesDTO&quot;</span>&gt;</span></span><br><span class="line">        select od.name name,sum(od.number) number from order_detail od ,orders o</span><br><span class="line">        where od.order_id = o.id</span><br><span class="line">            and o.status = 5</span><br><span class="line">            <span class="tag">&lt;<span class="name">if</span> <span class="attr">test</span>=<span class="string">&quot;begin != null&quot;</span>&gt;</span></span><br><span class="line">                and order_time <span class="symbol">&amp;gt;</span>= #&#123;begin&#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;end != null&quot;</span>&gt;</span></span><br><span class="line">                and order_time <span class="symbol">&amp;lt;</span>= #&#123;end&#125;</span><br><span class="line">            <span class="tag">&lt;/<span class="name">if</span>&gt;</span></span><br><span class="line">        group by name</span><br><span class="line">        order by number desc</span><br><span class="line">        limit 0, 10</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="功能测试-26"><a href="#功能测试-26" class="headerlink" title="功能测试"></a>功能测试</h3><p>可以通过如下方式进行测试：</p><ul><li>接口文档测试</li><li>前后端联调</li></ul><p>重启服务，直接采用前后端联调测试。</p><p><strong>查看近30日销量排名Top10统计</strong></p><p>若查询的某一段时间没有销量数据，则显示不出效果。</p><p><img src="assets/image-20230107210518821.png" alt="image-20230107210518821" style="zoom:50%;" /> </p><p>进入开发者模式，查看返回数据</p><p><img src="assets/image-20230107210711326.png" alt="image-20230107210711326" style="zoom:50%;" /> </p><p>也可通过断点方式启动，查看每步执行情况。</p><h1 id="day12"><a href="#day12" class="headerlink" title="day12"></a>day12</h1><h2 id="课程内容-7"><a href="#课程内容-7" class="headerlink" title="课程内容"></a>课程内容</h2><ul><li>工作台</li><li>Apache POI</li><li>导出运营数据Excel报表</li></ul><p>功能实现：<strong>工作台</strong>、<strong>数据导出</strong></p><p><strong>工作台效果图：</strong></p><div class="img-wrap"><div class="img-bg"><img class="img" src="https://raw.githubusercontent.com/silvan2077/markdown_pic1/main/Picgo/image-20230130190031553.png"/></div></div><p><strong>数据导出效果图：</strong></p><div class="img-wrap"><div class="img-bg"><img class="img" src="https://raw.githubusercontent.com/silvan2077/markdown_pic1/main/Picgo/image-20230130190124725.png"/></div></div><p>在数据统计页面点击<strong>数据导出</strong>：生成Excel报表<br><div class="img-wrap"><div class="img-bg"><img class="img" src="https://raw.githubusercontent.com/silvan2077/markdown_pic1/main/Picgo/image-20230130190243865.png"/></div></div></p><h2 id="工作台"><a href="#工作台" class="headerlink" title="工作台"></a>工作台</h2><h3 id="需求分析和设计-40"><a href="#需求分析和设计-40" class="headerlink" title="需求分析和设计"></a>需求分析和设计</h3><h4 id="产品原型-9"><a href="#产品原型-9" class="headerlink" title="产品原型"></a>产品原型</h4><p>工作台是系统运营的数据看板，并提供快捷操作入口，可以有效提高商家的工作效率。</p><p><strong>工作台展示的数据：</strong></p><ul><li>今日数据</li><li>订单管理</li><li>菜品总览</li><li>套餐总览</li><li>订单信息</li></ul><p><strong>原型图：</strong><br><div class="img-wrap"><div class="img-bg"><img class="img" src="https://raw.githubusercontent.com/silvan2077/markdown_pic1/main/Picgo/image-20230130191051003.png"/></div></div></p><div class="note info no-icon flat"><p><strong>名词解释：</strong></p><ul><li>营业额：已完成订单的总金额</li><li>有效订单：已完成订单的数量</li><li>订单完成率：有效订单数 / 总订单数 * 100%</li><li>平均客单价：营业额 / 有效订单数</li><li>新增用户：新增用户的数量</li></ul></div><h4 id="接口设计-12"><a href="#接口设计-12" class="headerlink" title="接口设计"></a>接口设计</h4><p>通过上述原型图分析，共包含6个接口。</p><p><strong>接口设计：</strong></p><ul><li>今日数据接口</li><li>订单管理接口</li><li>菜品总览接口</li><li>套餐总览接口</li><li>订单搜索（已完成）</li><li>各个状态的订单数量统计（已完成）</li></ul><div class="tabs" id="工作台接口设计"><ul class="nav-tabs"><li class="tab active"><button type="button" data-href="#工作台接口设计-1">今日数据</button></li><li class="tab"><button type="button" data-href="#工作台接口设计-2">订单管理</button></li><li class="tab"><button type="button" data-href="#工作台接口设计-3">菜品总览</button></li><li class="tab"><button type="button" data-href="#工作台接口设计-4">套餐总览</button></li></ul><div class="tab-contents"><div class="tab-item-content active" id="工作台接口设计-1"><div class="img-wrap"><div class="img-bg"><img class="img" src="https://raw.githubusercontent.com/silvan2077/markdown_pic1/main/Picgo/image-20230130191820764.png"/></div></div><div class="img-wrap"><div class="img-bg"><img class="img" src="https://raw.githubusercontent.com/silvan2077/markdown_pic1/main/Picgo/image-20230130191837090.png"/></div></div><button type="button" class="tab-to-top" aria-label="scroll to top"><i class="fas fa-arrow-up"></i></button></div><div class="tab-item-content" id="工作台接口设计-2"><div class="img-wrap"><div class="img-bg"><img class="img" src="https://raw.githubusercontent.com/silvan2077/markdown_pic1/main/Picgo/image-20230130192105373.png"/></div></div><button type="button" class="tab-to-top" aria-label="scroll to top"><i class="fas fa-arrow-up"></i></button></div><div class="tab-item-content" id="工作台接口设计-3"><div class="img-wrap"><div class="img-bg"><img class="img" src="https://raw.githubusercontent.com/silvan2077/markdown_pic1/main/Picgo/image-20230130192225168.png"/></div></div><button type="button" class="tab-to-top" aria-label="scroll to top"><i class="fas fa-arrow-up"></i></button></div><div class="tab-item-content" id="工作台接口设计-4"><div class="img-wrap"><div class="img-bg"><img class="img" src="https://raw.githubusercontent.com/silvan2077/markdown_pic1/main/Picgo/image-20230130195425389.png"/></div></div><button type="button" class="tab-to-top" aria-label="scroll to top"><i class="fas fa-arrow-up"></i></button></div></div></div><h3 id="代码导入-3"><a href="#代码导入-3" class="headerlink" title="代码导入"></a>代码导入</h3><p>直接导入课程资料中的工作台模块功能代码即可：<br><div class="img-wrap"><div class="img-bg"><img class="img" src="https://raw.githubusercontent.com/silvan2077/markdown_pic1/main/Picgo/image-20230130195535421.png"/></div></div></p><h4 id="Controller层-21"><a href="#Controller层-21" class="headerlink" title="Controller层"></a>Controller层</h4><p><strong>添加WorkSpaceController.java</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></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;/admin/workspace&quot;)</span></span><br><span class="line"><span class="meta">@Slf4j</span></span><br><span class="line"><span class="meta">@Api(tags = &quot;工作台相关接口&quot;)</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">WorkSpaceController</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> WorkspaceService workspaceService;</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 class="doctag">@return</span></span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    <span class="meta">@GetMapping(&quot;/businessData&quot;)</span></span><br><span class="line">    <span class="meta">@ApiOperation(&quot;工作台今日数据查询&quot;)</span></span><br><span class="line">    <span class="keyword">public</span> Result&lt;BusinessDataVO&gt; <span class="title function_">businessData</span><span class="params">()</span>&#123;</span><br><span class="line">        <span class="comment">//获得当天的开始时间</span></span><br><span class="line">        <span class="type">LocalDateTime</span> <span class="variable">begin</span> <span class="operator">=</span> LocalDateTime.now().with(LocalTime.MIN);</span><br><span class="line">        <span class="comment">//获得当天的结束时间</span></span><br><span class="line">        <span class="type">LocalDateTime</span> <span class="variable">end</span> <span class="operator">=</span> LocalDateTime.now().with(LocalTime.MAX);</span><br><span class="line"></span><br><span class="line">        <span class="type">BusinessDataVO</span> <span class="variable">businessDataVO</span> <span class="operator">=</span> workspaceService.getBusinessData(begin, end);</span><br><span class="line">        <span class="keyword">return</span> Result.success(businessDataVO);</span><br><span 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 class="doctag">@return</span></span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    <span class="meta">@GetMapping(&quot;/overviewOrders&quot;)</span></span><br><span class="line">    <span class="meta">@ApiOperation(&quot;查询订单管理数据&quot;)</span></span><br><span class="line">    <span class="keyword">public</span> Result&lt;OrderOverViewVO&gt; <span class="title function_">orderOverView</span><span class="params">()</span>&#123;</span><br><span class="line">        <span class="keyword">return</span> Result.success(workspaceService.getOrderOverView());</span><br><span 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 class="doctag">@return</span></span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    <span class="meta">@GetMapping(&quot;/overviewDishes&quot;)</span></span><br><span class="line">    <span class="meta">@ApiOperation(&quot;查询菜品总览&quot;)</span></span><br><span class="line">    <span class="keyword">public</span> Result&lt;DishOverViewVO&gt; <span class="title function_">dishOverView</span><span class="params">()</span>&#123;</span><br><span class="line">        <span class="keyword">return</span> Result.success(workspaceService.getDishOverView());</span><br><span 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 class="doctag">@return</span></span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    <span class="meta">@GetMapping(&quot;/overviewSetmeals&quot;)</span></span><br><span class="line">    <span class="meta">@ApiOperation(&quot;查询套餐总览&quot;)</span></span><br><span class="line">    <span class="keyword">public</span> Result&lt;SetmealOverViewVO&gt; <span class="title function_">setmealOverView</span><span class="params">()</span>&#123;</span><br><span class="line">        <span class="keyword">return</span> Result.success(workspaceService.getSetmealOverView());</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h4 id="Service层-17"><a href="#Service层-17" class="headerlink" title="Service层"></a>Service层</h4><p><strong>添加WorkspaceService.java</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><br><span class="line"><span class="keyword">public</span> <span class="keyword">interface</span> <span class="title class_">WorkspaceService</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 class="doctag">@param</span> begin</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@param</span> end</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@return</span></span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    BusinessDataVO <span class="title function_">getBusinessData</span><span class="params">(LocalDateTime begin, LocalDateTime end)</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 class="doctag">@return</span></span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    OrderOverViewVO <span class="title function_">getOrderOverView</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 class="doctag">@return</span></span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    DishOverViewVO <span class="title function_">getDishOverView</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 class="doctag">@return</span></span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    SetmealOverViewVO <span class="title function_">getSetmealOverView</span><span class="params">()</span>;</span><br><span class="line"></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p><strong>添加WorkspaceServiceImpl.java</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><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></pre></td><td class="code"><pre><span class="line"></span><br><span class="line"><span class="meta">@Service</span></span><br><span class="line"><span class="meta">@Slf4j</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">WorkspaceServiceImpl</span> <span class="keyword">implements</span> <span class="title class_">WorkspaceService</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> OrderMapper orderMapper;</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 class="meta">@Autowired</span></span><br><span class="line">    <span class="keyword">private</span> DishMapper dishMapper;</span><br><span class="line">    <span class="meta">@Autowired</span></span><br><span class="line">    <span class="keyword">private</span> SetmealMapper setmealMapper;</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 class="doctag">@param</span> begin</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@param</span> end</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@return</span></span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    <span class="keyword">public</span> BusinessDataVO <span class="title function_">getBusinessData</span><span class="params">(LocalDateTime begin, LocalDateTime end)</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"><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="type">Map</span> <span class="variable">map</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">HashMap</span>();</span><br><span class="line">        map.put(<span class="string">&quot;begin&quot;</span>,begin);</span><br><span class="line">        map.put(<span class="string">&quot;end&quot;</span>,end);</span><br><span class="line"></span><br><span class="line">        <span class="comment">//查询总订单数</span></span><br><span class="line">        <span class="type">Integer</span> <span class="variable">totalOrderCount</span> <span class="operator">=</span> orderMapper.countByMap(map);</span><br><span class="line"></span><br><span class="line">        map.put(<span class="string">&quot;status&quot;</span>, Orders.COMPLETED);</span><br><span class="line">        <span class="comment">//营业额</span></span><br><span class="line">        <span class="type">Double</span> <span class="variable">turnover</span> <span class="operator">=</span> orderMapper.sumByMap(map);</span><br><span class="line">        turnover = turnover == <span class="literal">null</span>? <span class="number">0.0</span> : turnover;</span><br><span class="line"></span><br><span class="line">        <span class="comment">//有效订单数</span></span><br><span class="line">        <span class="type">Integer</span> <span class="variable">validOrderCount</span> <span class="operator">=</span> orderMapper.countByMap(map);</span><br><span class="line"></span><br><span class="line">        <span class="type">Double</span> <span class="variable">unitPrice</span> <span class="operator">=</span> <span class="number">0.0</span>;</span><br><span class="line"></span><br><span class="line">        <span class="type">Double</span> <span class="variable">orderCompletionRate</span> <span class="operator">=</span> <span class="number">0.0</span>;</span><br><span class="line">        <span class="keyword">if</span>(totalOrderCount != <span class="number">0</span> &amp;&amp; validOrderCount != <span class="number">0</span>)&#123;</span><br><span class="line">            <span class="comment">//订单完成率</span></span><br><span class="line">            orderCompletionRate = validOrderCount.doubleValue() / totalOrderCount;</span><br><span class="line">            <span class="comment">//平均客单价</span></span><br><span class="line">            unitPrice = turnover / validOrderCount;</span><br><span 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">Integer</span> <span class="variable">newUsers</span> <span class="operator">=</span> userMapper.countByMap(map);</span><br><span class="line"></span><br><span class="line">        <span class="keyword">return</span> BusinessDataVO.builder()</span><br><span class="line">                .turnover(turnover)</span><br><span class="line">                .validOrderCount(validOrderCount)</span><br><span class="line">                .orderCompletionRate(orderCompletionRate)</span><br><span class="line">                .unitPrice(unitPrice)</span><br><span class="line">                .newUsers(newUsers)</span><br><span class="line">                .build();</span><br><span class="line">    &#125;</span><br><span class="line"></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">@return</span></span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    <span class="keyword">public</span> OrderOverViewVO <span class="title function_">getOrderOverView</span><span class="params">()</span> &#123;</span><br><span class="line">        <span class="type">Map</span> <span class="variable">map</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">HashMap</span>();</span><br><span class="line">        map.put(<span class="string">&quot;begin&quot;</span>, LocalDateTime.now().with(LocalTime.MIN));</span><br><span class="line">        map.put(<span class="string">&quot;status&quot;</span>, Orders.TO_BE_CONFIRMED);</span><br><span class="line"></span><br><span class="line">        <span class="comment">//待接单</span></span><br><span class="line">        <span class="type">Integer</span> <span class="variable">waitingOrders</span> <span class="operator">=</span> orderMapper.countByMap(map);</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;status&quot;</span>, Orders.CONFIRMED);</span><br><span class="line">        <span class="type">Integer</span> <span class="variable">deliveredOrders</span> <span class="operator">=</span> orderMapper.countByMap(map);</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;status&quot;</span>, Orders.COMPLETED);</span><br><span class="line">        <span class="type">Integer</span> <span class="variable">completedOrders</span> <span class="operator">=</span> orderMapper.countByMap(map);</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;status&quot;</span>, Orders.CANCELLED);</span><br><span class="line">        <span class="type">Integer</span> <span class="variable">cancelledOrders</span> <span class="operator">=</span> orderMapper.countByMap(map);</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;status&quot;</span>, <span class="literal">null</span>);</span><br><span class="line">        <span class="type">Integer</span> <span class="variable">allOrders</span> <span class="operator">=</span> orderMapper.countByMap(map);</span><br><span class="line"></span><br><span class="line">        <span class="keyword">return</span> OrderOverViewVO.builder()</span><br><span class="line">                .waitingOrders(waitingOrders)</span><br><span class="line">                .deliveredOrders(deliveredOrders)</span><br><span class="line">                .completedOrders(completedOrders)</span><br><span class="line">                .cancelledOrders(cancelledOrders)</span><br><span class="line">                .allOrders(allOrders)</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="comment">     * <span class="doctag">@return</span></span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    <span class="keyword">public</span> DishOverViewVO <span class="title function_">getDishOverView</span><span class="params">()</span> &#123;</span><br><span class="line">        <span class="type">Map</span> <span class="variable">map</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">HashMap</span>();</span><br><span class="line">        map.put(<span class="string">&quot;status&quot;</span>, StatusConstant.ENABLE);</span><br><span class="line">        <span class="type">Integer</span> <span class="variable">sold</span> <span class="operator">=</span> dishMapper.countByMap(map);</span><br><span class="line"></span><br><span class="line">        map.put(<span class="string">&quot;status&quot;</span>, StatusConstant.DISABLE);</span><br><span class="line">        <span class="type">Integer</span> <span class="variable">discontinued</span> <span class="operator">=</span> dishMapper.countByMap(map);</span><br><span class="line"></span><br><span class="line">        <span class="keyword">return</span> DishOverViewVO.builder()</span><br><span class="line">                .sold(sold)</span><br><span class="line">                .discontinued(discontinued)</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="comment">     * <span class="doctag">@return</span></span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    <span class="keyword">public</span> SetmealOverViewVO <span class="title function_">getSetmealOverView</span><span class="params">()</span> &#123;</span><br><span class="line">        <span class="type">Map</span> <span class="variable">map</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">HashMap</span>();</span><br><span class="line">        map.put(<span class="string">&quot;status&quot;</span>, StatusConstant.ENABLE);</span><br><span class="line">        <span class="type">Integer</span> <span class="variable">sold</span> <span class="operator">=</span> setmealMapper.countByMap(map);</span><br><span class="line"></span><br><span class="line">        map.put(<span class="string">&quot;status&quot;</span>, StatusConstant.DISABLE);</span><br><span class="line">        <span class="type">Integer</span> <span class="variable">discontinued</span> <span class="operator">=</span> setmealMapper.countByMap(map);</span><br><span class="line"></span><br><span class="line">        <span class="keyword">return</span> SetmealOverViewVO.builder()</span><br><span class="line">                .sold(sold)</span><br><span class="line">                .discontinued(discontinued)</span><br><span class="line">                .build();</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h4 id="Mapper层-20"><a href="#Mapper层-20" class="headerlink" title="Mapper层"></a>Mapper层</h4><p><strong>在SetmealMapper中添加countByMap方法定义</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></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">@param</span> map</span></span><br><span class="line"><span class="comment">    * <span class="doctag">@return</span></span></span><br><span class="line"><span class="comment">    */</span></span><br><span class="line">   Integer <span class="title function_">countByMap</span><span class="params">(Map map)</span>;</span><br></pre></td></tr></table></figure><p><strong>在SetmealMapper.xml中添加对应SQL实现</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><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line"><span class="tag">&lt;<span class="name">select</span> <span class="attr">id</span>=<span class="string">&quot;countByMap&quot;</span> <span class="attr">resultType</span>=<span class="string">&quot;java.lang.Integer&quot;</span>&gt;</span></span><br><span class="line">        select count(id) from setmeal</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">                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;categoryId != null&quot;</span>&gt;</span></span><br><span class="line">                and category_id = #&#123;categoryId&#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 class="tag">&lt;/<span class="name">select</span>&gt;</span></span><br></pre></td></tr></table></figure><p><strong>在DishMapper中添加countByMap方法定义</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></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">@param</span> map</span></span><br><span class="line"><span class="comment">    * <span class="doctag">@return</span></span></span><br><span class="line"><span class="comment">    */</span></span><br><span class="line">   Integer <span class="title function_">countByMap</span><span class="params">(Map map)</span>;</span><br></pre></td></tr></table></figure><p><strong>在DishMapper.xml中添加对应SQL实现</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><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line"><span class="tag">&lt;<span class="name">select</span> <span class="attr">id</span>=<span class="string">&quot;countByMap&quot;</span> <span class="attr">resultType</span>=<span class="string">&quot;java.lang.Integer&quot;</span>&gt;</span></span><br><span class="line">        select count(id) from dish</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">                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;categoryId != null&quot;</span>&gt;</span></span><br><span class="line">                and category_id = #&#123;categoryId&#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 class="tag">&lt;/<span class="name">select</span>&gt;</span></span><br></pre></td></tr></table></figure><h3 id="功能测试-27"><a href="#功能测试-27" class="headerlink" title="功能测试"></a>功能测试</h3><p>可以通过如下方式进行测试：</p><ul><li>通过接口文档测试</li><li>前后端联调测试</li></ul><h4 id="接口文档测试-4"><a href="#接口文档测试-4" class="headerlink" title="接口文档测试"></a>接口文档测试</h4><p><strong>启动服务</strong>，访问<a href="http://localhost:8080/doc.html">http://localhost:8080/doc.html</a>，进入工作台相关接口</p><div class="tabs" id="接口文档测试"><ul class="nav-tabs"><li class="tab active"><button type="button" data-href="#接口文档测试-1">今日数据查询</button></li><li class="tab"><button type="button" data-href="#接口文档测试-2">菜品总览查询</button></li><li class="tab"><button type="button" data-href="#接口文档测试-3">订单管理数据查询</button></li><li class="tab"><button type="button" data-href="#接口文档测试-4">套餐总览查询</button></li></ul><div class="tab-contents"><div class="tab-item-content active" id="接口文档测试-1"><div class="img-wrap"><div class="img-bg"><img class="img" src="https://raw.githubusercontent.com/silvan2077/markdown_pic1/main/Picgo/image-20230131103600918.png"/></div></div><button type="button" class="tab-to-top" aria-label="scroll to top"><i class="fas fa-arrow-up"></i></button></div><div class="tab-item-content" id="接口文档测试-2"><div class="img-wrap"><div class="img-bg"><img class="img" src="https://raw.githubusercontent.com/silvan2077/markdown_pic1/main/Picgo/image-20230131103714898.png"/></div></div><button type="button" class="tab-to-top" aria-label="scroll to top"><i class="fas fa-arrow-up"></i></button></div><div class="tab-item-content" id="接口文档测试-3"><div class="img-wrap"><div class="img-bg"><img class="img" src="https://raw.githubusercontent.com/silvan2077/markdown_pic1/main/Picgo/image-20230131103808342.png"/></div></div><button type="button" class="tab-to-top" aria-label="scroll to top"><i class="fas fa-arrow-up"></i></button></div><div class="tab-item-content" id="接口文档测试-4"><div class="img-wrap"><div class="img-bg"><img class="img" src="https://raw.githubusercontent.com/silvan2077/markdown_pic1/main/Picgo/image-20230131103903802.png"/></div></div><button type="button" class="tab-to-top" aria-label="scroll to top"><i class="fas fa-arrow-up"></i></button></div></div></div><h4 id="前后端联调测试-4"><a href="#前后端联调测试-4" class="headerlink" title="前后端联调测试"></a>前后端联调测试</h4><p><strong>启动nginx</strong>,访问 <a href="http://localhost">http://localhost</a>，进入工作台<br> <div class="img-wrap"><div class="img-bg"><img class="img" src="https://raw.githubusercontent.com/silvan2077/markdown_pic1/main/Picgo/image-20230131104243713.png"/></div></div></p><p>进入开发者模式，分别查看今日数据、订单管理、菜品总览、套餐总览</p><div class="tabs" id="前后端联调测试"><ul class="nav-tabs"><li class="tab active"><button type="button" data-href="#前后端联调测试-1">今日数据查询</button></li><li class="tab"><button type="button" data-href="#前后端联调测试-2">菜品总览查询</button></li><li class="tab"><button type="button" data-href="#前后端联调测试-3">订单管理数据查询</button></li><li class="tab"><button type="button" data-href="#前后端联调测试-4">套餐总览查询</button></li></ul><div class="tab-contents"><div class="tab-item-content active" id="前后端联调测试-1"><div class="img-wrap"><div class="img-bg"><img class="img" src="https://raw.githubusercontent.com/silvan2077/markdown_pic1/main/Picgo/image-20230131104849876.png"/></div></div><button type="button" class="tab-to-top" aria-label="scroll to top"><i class="fas fa-arrow-up"></i></button></div><div class="tab-item-content" id="前后端联调测试-2"><div class="img-wrap"><div class="img-bg"><img class="img" src="https://raw.githubusercontent.com/silvan2077/markdown_pic1/main/Picgo/image-20230131104939694.png"/></div></div><button type="button" class="tab-to-top" aria-label="scroll to top"><i class="fas fa-arrow-up"></i></button></div><div class="tab-item-content" id="前后端联调测试-3"><div class="img-wrap"><div class="img-bg"><img class="img" src="https://raw.githubusercontent.com/silvan2077/markdown_pic1/main/Picgo/image-20230131105023109.png"/></div></div><button type="button" class="tab-to-top" aria-label="scroll to top"><i class="fas fa-arrow-up"></i></button></div><div class="tab-item-content" id="前后端联调测试-4"><div class="img-wrap"><div class="img-bg"><img class="img" src="https://raw.githubusercontent.com/silvan2077/markdown_pic1/main/Picgo/image-20230131105123610.png"/></div></div><button type="button" class="tab-to-top" aria-label="scroll to top"><i class="fas fa-arrow-up"></i></button></div></div></div><h2 id="Apache-POI"><a href="#Apache-POI" class="headerlink" title="Apache POI"></a>Apache POI</h2><h3 id="介绍-7"><a href="#介绍-7" class="headerlink" title="介绍"></a>介绍</h3><p><code>Apache POI</code> 是一个处理<code>Miscrosoft Office</code>各种文件格式的开源项目。简单来说就是，可以使用 <code>POI</code> 在 Java 程序中对Miscrosoft Office各种文件进行读写操作。<br>一般情况下，POI 都是用于操作 Excel 文件。</p><div class="img-wrap"><div class="img-bg"><img class="img" src="https://raw.githubusercontent.com/silvan2077/markdown_pic1/main/Picgo/image-20230131110631081.png"/></div></div><p><strong>Apache POI 的应用场景：</strong></p><div class="tabs" id="apachepoi"><ul class="nav-tabs"><li class="tab active"><button type="button" data-href="#apachepoi-1">银行网银系统导出交易明细</button></li><li class="tab"><button type="button" data-href="#apachepoi-2">各种业务系统导出Excel报表</button></li><li class="tab"><button type="button" data-href="#apachepoi-3">批量导入业务数据</button></li></ul><div class="tab-contents"><div class="tab-item-content active" id="apachepoi-1"><div class="img-wrap"><div class="img-bg"><img class="img" src="https://raw.githubusercontent.com/silvan2077/markdown_pic1/main/Picgo/image-20230131110810568.png"/></div></div><button type="button" class="tab-to-top" aria-label="scroll to top"><i class="fas fa-arrow-up"></i></button></div><div class="tab-item-content" id="apachepoi-2"><div class="img-wrap"><div class="img-bg"><img class="img" src="https://raw.githubusercontent.com/silvan2077/markdown_pic1/main/Picgo/image-20230131110839959.png"/></div></div><button type="button" class="tab-to-top" aria-label="scroll to top"><i class="fas fa-arrow-up"></i></button></div><div class="tab-item-content" id="apachepoi-3"><div class="img-wrap"><div class="img-bg"><img class="img" src="https://raw.githubusercontent.com/silvan2077/markdown_pic1/main/Picgo/image-20230131110856903.png"/></div></div><button type="button" class="tab-to-top" aria-label="scroll to top"><i class="fas fa-arrow-up"></i></button></div></div></div><h3 id="入门案例-5"><a href="#入门案例-5" class="headerlink" title="入门案例"></a>入门案例</h3><p>Apache POI既可以将数据写入Excel文件，也可以读取Excel文件中的数据，接下来分别进行实现。</p><p><strong>Apache POI的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><span class="line">6</span><br><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="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.poi<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>poi<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.16<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.apache.poi<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>poi-ooxml<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.16<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><h4 id="将数据写入Excel文件"><a href="#将数据写入Excel文件" class="headerlink" title="将数据写入Excel文件"></a>将数据写入Excel文件</h4><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><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="keyword">public</span> <span class="keyword">class</span> <span class="title class_">POITest</span> &#123;</span><br><span class="line"></span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     * 基于POI向Excel文件写入数据</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@throws</span> Exception</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_">write</span><span class="params">()</span> <span class="keyword">throws</span> Exception&#123;</span><br><span class="line">        <span class="comment">//在内存中创建一个Excel文件对象</span></span><br><span class="line">        <span class="type">XSSFWorkbook</span> <span class="variable">excel</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">XSSFWorkbook</span>();</span><br><span class="line">        <span class="comment">//创建Sheet页</span></span><br><span class="line">        <span class="type">XSSFSheet</span> <span class="variable">sheet</span> <span class="operator">=</span> excel.createSheet(<span class="string">&quot;itcast&quot;</span>);</span><br><span class="line"></span><br><span class="line">        <span class="comment">//在Sheet页中创建行，0表示第1行</span></span><br><span class="line">        <span class="type">XSSFRow</span> <span class="variable">row1</span> <span class="operator">=</span> sheet.createRow(<span class="number">0</span>);</span><br><span class="line">        <span class="comment">//创建单元格并在单元格中设置值，单元格编号也是从0开始，1表示第2个单元格</span></span><br><span class="line">        row1.createCell(<span class="number">1</span>).setCellValue(<span class="string">&quot;姓名&quot;</span>);</span><br><span class="line">        row1.createCell(<span class="number">2</span>).setCellValue(<span class="string">&quot;城市&quot;</span>);</span><br><span class="line"></span><br><span class="line">        <span class="type">XSSFRow</span> <span class="variable">row2</span> <span class="operator">=</span> sheet.createRow(<span class="number">1</span>);</span><br><span class="line">        row2.createCell(<span class="number">1</span>).setCellValue(<span class="string">&quot;张三&quot;</span>);</span><br><span class="line">        row2.createCell(<span class="number">2</span>).setCellValue(<span class="string">&quot;北京&quot;</span>);</span><br><span class="line"></span><br><span class="line">        <span class="type">XSSFRow</span> <span class="variable">row3</span> <span class="operator">=</span> sheet.createRow(<span class="number">2</span>);</span><br><span class="line">        row3.createCell(<span class="number">1</span>).setCellValue(<span class="string">&quot;李四&quot;</span>);</span><br><span class="line">        row3.createCell(<span class="number">2</span>).setCellValue(<span class="string">&quot;上海&quot;</span>);</span><br><span class="line"></span><br><span class="line">        <span class="type">FileOutputStream</span> <span class="variable">out</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">FileOutputStream</span>(<span class="keyword">new</span> <span class="title class_">File</span>(<span class="string">&quot;D:\\itcast.xlsx&quot;</span>));</span><br><span class="line">        <span class="comment">//通过输出流将内存中的Excel文件写入到磁盘上</span></span><br><span class="line">        excel.write(out);</span><br><span class="line"></span><br><span class="line">        <span class="comment">//关闭资源</span></span><br><span class="line">        out.flush();</span><br><span class="line">        out.close();</span><br><span class="line">        excel.close();</span><br><span class="line">    &#125;</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">        write();</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>在D盘中生成itcast.xlsx文件，创建名称为itcast的Sheet页，同时将内容成功写入。</p><div class="img-wrap"><div class="img-bg"><img class="img" src="https://raw.githubusercontent.com/silvan2077/markdown_pic1/main/Picgo/image-20230131112905034.png"/></div></div><h4 id="读取Excel文件中的数据"><a href="#读取Excel文件中的数据" class="headerlink" title="读取Excel文件中的数据"></a>读取Excel文件中的数据</h4><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><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_">POITest</span> &#123;</span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     * 基于POI读取Excel文件</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@throws</span> Exception</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_">read</span><span class="params">()</span> <span class="keyword">throws</span> Exception&#123;</span><br><span class="line">        <span class="type">FileInputStream</span> <span class="variable">in</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">FileInputStream</span>(<span class="keyword">new</span> <span class="title class_">File</span>(<span class="string">&quot;D:\\itcast.xlsx&quot;</span>));</span><br><span class="line">        <span class="comment">//通过输入流读取指定的Excel文件</span></span><br><span class="line">        <span class="type">XSSFWorkbook</span> <span class="variable">excel</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">XSSFWorkbook</span>(in);</span><br><span class="line">        <span class="comment">//获取Excel文件的第1个Sheet页</span></span><br><span class="line">        <span class="type">XSSFSheet</span> <span class="variable">sheet</span> <span class="operator">=</span> excel.getSheetAt(<span class="number">0</span>);</span><br><span class="line"></span><br><span class="line">        <span class="comment">//获取Sheet页中的最后一行的行号</span></span><br><span class="line">        <span class="type">int</span> <span class="variable">lastRowNum</span> <span class="operator">=</span> sheet.getLastRowNum();</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;= lastRowNum; i++) &#123;</span><br><span class="line">            <span class="comment">//获取Sheet页中的行</span></span><br><span class="line">            <span class="type">XSSFRow</span> <span class="variable">titleRow</span> <span class="operator">=</span> sheet.getRow(i);</span><br><span class="line">            <span class="comment">//获取行的第2个单元格</span></span><br><span class="line">            <span class="type">XSSFCell</span> <span class="variable">cell1</span> <span class="operator">=</span> titleRow.getCell(<span class="number">1</span>);</span><br><span class="line">            <span class="comment">//获取单元格中的文本内容</span></span><br><span class="line">            <span class="type">String</span> <span class="variable">cellValue1</span> <span class="operator">=</span> cell1.getStringCellValue();</span><br><span class="line">            <span class="comment">//获取行的第3个单元格</span></span><br><span class="line">            <span class="type">XSSFCell</span> <span class="variable">cell2</span> <span class="operator">=</span> titleRow.getCell(<span class="number">2</span>);</span><br><span class="line">            <span class="comment">//获取单元格中的文本内容</span></span><br><span class="line">            <span class="type">String</span> <span class="variable">cellValue2</span> <span class="operator">=</span> cell2.getStringCellValue();</span><br><span class="line"></span><br><span class="line">            System.out.println(cellValue1 + <span class="string">&quot; &quot;</span> +cellValue2);</span><br><span class="line">        &#125;</span><br><span class="line"></span><br><span class="line">        <span class="comment">//关闭资源</span></span><br><span class="line">        in.close();</span><br><span class="line">        excel.close();</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> Exception &#123;</span><br><span class="line">        read();</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br></pre></td></tr></table></figure><p><strong>2. 实现效果</strong></p><p>将itcast.xlsx文件中的数据进行读取<br><div class="img-wrap"><div class="img-bg"><img class="img" src="https://raw.githubusercontent.com/silvan2077/markdown_pic1/main/Picgo/image-20230131113255962.png"/></div></div></p><h2 id="导出运营数据Excel报表"><a href="#导出运营数据Excel报表" class="headerlink" title="导出运营数据Excel报表"></a>导出运营数据Excel报表</h2><h3 id="需求分析和设计-41"><a href="#需求分析和设计-41" class="headerlink" title="需求分析和设计"></a>需求分析和设计</h3><h4 id="产品原型-10"><a href="#产品原型-10" class="headerlink" title="产品原型"></a>产品原型</h4><p>在数据统计页面，有一个数据导出的按钮，点击该按钮时，其实就会下载一个文件。这个文件实际上是一个Excel形式的文件，文件中主要包含最近30日运营相关的数据。表格的形式已经固定，主要由概览数据和明细数据两部分组成。真正导出这个报表之后，相对应的数字就会填充在表格中，就可以进行存档。</p><p><strong>原型图：</strong></p><div class="img-wrap"><div class="img-bg"><img class="img" src="https://raw.githubusercontent.com/silvan2077/markdown_pic1/main/Picgo/image-20230131151132672.png"/></div></div><p>导出的Excel报表格式：<br><div class="img-wrap"><div class="img-bg"><img class="img" src="https://raw.githubusercontent.com/silvan2077/markdown_pic1/main/Picgo/image-20230130201026785.png"/></div></div></p><div class="note info no-icon flat"><p><strong>业务规则：</strong></p><ul><li>导出Excel形式的报表文件</li><li>导出最近30天的运营数据</li></ul></div><h4 id="接口设计-13"><a href="#接口设计-13" class="headerlink" title="接口设计"></a>接口设计</h4><p>通过上述原型图设计对应的接口。</p><div class="img-wrap"><div class="img-bg"><img class="img" src="https://raw.githubusercontent.com/silvan2077/markdown_pic1/main/Picgo/image-20230130201109280.png"/></div></div><div class="note warning no-icon flat"><p><strong>注意：</strong></p><ul><li>当前接口没有传递参数，因为导出的是最近30天的运营数据，后端计算即可，所以不需要任何参数</li><li>当前接口没有返回数据，因为报表导出功能本质上是文件下载，服务端会通过输出流将Excel文件下载到客户端浏览器</li></ul></div><h3 id="代码开发-26"><a href="#代码开发-26" class="headerlink" title="代码开发"></a>代码开发</h3><h4 id="实现步骤"><a href="#实现步骤" class="headerlink" title="实现步骤"></a>实现步骤</h4><ol><li><p>设计Excel模板文件</p></li><li><p>查询近30天的运营数据</p></li><li><p>将查询到的运营数据写入模板文件</p></li><li><p>通过输出流将Excel文件下载到客户端浏览器</p></li></ol><div class="img-wrap"><div class="img-bg"><img class="img" src="https://raw.githubusercontent.com/silvan2077/markdown_pic1/main/Picgo/image-20230131152610559.png"/></div></div><h4 id="Controller层-22"><a href="#Controller层-22" class="headerlink" title="Controller层"></a>Controller层</h4><p><strong>根据接口定义，在ReportController中创建export方法：</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">    * 导出运营数据报表</span></span><br><span class="line"><span class="comment">    * <span class="doctag">@param</span> response</span></span><br><span class="line"><span class="comment">    */</span></span><br><span class="line">   <span class="meta">@GetMapping(&quot;/export&quot;)</span></span><br><span class="line">   <span class="meta">@ApiOperation(&quot;导出运营数据报表&quot;)</span></span><br><span class="line">   <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">export</span><span class="params">(HttpServletResponse response)</span>&#123;</span><br><span class="line">       reportService.exportBusinessData(response);</span><br><span class="line">   &#125;</span><br></pre></td></tr></table></figure><h4 id="Service层-18"><a href="#Service层-18" class="headerlink" title="Service层"></a>Service层</h4><p><strong>在ReportService接口中声明导出运营数据报表的方法：</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></pre></td><td class="code"><pre><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment">    * 导出近30天的运营数据报表</span></span><br><span class="line"><span class="comment">    * <span class="doctag">@param</span> response</span></span><br><span class="line"><span class="comment">    **/</span></span><br><span class="line">   <span class="keyword">void</span> <span class="title function_">exportBusinessData</span><span class="params">(HttpServletResponse response)</span>;</span><br></pre></td></tr></table></figure><p><strong>在ReportServiceImpl实现类中实现导出运营数据报表的方法:</strong></p><p>提前将资料中的<strong>运营数据报表模板.xlsx</strong>拷贝到项目的resources/template目录中</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">/**导出近30天的运营数据报表</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@param</span> response</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_">exportBusinessData</span><span class="params">(HttpServletResponse response)</span> &#123;</span><br><span class="line">    <span class="type">LocalDate</span> <span class="variable">begin</span> <span class="operator">=</span> LocalDate.now().minusDays(<span class="number">30</span>);</span><br><span class="line">    <span class="type">LocalDate</span> <span class="variable">end</span> <span class="operator">=</span> LocalDate.now().minusDays(<span class="number">1</span>);</span><br><span class="line">    <span class="comment">//查询概览运营数据，提供给Excel模板文件</span></span><br><span class="line">    <span class="type">BusinessDataVO</span> <span class="variable">businessData</span> <span class="operator">=</span> workspaceService.getBusinessData(LocalDateTime.of(begin,LocalTime.MIN), LocalDateTime.of(end, LocalTime.MAX));</span><br><span class="line">    <span class="type">InputStream</span> <span class="variable">inputStream</span> <span class="operator">=</span> <span class="built_in">this</span>.getClass().getClassLoader().getResourceAsStream(<span class="string">&quot;template/运营数据报表模板.xlsx&quot;</span>);</span><br><span class="line">    <span class="keyword">try</span> &#123;</span><br><span class="line">        <span class="comment">//基于提供好的模板文件创建一个新的Excel表格对象</span></span><br><span class="line">        <span class="type">XSSFWorkbook</span> <span class="variable">excel</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">XSSFWorkbook</span>(inputStream);</span><br><span class="line">        <span class="comment">//获得Excel文件中的一个Sheet页</span></span><br><span class="line">        <span class="type">XSSFSheet</span> <span class="variable">sheet</span> <span class="operator">=</span> excel.getSheet(<span class="string">&quot;Sheet1&quot;</span>);</span><br><span class="line"></span><br><span class="line">        sheet.getRow(<span class="number">1</span>).getCell(<span class="number">1</span>).setCellValue(begin + <span class="string">&quot;至&quot;</span> + end);</span><br><span class="line">        <span class="comment">//获得第4行</span></span><br><span class="line">        <span class="type">XSSFRow</span> <span class="variable">row</span> <span class="operator">=</span> sheet.getRow(<span class="number">3</span>);</span><br><span class="line">        <span class="comment">//获取单元格</span></span><br><span class="line">        row.getCell(<span class="number">2</span>).setCellValue(businessData.getTurnover());</span><br><span class="line">        row.getCell(<span class="number">4</span>).setCellValue(businessData.getOrderCompletionRate());</span><br><span class="line">        row.getCell(<span class="number">6</span>).setCellValue(businessData.getNewUsers());</span><br><span class="line">        row = sheet.getRow(<span class="number">4</span>);</span><br><span class="line">        row.getCell(<span class="number">2</span>).setCellValue(businessData.getValidOrderCount());</span><br><span class="line">        row.getCell(<span class="number">4</span>).setCellValue(businessData.getUnitPrice());</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">30</span>; i++) &#123;</span><br><span class="line">            <span class="type">LocalDate</span> <span class="variable">date</span> <span class="operator">=</span> begin.plusDays(i);</span><br><span class="line">           <span class="comment">//准备明细数据</span></span><br><span class="line">            businessData = workspaceService.getBusinessData(LocalDateTime.of(date,LocalTime.MIN), LocalDateTime.of(date, LocalTime.MAX));</span><br><span class="line">            row = sheet.getRow(<span class="number">7</span> + i);</span><br><span class="line">            row.getCell(<span class="number">1</span>).setCellValue(date.toString());</span><br><span class="line">            row.getCell(<span class="number">2</span>).setCellValue(businessData.getTurnover());</span><br><span class="line">            row.getCell(<span class="number">3</span>).setCellValue(businessData.getValidOrderCount());</span><br><span class="line">            row.getCell(<span class="number">4</span>).setCellValue(businessData.getOrderCompletionRate());</span><br><span class="line">            row.getCell(<span class="number">5</span>).setCellValue(businessData.getUnitPrice());</span><br><span class="line">            row.getCell(<span class="number">6</span>).setCellValue(businessData.getNewUsers());</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="comment">//通过输出流将文件下载到客户端浏览器中</span></span><br><span class="line">        <span class="type">ServletOutputStream</span> <span class="variable">out</span> <span class="operator">=</span> response.getOutputStream();</span><br><span class="line">        excel.write(out);</span><br><span class="line">        <span class="comment">//关闭资源</span></span><br><span class="line">        out.flush();</span><br><span class="line">        out.close();</span><br><span class="line">        excel.close();</span><br><span class="line"></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">&#125;</span><br></pre></td></tr></table></figure><h3 id="功能测试-28"><a href="#功能测试-28" class="headerlink" title="功能测试"></a>功能测试</h3><p>直接使用前后端联调测试。</p><p><strong>进入数据统计</strong></p><div class="img-wrap"><div class="img-bg"><img class="img" src="https://raw.githubusercontent.com/silvan2077/markdown_pic1/main/Picgo/image-20230131155111294.png"/></div></div><p><strong>点击数据导出</strong>：Excel报表下载成功</p><div class="img-wrap"><div class="img-bg"><img class="img" src="https://raw.githubusercontent.com/silvan2077/markdown_pic1/main/Picgo/image-20230131160647328.png"/></div></div>]]></content>
    
    
      
      
    <summary type="html">&lt;h1 id=&quot;day01&quot;&gt;&lt;a href=&quot;#day01&quot; class=&quot;headerlink&quot; title=&quot;day01&quot;&gt;&lt;/a&gt;day01&lt;/h1&gt;&lt;h2 id=&quot;前端环境搭建&quot;&gt;&lt;a href=&quot;#前端环境搭建&quot; class=&quot;headerlink&quot; title=&quot;前</summary>
      
    
    
    
    
  </entry>
  
  <entry>
    <title>瑞吉外卖</title>
    <link href="https://silvan.chat/2025/11/07/reggie/"/>
    <id>https://silvan.chat/2025/11/07/reggie/</id>
    <published>2025-11-07T08:17:44.940Z</published>
    <updated>2025-11-11T10:07:53.444Z</updated>
    
    <content type="html"><![CDATA[<h2 id="准备工作"><a href="#准备工作" class="headerlink" title="准备工作"></a>准备工作</h2><div class="note info no-icon flat"><p>个人在学习该项目时使用的是SpringBoot3，所以有些操作与视频有所不同</p></div><ul><li><p>首先创建所需的数据库表</p><div class="tabs" id="准备工作-建表"><ul class="nav-tabs"><li class="tab active"><button type="button" data-href="#准备工作-建表-1">SQL语句</button></li><li class="tab"><button type="button" data-href="#准备工作-建表-2">创建结果</button></li></ul><div class="tab-contents"><div class="tab-item-content active" id="准备工作-建表-1"><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><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><span class="line">301</span><br><span class="line">302</span><br><span class="line">303</span><br><span class="line">304</span><br><span class="line">305</span><br><span class="line">306</span><br><span class="line">307</span><br><span class="line">308</span><br><span class="line">309</span><br><span class="line">310</span><br><span class="line">311</span><br><span class="line">312</span><br><span class="line">313</span><br><span class="line">314</span><br><span class="line">315</span><br><span class="line">316</span><br><span class="line">317</span><br><span class="line">318</span><br><span class="line">319</span><br><span class="line">320</span><br><span class="line">321</span><br><span class="line">322</span><br><span class="line">323</span><br><span class="line">324</span><br><span class="line">325</span><br><span class="line">326</span><br><span class="line">327</span><br><span class="line">328</span><br><span class="line">329</span><br><span class="line">330</span><br><span class="line">331</span><br><span class="line">332</span><br><span class="line">333</span><br><span class="line">334</span><br><span class="line">335</span><br><span class="line">336</span><br><span class="line">337</span><br><span class="line">338</span><br><span class="line">339</span><br><span class="line">340</span><br><span class="line">341</span><br><span class="line">342</span><br><span class="line">343</span><br><span class="line">344</span><br><span class="line">345</span><br><span class="line">346</span><br><span class="line">347</span><br><span class="line">348</span><br><span class="line">349</span><br><span class="line">350</span><br><span class="line">351</span><br><span class="line">352</span><br><span class="line">353</span><br><span class="line">354</span><br><span class="line">355</span><br><span class="line">356</span><br><span class="line">357</span><br><span class="line">358</span><br><span class="line">359</span><br><span class="line">360</span><br><span class="line">361</span><br><span class="line">362</span><br><span class="line">363</span><br><span class="line">364</span><br><span class="line">365</span><br><span class="line">366</span><br><span class="line">367</span><br><span class="line">368</span><br><span class="line">369</span><br><span class="line">370</span><br><span class="line">371</span><br><span class="line">372</span><br><span class="line">373</span><br><span class="line">374</span><br><span class="line">375</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/*</span></span><br><span class="line"><span class="comment">Navicat MySQL Data Transfer</span></span><br><span class="line"><span class="comment"></span></span><br><span class="line"><span class="comment">Source Server         : localhost</span></span><br><span class="line"><span class="comment">Source Server Version : 50728</span></span><br><span class="line"><span class="comment">Source Host           : localhost:3306</span></span><br><span class="line"><span class="comment">Source Database       : reggie</span></span><br><span class="line"><span class="comment"></span></span><br><span class="line"><span class="comment">Target Server Type    : MYSQL</span></span><br><span class="line"><span class="comment">Target Server Version : 50728</span></span><br><span class="line"><span class="comment">File Encoding         : 65001</span></span><br><span class="line"><span class="comment"></span></span><br><span class="line"><span class="comment">Date: 2021-07-23 10:41:41</span></span><br><span class="line"><span class="comment">*/</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">SET</span> FOREIGN_KEY_CHECKS<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="comment">-- Table structure for address_book</span></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> `address_book`;</span><br><span class="line"><span class="keyword">CREATE TABLE</span> `address_book` (</span><br><span class="line">  `id` <span class="type">bigint</span>(<span class="number">20</span>) <span class="keyword">NOT NULL</span> COMMENT <span class="string">&#x27;主键&#x27;</span>,</span><br><span class="line">  `user_id` <span class="type">bigint</span>(<span class="number">20</span>) <span class="keyword">NOT NULL</span> COMMENT <span class="string">&#x27;用户id&#x27;</span>,</span><br><span class="line">  `consignee` <span class="type">varchar</span>(<span class="number">50</span>) <span class="keyword">COLLATE</span> utf8_bin <span class="keyword">NOT NULL</span> COMMENT <span class="string">&#x27;收货人&#x27;</span>,</span><br><span class="line">  `sex` tinyint(<span class="number">4</span>) <span class="keyword">NOT NULL</span> COMMENT <span class="string">&#x27;性别 0 女 1 男&#x27;</span>,</span><br><span class="line">  `phone` <span class="type">varchar</span>(<span class="number">11</span>) <span class="keyword">COLLATE</span> utf8_bin <span class="keyword">NOT NULL</span> COMMENT <span class="string">&#x27;手机号&#x27;</span>,</span><br><span class="line">  `province_code` <span class="type">varchar</span>(<span class="number">12</span>) <span class="keyword">CHARACTER SET</span> utf8mb4 <span class="keyword">DEFAULT</span> <span class="keyword">NULL</span> COMMENT <span class="string">&#x27;省级区划编号&#x27;</span>,</span><br><span class="line">  `province_name` <span class="type">varchar</span>(<span class="number">32</span>) <span class="keyword">CHARACTER SET</span> utf8mb4 <span class="keyword">DEFAULT</span> <span class="keyword">NULL</span> COMMENT <span class="string">&#x27;省级名称&#x27;</span>,</span><br><span class="line">  `city_code` <span class="type">varchar</span>(<span class="number">12</span>) <span class="keyword">CHARACTER SET</span> utf8mb4 <span class="keyword">DEFAULT</span> <span class="keyword">NULL</span> COMMENT <span class="string">&#x27;市级区划编号&#x27;</span>,</span><br><span class="line">  `city_name` <span class="type">varchar</span>(<span class="number">32</span>) <span class="keyword">CHARACTER SET</span> utf8mb4 <span class="keyword">DEFAULT</span> <span class="keyword">NULL</span> COMMENT <span class="string">&#x27;市级名称&#x27;</span>,</span><br><span class="line">  `district_code` <span class="type">varchar</span>(<span class="number">12</span>) <span class="keyword">CHARACTER SET</span> utf8mb4 <span class="keyword">DEFAULT</span> <span class="keyword">NULL</span> COMMENT <span class="string">&#x27;区级区划编号&#x27;</span>,</span><br><span class="line">  `district_name` <span class="type">varchar</span>(<span class="number">32</span>) <span class="keyword">CHARACTER SET</span> utf8mb4 <span class="keyword">DEFAULT</span> <span class="keyword">NULL</span> COMMENT <span class="string">&#x27;区级名称&#x27;</span>,</span><br><span class="line">  `detail` <span class="type">varchar</span>(<span class="number">200</span>) <span class="keyword">CHARACTER SET</span> utf8mb4 <span class="keyword">DEFAULT</span> <span class="keyword">NULL</span> COMMENT <span class="string">&#x27;详细地址&#x27;</span>,</span><br><span class="line">  `label` <span class="type">varchar</span>(<span class="number">100</span>) <span class="keyword">CHARACTER SET</span> utf8mb4 <span class="keyword">DEFAULT</span> <span class="keyword">NULL</span> COMMENT <span class="string">&#x27;标签&#x27;</span>,</span><br><span class="line">  `is_default` tinyint(<span class="number">1</span>) <span class="keyword">NOT NULL</span> <span class="keyword">DEFAULT</span> <span class="string">&#x27;0&#x27;</span> COMMENT <span class="string">&#x27;默认 0 否 1是&#x27;</span>,</span><br><span class="line">  `create_time` datetime <span class="keyword">NOT NULL</span> COMMENT <span class="string">&#x27;创建时间&#x27;</span>,</span><br><span class="line">  `update_time` datetime <span class="keyword">NOT NULL</span> COMMENT <span class="string">&#x27;更新时间&#x27;</span>,</span><br><span class="line">  `create_user` <span class="type">bigint</span>(<span class="number">20</span>) <span class="keyword">NOT NULL</span> COMMENT <span class="string">&#x27;创建人&#x27;</span>,</span><br><span class="line">  `update_user` <span class="type">bigint</span>(<span class="number">20</span>) <span class="keyword">NOT NULL</span> COMMENT <span class="string">&#x27;修改人&#x27;</span>,</span><br><span class="line">  `is_deleted` <span class="type">int</span>(<span class="number">11</span>) <span class="keyword">NOT NULL</span> <span class="keyword">DEFAULT</span> <span class="string">&#x27;0&#x27;</span> COMMENT <span class="string">&#x27;是否删除&#x27;</span>,</span><br><span class="line">  <span class="keyword">PRIMARY KEY</span> (`id`) <span class="keyword">USING</span> BTREE</span><br><span class="line">) ENGINE<span class="operator">=</span>InnoDB <span class="keyword">DEFAULT</span> CHARSET<span class="operator">=</span>utf8 <span class="keyword">COLLATE</span><span class="operator">=</span>utf8_bin 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="comment">-- Records of address_book</span></span><br><span class="line"><span class="comment">-- ----------------------------</span></span><br><span class="line"><span class="keyword">INSERT INTO</span> `address_book` <span class="keyword">VALUES</span> (<span class="string">&#x27;1417414526093082626&#x27;</span>, <span class="string">&#x27;1417012167126876162&#x27;</span>, <span class="string">&#x27;小明&#x27;</span>, <span class="string">&#x27;1&#x27;</span>, <span class="string">&#x27;13812345678&#x27;</span>, <span class="keyword">null</span>, <span class="keyword">null</span>, <span class="keyword">null</span>, <span class="keyword">null</span>, <span class="keyword">null</span>, <span class="keyword">null</span>, <span class="string">&#x27;昌平区金燕龙办公楼&#x27;</span>, <span class="string">&#x27;公司&#x27;</span>, <span class="string">&#x27;1&#x27;</span>, <span class="string">&#x27;2021-07-20 17:22:12&#x27;</span>, <span class="string">&#x27;2021-07-20 17:26:33&#x27;</span>, <span class="string">&#x27;1417012167126876162&#x27;</span>, <span class="string">&#x27;1417012167126876162&#x27;</span>, <span class="string">&#x27;0&#x27;</span>);</span><br><span class="line"><span class="keyword">INSERT INTO</span> `address_book` <span class="keyword">VALUES</span> (<span class="string">&#x27;1417414926166769666&#x27;</span>, <span class="string">&#x27;1417012167126876162&#x27;</span>, <span class="string">&#x27;小李&#x27;</span>, <span class="string">&#x27;1&#x27;</span>, <span class="string">&#x27;13512345678&#x27;</span>, <span class="keyword">null</span>, <span class="keyword">null</span>, <span class="keyword">null</span>, <span class="keyword">null</span>, <span class="keyword">null</span>, <span class="keyword">null</span>, <span class="string">&#x27;测试&#x27;</span>, <span class="string">&#x27;家&#x27;</span>, <span class="string">&#x27;0&#x27;</span>, <span class="string">&#x27;2021-07-20 17:23:47&#x27;</span>, <span class="string">&#x27;2021-07-20 17:23:47&#x27;</span>, <span class="string">&#x27;1417012167126876162&#x27;</span>, <span class="string">&#x27;1417012167126876162&#x27;</span>, <span class="string">&#x27;0&#x27;</span>);</span><br><span class="line"></span><br><span class="line"><span class="comment">-- ----------------------------</span></span><br><span class="line"><span class="comment">-- Table structure for category</span></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> `category`;</span><br><span class="line"><span class="keyword">CREATE TABLE</span> `category` (</span><br><span class="line">  `id` <span class="type">bigint</span>(<span class="number">20</span>) <span class="keyword">NOT NULL</span> COMMENT <span class="string">&#x27;主键&#x27;</span>,</span><br><span class="line">  `type` <span class="type">int</span>(<span class="number">11</span>) <span class="keyword">DEFAULT</span> <span class="keyword">NULL</span> COMMENT <span class="string">&#x27;类型   1 菜品分类 2 套餐分类&#x27;</span>,</span><br><span class="line">  `name` <span class="type">varchar</span>(<span class="number">64</span>) <span class="keyword">COLLATE</span> utf8_bin <span class="keyword">NOT NULL</span> COMMENT <span class="string">&#x27;分类名称&#x27;</span>,</span><br><span class="line">  `sort` <span class="type">int</span>(<span class="number">11</span>) <span class="keyword">NOT NULL</span> <span class="keyword">DEFAULT</span> <span class="string">&#x27;0&#x27;</span> COMMENT <span class="string">&#x27;顺序&#x27;</span>,</span><br><span class="line">  `create_time` datetime <span class="keyword">NOT NULL</span> COMMENT <span class="string">&#x27;创建时间&#x27;</span>,</span><br><span class="line">  `update_time` datetime <span class="keyword">NOT NULL</span> COMMENT <span class="string">&#x27;更新时间&#x27;</span>,</span><br><span class="line">  `create_user` <span class="type">bigint</span>(<span class="number">20</span>) <span class="keyword">NOT NULL</span> COMMENT <span class="string">&#x27;创建人&#x27;</span>,</span><br><span class="line">  `update_user` <span class="type">bigint</span>(<span class="number">20</span>) <span class="keyword">NOT NULL</span> COMMENT <span class="string">&#x27;修改人&#x27;</span>,</span><br><span class="line">  <span class="keyword">PRIMARY KEY</span> (`id`) <span class="keyword">USING</span> BTREE,</span><br><span class="line">  <span class="keyword">UNIQUE</span> KEY `idx_category_name` (`name`)</span><br><span class="line">) ENGINE<span class="operator">=</span>InnoDB <span class="keyword">DEFAULT</span> CHARSET<span class="operator">=</span>utf8 <span class="keyword">COLLATE</span><span class="operator">=</span>utf8_bin 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="comment">-- Records of category</span></span><br><span class="line"><span class="comment">-- ----------------------------</span></span><br><span class="line"><span class="keyword">INSERT INTO</span> `category` <span class="keyword">VALUES</span> (<span class="string">&#x27;1397844263642378242&#x27;</span>, <span class="string">&#x27;1&#x27;</span>, <span class="string">&#x27;湘菜&#x27;</span>, <span class="string">&#x27;1&#x27;</span>, <span class="string">&#x27;2021-05-27 09:16:58&#x27;</span>, <span class="string">&#x27;2021-07-15 20:25:23&#x27;</span>, <span class="string">&#x27;1&#x27;</span>, <span class="string">&#x27;1&#x27;</span>);</span><br><span class="line"><span class="keyword">INSERT INTO</span> `category` <span class="keyword">VALUES</span> (<span class="string">&#x27;1397844303408574465&#x27;</span>, <span class="string">&#x27;1&#x27;</span>, <span class="string">&#x27;川菜&#x27;</span>, <span class="string">&#x27;2&#x27;</span>, <span class="string">&#x27;2021-05-27 09:17:07&#x27;</span>, <span class="string">&#x27;2021-06-02 14:27:22&#x27;</span>, <span class="string">&#x27;1&#x27;</span>, <span class="string">&#x27;1&#x27;</span>);</span><br><span class="line"><span class="keyword">INSERT INTO</span> `category` <span class="keyword">VALUES</span> (<span class="string">&#x27;1397844391040167938&#x27;</span>, <span class="string">&#x27;1&#x27;</span>, <span class="string">&#x27;粤菜&#x27;</span>, <span class="string">&#x27;3&#x27;</span>, <span class="string">&#x27;2021-05-27 09:17:28&#x27;</span>, <span class="string">&#x27;2021-07-09 14:37:13&#x27;</span>, <span class="string">&#x27;1&#x27;</span>, <span class="string">&#x27;1&#x27;</span>);</span><br><span class="line"><span class="keyword">INSERT INTO</span> `category` <span class="keyword">VALUES</span> (<span class="string">&#x27;1413341197421846529&#x27;</span>, <span class="string">&#x27;1&#x27;</span>, <span class="string">&#x27;饮品&#x27;</span>, <span class="string">&#x27;11&#x27;</span>, <span class="string">&#x27;2021-07-09 11:36:15&#x27;</span>, <span class="string">&#x27;2021-07-09 14:39:15&#x27;</span>, <span class="string">&#x27;1&#x27;</span>, <span class="string">&#x27;1&#x27;</span>);</span><br><span class="line"><span class="keyword">INSERT INTO</span> `category` <span class="keyword">VALUES</span> (<span class="string">&#x27;1413342269393674242&#x27;</span>, <span class="string">&#x27;2&#x27;</span>, <span class="string">&#x27;商务套餐&#x27;</span>, <span class="string">&#x27;5&#x27;</span>, <span class="string">&#x27;2021-07-09 11:40:30&#x27;</span>, <span class="string">&#x27;2021-07-09 14:43:45&#x27;</span>, <span class="string">&#x27;1&#x27;</span>, <span class="string">&#x27;1&#x27;</span>);</span><br><span class="line"><span class="keyword">INSERT INTO</span> `category` <span class="keyword">VALUES</span> (<span class="string">&#x27;1413384954989060097&#x27;</span>, <span class="string">&#x27;1&#x27;</span>, <span class="string">&#x27;主食&#x27;</span>, <span class="string">&#x27;12&#x27;</span>, <span class="string">&#x27;2021-07-09 14:30:07&#x27;</span>, <span class="string">&#x27;2021-07-09 14:39:19&#x27;</span>, <span class="string">&#x27;1&#x27;</span>, <span class="string">&#x27;1&#x27;</span>);</span><br><span class="line"><span class="keyword">INSERT INTO</span> `category` <span class="keyword">VALUES</span> (<span class="string">&#x27;1413386191767674881&#x27;</span>, <span class="string">&#x27;2&#x27;</span>, <span class="string">&#x27;儿童套餐&#x27;</span>, <span class="string">&#x27;6&#x27;</span>, <span class="string">&#x27;2021-07-09 14:35:02&#x27;</span>, <span class="string">&#x27;2021-07-09 14:39:05&#x27;</span>, <span class="string">&#x27;1&#x27;</span>, <span class="string">&#x27;1&#x27;</span>);</span><br><span class="line"></span><br><span class="line"><span class="comment">-- ----------------------------</span></span><br><span class="line"><span class="comment">-- Table structure for dish</span></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> `dish`;</span><br><span class="line"><span class="keyword">CREATE TABLE</span> `dish` (</span><br><span class="line">  `id` <span class="type">bigint</span>(<span class="number">20</span>) <span class="keyword">NOT NULL</span> COMMENT <span class="string">&#x27;主键&#x27;</span>,</span><br><span class="line">  `name` <span class="type">varchar</span>(<span class="number">64</span>) <span class="keyword">COLLATE</span> utf8_bin <span class="keyword">NOT NULL</span> COMMENT <span class="string">&#x27;菜品名称&#x27;</span>,</span><br><span class="line">  `category_id` <span class="type">bigint</span>(<span class="number">20</span>) <span class="keyword">NOT NULL</span> COMMENT <span class="string">&#x27;菜品分类id&#x27;</span>,</span><br><span class="line">  `price` <span class="type">decimal</span>(<span class="number">10</span>,<span class="number">2</span>) <span class="keyword">DEFAULT</span> <span class="keyword">NULL</span> COMMENT <span class="string">&#x27;菜品价格&#x27;</span>,</span><br><span class="line">  `code` <span class="type">varchar</span>(<span class="number">64</span>) <span class="keyword">COLLATE</span> utf8_bin <span class="keyword">NOT NULL</span> COMMENT <span class="string">&#x27;商品码&#x27;</span>,</span><br><span class="line">  `image` <span class="type">varchar</span>(<span class="number">200</span>) <span class="keyword">COLLATE</span> utf8_bin <span class="keyword">NOT NULL</span> COMMENT <span class="string">&#x27;图片&#x27;</span>,</span><br><span class="line">  `description` <span class="type">varchar</span>(<span class="number">400</span>) <span class="keyword">COLLATE</span> utf8_bin <span class="keyword">DEFAULT</span> <span class="keyword">NULL</span> COMMENT <span class="string">&#x27;描述信息&#x27;</span>,</span><br><span class="line">  `status` <span class="type">int</span>(<span class="number">11</span>) <span class="keyword">NOT NULL</span> <span class="keyword">DEFAULT</span> <span class="string">&#x27;1&#x27;</span> COMMENT <span class="string">&#x27;0 停售 1 起售&#x27;</span>,</span><br><span class="line">  `sort` <span class="type">int</span>(<span class="number">11</span>) <span class="keyword">NOT NULL</span> <span class="keyword">DEFAULT</span> <span class="string">&#x27;0&#x27;</span> COMMENT <span class="string">&#x27;顺序&#x27;</span>,</span><br><span class="line">  `create_time` datetime <span class="keyword">NOT NULL</span> COMMENT <span class="string">&#x27;创建时间&#x27;</span>,</span><br><span class="line">  `update_time` datetime <span class="keyword">NOT NULL</span> COMMENT <span class="string">&#x27;更新时间&#x27;</span>,</span><br><span class="line">  `create_user` <span class="type">bigint</span>(<span class="number">20</span>) <span class="keyword">NOT NULL</span> COMMENT <span class="string">&#x27;创建人&#x27;</span>,</span><br><span class="line">  `update_user` <span class="type">bigint</span>(<span class="number">20</span>) <span class="keyword">NOT NULL</span> COMMENT <span class="string">&#x27;修改人&#x27;</span>,</span><br><span class="line">  `is_deleted` <span class="type">int</span>(<span class="number">11</span>) <span class="keyword">NOT NULL</span> <span class="keyword">DEFAULT</span> <span class="string">&#x27;0&#x27;</span> COMMENT <span class="string">&#x27;是否删除&#x27;</span>,</span><br><span class="line">  <span class="keyword">PRIMARY KEY</span> (`id`) <span class="keyword">USING</span> BTREE,</span><br><span class="line">  <span class="keyword">UNIQUE</span> KEY `idx_dish_name` (`name`)</span><br><span class="line">) ENGINE<span class="operator">=</span>InnoDB <span class="keyword">DEFAULT</span> CHARSET<span class="operator">=</span>utf8 <span class="keyword">COLLATE</span><span class="operator">=</span>utf8_bin 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="comment">-- Records of dish</span></span><br><span class="line"><span class="comment">-- ----------------------------</span></span><br><span class="line"><span class="keyword">INSERT INTO</span> `dish` <span class="keyword">VALUES</span> (<span class="string">&#x27;1397849739276890114&#x27;</span>, <span class="string">&#x27;辣子鸡&#x27;</span>, <span class="string">&#x27;1397844263642378242&#x27;</span>, <span class="string">&#x27;7800.00&#x27;</span>, <span class="string">&#x27;222222222&#x27;</span>, <span class="string">&#x27;f966a38e-0780-40be-bb52-5699d13cb3d9.jpg&#x27;</span>, <span class="string">&#x27;来自鲜嫩美味的小鸡，值得一尝&#x27;</span>, <span class="string">&#x27;1&#x27;</span>, <span class="string">&#x27;0&#x27;</span>, <span class="string">&#x27;2021-05-27 09:38:43&#x27;</span>, <span class="string">&#x27;2021-05-27 09:38:43&#x27;</span>, <span class="string">&#x27;1&#x27;</span>, <span class="string">&#x27;1&#x27;</span>, <span class="string">&#x27;0&#x27;</span>);</span><br><span class="line"><span class="keyword">INSERT INTO</span> `dish` <span class="keyword">VALUES</span> (<span class="string">&#x27;1397850140982161409&#x27;</span>, <span class="string">&#x27;毛氏红烧肉&#x27;</span>, <span class="string">&#x27;1397844263642378242&#x27;</span>, <span class="string">&#x27;6800.00&#x27;</span>, <span class="string">&#x27;123412341234&#x27;</span>, <span class="string">&#x27;0a3b3288-3446-4420-bbff-f263d0c02d8e.jpg&#x27;</span>, <span class="string">&#x27;毛氏红烧肉毛氏红烧肉，确定不来一份？&#x27;</span>, <span class="string">&#x27;1&#x27;</span>, <span class="string">&#x27;0&#x27;</span>, <span class="string">&#x27;2021-05-27 09:40:19&#x27;</span>, <span class="string">&#x27;2021-05-27 09:40:19&#x27;</span>, <span class="string">&#x27;1&#x27;</span>, <span class="string">&#x27;1&#x27;</span>, <span class="string">&#x27;0&#x27;</span>);</span><br><span class="line"><span class="keyword">INSERT INTO</span> `dish` <span class="keyword">VALUES</span> (<span class="string">&#x27;1397850392090947585&#x27;</span>, <span class="string">&#x27;组庵鱼翅&#x27;</span>, <span class="string">&#x27;1397844263642378242&#x27;</span>, <span class="string">&#x27;4800.00&#x27;</span>, <span class="string">&#x27;123412341234&#x27;</span>, <span class="string">&#x27;740c79ce-af29-41b8-b78d-5f49c96e38c4.jpg&#x27;</span>, <span class="string">&#x27;组庵鱼翅，看图足以表明好吃程度&#x27;</span>, <span class="string">&#x27;1&#x27;</span>, <span class="string">&#x27;0&#x27;</span>, <span class="string">&#x27;2021-05-27 09:41:19&#x27;</span>, <span class="string">&#x27;2021-05-27 09:41:19&#x27;</span>, <span class="string">&#x27;1&#x27;</span>, <span class="string">&#x27;1&#x27;</span>, <span class="string">&#x27;0&#x27;</span>);</span><br><span class="line"><span class="keyword">INSERT INTO</span> `dish` <span class="keyword">VALUES</span> (<span class="string">&#x27;1397850851245600769&#x27;</span>, <span class="string">&#x27;霸王别姬&#x27;</span>, <span class="string">&#x27;1397844263642378242&#x27;</span>, <span class="string">&#x27;12800.00&#x27;</span>, <span class="string">&#x27;123412341234&#x27;</span>, <span class="string">&#x27;057dd338-e487-4bbc-a74c-0384c44a9ca3.jpg&#x27;</span>, <span class="string">&#x27;还有什么比霸王别姬更美味的呢？&#x27;</span>, <span class="string">&#x27;1&#x27;</span>, <span class="string">&#x27;0&#x27;</span>, <span class="string">&#x27;2021-05-27 09:43:08&#x27;</span>, <span class="string">&#x27;2021-05-27 09:43:08&#x27;</span>, <span class="string">&#x27;1&#x27;</span>, <span class="string">&#x27;1&#x27;</span>, <span class="string">&#x27;0&#x27;</span>);</span><br><span class="line"><span class="keyword">INSERT INTO</span> `dish` <span class="keyword">VALUES</span> (<span class="string">&#x27;1397851099502260226&#x27;</span>, <span class="string">&#x27;全家福&#x27;</span>, <span class="string">&#x27;1397844263642378242&#x27;</span>, <span class="string">&#x27;11800.00&#x27;</span>, <span class="string">&#x27;23412341234&#x27;</span>, <span class="string">&#x27;a53a4e6a-3b83-4044-87f9-9d49b30a8fdc.jpg&#x27;</span>, <span class="string">&#x27;别光吃肉啦，来份全家福吧，让你长寿又美味&#x27;</span>, <span class="string">&#x27;1&#x27;</span>, <span class="string">&#x27;0&#x27;</span>, <span class="string">&#x27;2021-05-27 09:44:08&#x27;</span>, <span class="string">&#x27;2021-05-27 09:44:08&#x27;</span>, <span class="string">&#x27;1&#x27;</span>, <span class="string">&#x27;1&#x27;</span>, <span class="string">&#x27;0&#x27;</span>);</span><br><span class="line"><span class="keyword">INSERT INTO</span> `dish` <span class="keyword">VALUES</span> (<span class="string">&#x27;1397851370462687234&#x27;</span>, <span class="string">&#x27;邵阳猪血丸子&#x27;</span>, <span class="string">&#x27;1397844263642378242&#x27;</span>, <span class="string">&#x27;13800.00&#x27;</span>, <span class="string">&#x27;1246812345678&#x27;</span>, <span class="string">&#x27;2a50628e-7758-4c51-9fbb-d37c61cdacad.jpg&#x27;</span>, <span class="string">&#x27;看，美味不？来嘛来嘛，这才是最爱吖&#x27;</span>, <span class="string">&#x27;1&#x27;</span>, <span class="string">&#x27;0&#x27;</span>, <span class="string">&#x27;2021-05-27 09:45:12&#x27;</span>, <span class="string">&#x27;2021-05-27 09:45:12&#x27;</span>, <span class="string">&#x27;1&#x27;</span>, <span class="string">&#x27;1&#x27;</span>, <span class="string">&#x27;0&#x27;</span>);</span><br><span class="line"><span class="keyword">INSERT INTO</span> `dish` <span class="keyword">VALUES</span> (<span class="string">&#x27;1397851668262465537&#x27;</span>, <span class="string">&#x27;口味蛇&#x27;</span>, <span class="string">&#x27;1397844263642378242&#x27;</span>, <span class="string">&#x27;16800.00&#x27;</span>, <span class="string">&#x27;1234567812345678&#x27;</span>, <span class="string">&#x27;0f4bd884-dc9c-4cf9-b59e-7d5958fec3dd.jpg&#x27;</span>, <span class="string">&#x27;爬行界的扛把子，东兴-口味蛇，让你欲罢不能&#x27;</span>, <span class="string">&#x27;1&#x27;</span>, <span class="string">&#x27;0&#x27;</span>, <span class="string">&#x27;2021-05-27 09:46:23&#x27;</span>, <span class="string">&#x27;2021-05-27 09:46:23&#x27;</span>, <span class="string">&#x27;1&#x27;</span>, <span class="string">&#x27;1&#x27;</span>, <span class="string">&#x27;0&#x27;</span>);</span><br><span class="line"><span class="keyword">INSERT INTO</span> `dish` <span class="keyword">VALUES</span> (<span class="string">&#x27;1397852391150759938&#x27;</span>, <span class="string">&#x27;辣子鸡丁&#x27;</span>, <span class="string">&#x27;1397844303408574465&#x27;</span>, <span class="string">&#x27;8800.00&#x27;</span>, <span class="string">&#x27;2346812468&#x27;</span>, <span class="string">&#x27;ef2b73f2-75d1-4d3a-beea-22da0e1421bd.jpg&#x27;</span>, <span class="string">&#x27;辣子鸡丁，辣子鸡丁，永远的魂&#x27;</span>, <span class="string">&#x27;1&#x27;</span>, <span class="string">&#x27;0&#x27;</span>, <span class="string">&#x27;2021-05-27 09:49:16&#x27;</span>, <span class="string">&#x27;2021-05-27 09:49:16&#x27;</span>, <span class="string">&#x27;1&#x27;</span>, <span class="string">&#x27;1&#x27;</span>, <span class="string">&#x27;0&#x27;</span>);</span><br><span class="line"><span class="keyword">INSERT INTO</span> `dish` <span class="keyword">VALUES</span> (<span class="string">&#x27;1397853183287013378&#x27;</span>, <span class="string">&#x27;麻辣兔头&#x27;</span>, <span class="string">&#x27;1397844303408574465&#x27;</span>, <span class="string">&#x27;19800.00&#x27;</span>, <span class="string">&#x27;123456787654321&#x27;</span>, <span class="string">&#x27;2a2e9d66-b41d-4645-87bd-95f2cfeed218.jpg&#x27;</span>, <span class="string">&#x27;麻辣兔头的详细制作，麻辣鲜香，色泽红润，回味悠长&#x27;</span>, <span class="string">&#x27;1&#x27;</span>, <span class="string">&#x27;0&#x27;</span>, <span class="string">&#x27;2021-05-27 09:52:24&#x27;</span>, <span class="string">&#x27;2021-05-27 09:52:24&#x27;</span>, <span class="string">&#x27;1&#x27;</span>, <span class="string">&#x27;1&#x27;</span>, <span class="string">&#x27;0&#x27;</span>);</span><br><span class="line"><span class="keyword">INSERT INTO</span> `dish` <span class="keyword">VALUES</span> (<span class="string">&#x27;1397853709101740034&#x27;</span>, <span class="string">&#x27;蒜泥白肉&#x27;</span>, <span class="string">&#x27;1397844303408574465&#x27;</span>, <span class="string">&#x27;9800.00&#x27;</span>, <span class="string">&#x27;1234321234321&#x27;</span>, <span class="string">&#x27;d2f61d70-ac85-4529-9b74-6d9a2255c6d7.jpg&#x27;</span>, <span class="string">&#x27;多么的有食欲啊&#x27;</span>, <span class="string">&#x27;1&#x27;</span>, <span class="string">&#x27;0&#x27;</span>, <span class="string">&#x27;2021-05-27 09:54:30&#x27;</span>, <span class="string">&#x27;2021-05-27 09:54:30&#x27;</span>, <span class="string">&#x27;1&#x27;</span>, <span class="string">&#x27;1&#x27;</span>, <span class="string">&#x27;0&#x27;</span>);</span><br><span class="line"><span class="keyword">INSERT INTO</span> `dish` <span class="keyword">VALUES</span> (<span class="string">&#x27;1397853890262118402&#x27;</span>, <span class="string">&#x27;鱼香肉丝&#x27;</span>, <span class="string">&#x27;1397844303408574465&#x27;</span>, <span class="string">&#x27;3800.00&#x27;</span>, <span class="string">&#x27;1234212321234&#x27;</span>, <span class="string">&#x27;8dcfda14-5712-4d28-82f7-ae905b3c2308.jpg&#x27;</span>, <span class="string">&#x27;鱼香肉丝简直就是我们童年回忆的一道经典菜，上学的时候点个鱼香肉丝盖饭坐在宿舍床上看着肥皂剧，绝了！现在完美复刻一下上学的时候感觉&#x27;</span>, <span class="string">&#x27;1&#x27;</span>, <span class="string">&#x27;0&#x27;</span>, <span class="string">&#x27;2021-05-27 09:55:13&#x27;</span>, <span class="string">&#x27;2021-05-27 09:55:13&#x27;</span>, <span class="string">&#x27;1&#x27;</span>, <span class="string">&#x27;1&#x27;</span>, <span class="string">&#x27;0&#x27;</span>);</span><br><span class="line"><span class="keyword">INSERT INTO</span> `dish` <span class="keyword">VALUES</span> (<span class="string">&#x27;1397854652581064706&#x27;</span>, <span class="string">&#x27;麻辣水煮鱼&#x27;</span>, <span class="string">&#x27;1397844303408574465&#x27;</span>, <span class="string">&#x27;14800.00&#x27;</span>, <span class="string">&#x27;2345312·345321&#x27;</span>, <span class="string">&#x27;1fdbfbf3-1d86-4b29-a3fc-46345852f2f8.jpg&#x27;</span>, <span class="string">&#x27;鱼片是买的切好的鱼片，放几个虾，增加味道&#x27;</span>, <span class="string">&#x27;1&#x27;</span>, <span class="string">&#x27;0&#x27;</span>, <span class="string">&#x27;2021-05-27 09:58:15&#x27;</span>, <span class="string">&#x27;2021-05-27 09:58:15&#x27;</span>, <span class="string">&#x27;1&#x27;</span>, <span class="string">&#x27;1&#x27;</span>, <span class="string">&#x27;0&#x27;</span>);</span><br><span class="line"><span class="keyword">INSERT INTO</span> `dish` <span class="keyword">VALUES</span> (<span class="string">&#x27;1397854865672679425&#x27;</span>, <span class="string">&#x27;鱼香炒鸡蛋&#x27;</span>, <span class="string">&#x27;1397844303408574465&#x27;</span>, <span class="string">&#x27;2000.00&#x27;</span>, <span class="string">&#x27;23456431·23456&#x27;</span>, <span class="string">&#x27;0f252364-a561-4e8d-8065-9a6797a6b1d3.jpg&#x27;</span>, <span class="string">&#x27;鱼香菜也是川味的特色。里面没有鱼却鱼香味&#x27;</span>, <span class="string">&#x27;1&#x27;</span>, <span class="string">&#x27;0&#x27;</span>, <span class="string">&#x27;2021-05-27 09:59:06&#x27;</span>, <span class="string">&#x27;2021-05-27 09:59:06&#x27;</span>, <span class="string">&#x27;1&#x27;</span>, <span class="string">&#x27;1&#x27;</span>, <span class="string">&#x27;0&#x27;</span>);</span><br><span class="line"><span class="keyword">INSERT INTO</span> `dish` <span class="keyword">VALUES</span> (<span class="string">&#x27;1397860242057375745&#x27;</span>, <span class="string">&#x27;脆皮烧鹅&#x27;</span>, <span class="string">&#x27;1397844391040167938&#x27;</span>, <span class="string">&#x27;12800.00&#x27;</span>, <span class="string">&#x27;123456786543213456&#x27;</span>, <span class="string">&#x27;e476f679-5c15-436b-87fa-8c4e9644bf33.jpeg&#x27;</span>, <span class="string">&#x27;“广东烤鸭美而香，却胜烧鹅说古冈（今新会），燕瘦环肥各佳妙，君休偏重便宜坊”，可见烧鹅与烧鸭在粤菜之中已早负盛名。作为广州最普遍和最受欢迎的烧烤肉食，以它的“色泽金红，皮脆肉嫩，味香可口”的特色，在省城各大街小巷的烧卤店随处可见。&#x27;</span>, <span class="string">&#x27;1&#x27;</span>, <span class="string">&#x27;0&#x27;</span>, <span class="string">&#x27;2021-05-27 10:20:27&#x27;</span>, <span class="string">&#x27;2021-05-27 10:20:27&#x27;</span>, <span class="string">&#x27;1&#x27;</span>, <span class="string">&#x27;1&#x27;</span>, <span class="string">&#x27;0&#x27;</span>);</span><br><span class="line"><span class="keyword">INSERT INTO</span> `dish` <span class="keyword">VALUES</span> (<span class="string">&#x27;1397860578738352129&#x27;</span>, <span class="string">&#x27;白切鸡&#x27;</span>, <span class="string">&#x27;1397844391040167938&#x27;</span>, <span class="string">&#x27;6600.00&#x27;</span>, <span class="string">&#x27;12345678654&#x27;</span>, <span class="string">&#x27;9ec6fc2d-50d2-422e-b954-de87dcd04198.jpeg&#x27;</span>, <span class="string">&#x27;白切鸡是一道色香味俱全的特色传统名肴，又叫白斩鸡，是粤菜系鸡肴中的一种，始于清代的民间。白切鸡通常选用细骨农家鸡与沙姜、蒜茸等食材，慢火煮浸白切鸡皮爽肉滑，清淡鲜美。著名的泮溪酒家白切鸡，曾获商业部优质产品金鼎奖。湛江白切鸡更是驰名粤港澳。粤菜厨坛中，鸡的菜式有200余款之多，而最为人常食不厌的正是白切鸡，深受食家青睐。&#x27;</span>, <span class="string">&#x27;1&#x27;</span>, <span class="string">&#x27;0&#x27;</span>, <span class="string">&#x27;2021-05-27 10:21:48&#x27;</span>, <span class="string">&#x27;2021-05-27 10:21:48&#x27;</span>, <span class="string">&#x27;1&#x27;</span>, <span class="string">&#x27;1&#x27;</span>, <span class="string">&#x27;0&#x27;</span>);</span><br><span class="line"><span class="keyword">INSERT INTO</span> `dish` <span class="keyword">VALUES</span> (<span class="string">&#x27;1397860792492666881&#x27;</span>, <span class="string">&#x27;烤乳猪&#x27;</span>, <span class="string">&#x27;1397844391040167938&#x27;</span>, <span class="string">&#x27;38800.00&#x27;</span>, <span class="string">&#x27;213456432123456&#x27;</span>, <span class="string">&#x27;2e96a7e3-affb-438e-b7c3-e1430df425c9.jpeg&#x27;</span>, <span class="string">&#x27;广式烧乳猪主料是小乳猪，辅料是蒜，调料是五香粉、芝麻酱、八角粉等，本菜品主要通过将食材放入炭火中烧烤而成。烤乳猪是广州最著名的特色菜，并且是“满汉全席”中的主打菜肴之一。烤乳猪也是许多年来广东人祭祖的祭品之一，是家家都少不了的应节之物，用乳猪祭完先人后，亲戚们再聚餐食用。&#x27;</span>, <span class="string">&#x27;1&#x27;</span>, <span class="string">&#x27;0&#x27;</span>, <span class="string">&#x27;2021-05-27 10:22:39&#x27;</span>, <span class="string">&#x27;2021-05-27 10:22:39&#x27;</span>, <span class="string">&#x27;1&#x27;</span>, <span class="string">&#x27;1&#x27;</span>, <span class="string">&#x27;0&#x27;</span>);</span><br><span class="line"><span class="keyword">INSERT INTO</span> `dish` <span class="keyword">VALUES</span> (<span class="string">&#x27;1397860963880316929&#x27;</span>, <span class="string">&#x27;脆皮乳鸽&#x27;</span>, <span class="string">&#x27;1397844391040167938&#x27;</span>, <span class="string">&#x27;10800.00&#x27;</span>, <span class="string">&#x27;1234563212345&#x27;</span>, <span class="string">&#x27;3fabb83a-1c09-4fd9-892b-4ef7457daafa.jpeg&#x27;</span>, <span class="string">&#x27;“脆皮乳鸽”是广东菜中的一道传统名菜，属于粤菜系，具有皮脆肉嫩、色泽红亮、鲜香味美的特点，常吃可使身体强健，清肺顺气。随着菜品制作工艺的不断发展，逐渐形成了熟炸法、生炸法和烤制法三种制作方法。无论那种制作方法，都是在鸽子经过一系列的加工，挂脆皮水后再加工而成，正宗的“脆皮乳鸽皮脆肉嫩、色泽红亮、鲜香味美、香气馥郁。这三种方法的制作过程都不算复杂，但想达到理想的效果并不容易。&#x27;</span>, <span class="string">&#x27;1&#x27;</span>, <span class="string">&#x27;0&#x27;</span>, <span class="string">&#x27;2021-05-27 10:23:19&#x27;</span>, <span class="string">&#x27;2021-05-27 10:23:19&#x27;</span>, <span class="string">&#x27;1&#x27;</span>, <span class="string">&#x27;1&#x27;</span>, <span class="string">&#x27;0&#x27;</span>);</span><br><span class="line"><span class="keyword">INSERT INTO</span> `dish` <span class="keyword">VALUES</span> (<span class="string">&#x27;1397861683434139649&#x27;</span>, <span class="string">&#x27;清蒸河鲜海鲜&#x27;</span>, <span class="string">&#x27;1397844391040167938&#x27;</span>, <span class="string">&#x27;38800.00&#x27;</span>, <span class="string">&#x27;1234567876543213456&#x27;</span>, <span class="string">&#x27;1405081e-f545-42e1-86a2-f7559ae2e276.jpeg&#x27;</span>, <span class="string">&#x27;新鲜的海鲜，清蒸是最好的处理方式。鲜，体会为什么叫海鲜。清蒸是广州最经典的烹饪手法，过去岭南地区由于峻山大岭阻隔，交通不便，经济发展起步慢，自家打的鱼放在锅里煮了就吃，没有太多的讲究，但却发现这清淡的煮法能使鱼的鲜甜跃然舌尖。&#x27;</span>, <span class="string">&#x27;1&#x27;</span>, <span class="string">&#x27;0&#x27;</span>, <span class="string">&#x27;2021-05-27 10:26:11&#x27;</span>, <span class="string">&#x27;2021-05-27 10:26:11&#x27;</span>, <span class="string">&#x27;1&#x27;</span>, <span class="string">&#x27;1&#x27;</span>, <span class="string">&#x27;0&#x27;</span>);</span><br><span class="line"><span class="keyword">INSERT INTO</span> `dish` <span class="keyword">VALUES</span> (<span class="string">&#x27;1397862198033297410&#x27;</span>, <span class="string">&#x27;老火靓汤&#x27;</span>, <span class="string">&#x27;1397844391040167938&#x27;</span>, <span class="string">&#x27;49800.00&#x27;</span>, <span class="string">&#x27;123456786532455&#x27;</span>, <span class="string">&#x27;583df4b7-a159-4cfc-9543-4f666120b25f.jpeg&#x27;</span>, <span class="string">&#x27;老火靓汤又称广府汤，是广府人传承数千年的食补养生秘方，慢火煲煮的中华老火靓汤，火候足，时间长，既取药补之效，又取入口之甘甜。 广府老火汤种类繁多，可以用各种汤料和烹调方法，烹制出各种不同口味、不同功效的汤来。&#x27;</span>, <span class="string">&#x27;1&#x27;</span>, <span class="string">&#x27;0&#x27;</span>, <span class="string">&#x27;2021-05-27 10:28:14&#x27;</span>, <span class="string">&#x27;2021-05-27 10:28:14&#x27;</span>, <span class="string">&#x27;1&#x27;</span>, <span class="string">&#x27;1&#x27;</span>, <span class="string">&#x27;0&#x27;</span>);</span><br><span class="line"><span class="keyword">INSERT INTO</span> `dish` <span class="keyword">VALUES</span> (<span class="string">&#x27;1397862477831122945&#x27;</span>, <span class="string">&#x27;上汤焗龙虾&#x27;</span>, <span class="string">&#x27;1397844391040167938&#x27;</span>, <span class="string">&#x27;108800.00&#x27;</span>, <span class="string">&#x27;1234567865432&#x27;</span>, <span class="string">&#x27;5b8d2da3-3744-4bb3-acdc-329056b8259d.jpeg&#x27;</span>, <span class="string">&#x27;上汤焗龙虾是一道色香味俱全的传统名菜，属于粤菜系。此菜以龙虾为主料，配以高汤制成的一道海鲜美食。本品肉质洁白细嫩，味道鲜美，蛋白质含量高，脂肪含量低，营养丰富。是色香味俱全的传统名菜。&#x27;</span>, <span class="string">&#x27;1&#x27;</span>, <span class="string">&#x27;0&#x27;</span>, <span class="string">&#x27;2021-05-27 10:29:20&#x27;</span>, <span class="string">&#x27;2021-05-27 10:29:20&#x27;</span>, <span class="string">&#x27;1&#x27;</span>, <span class="string">&#x27;1&#x27;</span>, <span class="string">&#x27;0&#x27;</span>);</span><br><span class="line"><span class="keyword">INSERT INTO</span> `dish` <span class="keyword">VALUES</span> (<span class="string">&#x27;1413342036832100354&#x27;</span>, <span class="string">&#x27;北冰洋&#x27;</span>, <span class="string">&#x27;1413341197421846529&#x27;</span>, <span class="string">&#x27;500.00&#x27;</span>, <span class="string">&#x27;&#x27;</span>, <span class="string">&#x27;c99e0aab-3cb7-4eaa-80fd-f47d4ffea694.png&#x27;</span>, <span class="string">&#x27;&#x27;</span>, <span class="string">&#x27;1&#x27;</span>, <span class="string">&#x27;0&#x27;</span>, <span class="string">&#x27;2021-07-09 11:39:35&#x27;</span>, <span class="string">&#x27;2021-07-09 15:12:18&#x27;</span>, <span class="string">&#x27;1&#x27;</span>, <span class="string">&#x27;1&#x27;</span>, <span class="string">&#x27;0&#x27;</span>);</span><br><span class="line"><span class="keyword">INSERT INTO</span> `dish` <span class="keyword">VALUES</span> (<span class="string">&#x27;1413384757047271425&#x27;</span>, <span class="string">&#x27;王老吉&#x27;</span>, <span class="string">&#x27;1413341197421846529&#x27;</span>, <span class="string">&#x27;500.00&#x27;</span>, <span class="string">&#x27;&#x27;</span>, <span class="string">&#x27;00874a5e-0df2-446b-8f69-a30eb7d88ee8.png&#x27;</span>, <span class="string">&#x27;&#x27;</span>, <span class="string">&#x27;1&#x27;</span>, <span class="string">&#x27;0&#x27;</span>, <span class="string">&#x27;2021-07-09 14:29:20&#x27;</span>, <span class="string">&#x27;2021-07-12 09:09:16&#x27;</span>, <span class="string">&#x27;1&#x27;</span>, <span class="string">&#x27;1&#x27;</span>, <span class="string">&#x27;0&#x27;</span>);</span><br><span class="line"><span class="keyword">INSERT INTO</span> `dish` <span class="keyword">VALUES</span> (<span class="string">&#x27;1413385247889891330&#x27;</span>, <span class="string">&#x27;米饭&#x27;</span>, <span class="string">&#x27;1413384954989060097&#x27;</span>, <span class="string">&#x27;200.00&#x27;</span>, <span class="string">&#x27;&#x27;</span>, <span class="string">&#x27;ee04a05a-1230-46b6-8ad5-1a95b140fff3.png&#x27;</span>, <span class="string">&#x27;&#x27;</span>, <span class="string">&#x27;1&#x27;</span>, <span class="string">&#x27;0&#x27;</span>, <span class="string">&#x27;2021-07-09 14:31:17&#x27;</span>, <span class="string">&#x27;2021-07-11 16:35:26&#x27;</span>, <span class="string">&#x27;1&#x27;</span>, <span class="string">&#x27;1&#x27;</span>, <span class="string">&#x27;0&#x27;</span>);</span><br><span class="line"></span><br><span class="line"><span class="comment">-- ----------------------------</span></span><br><span class="line"><span class="comment">-- Table structure for dish_flavor</span></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> `dish_flavor`;</span><br><span class="line"><span class="keyword">CREATE TABLE</span> `dish_flavor` (</span><br><span class="line">  `id` <span class="type">bigint</span>(<span class="number">20</span>) <span class="keyword">NOT NULL</span> COMMENT <span class="string">&#x27;主键&#x27;</span>,</span><br><span class="line">  `dish_id` <span class="type">bigint</span>(<span class="number">20</span>) <span class="keyword">NOT NULL</span> COMMENT <span class="string">&#x27;菜品&#x27;</span>,</span><br><span class="line">  `name` <span class="type">varchar</span>(<span class="number">64</span>) <span class="keyword">COLLATE</span> utf8_bin <span class="keyword">NOT NULL</span> COMMENT <span class="string">&#x27;口味名称&#x27;</span>,</span><br><span class="line">  `<span class="keyword">value</span>` <span class="type">varchar</span>(<span class="number">500</span>) <span class="keyword">COLLATE</span> utf8_bin <span class="keyword">DEFAULT</span> <span class="keyword">NULL</span> COMMENT <span class="string">&#x27;口味数据list&#x27;</span>,</span><br><span class="line">  `create_time` datetime <span class="keyword">NOT NULL</span> COMMENT <span class="string">&#x27;创建时间&#x27;</span>,</span><br><span class="line">  `update_time` datetime <span class="keyword">NOT NULL</span> COMMENT <span class="string">&#x27;更新时间&#x27;</span>,</span><br><span class="line">  `create_user` <span class="type">bigint</span>(<span class="number">20</span>) <span class="keyword">NOT NULL</span> COMMENT <span class="string">&#x27;创建人&#x27;</span>,</span><br><span class="line">  `update_user` <span class="type">bigint</span>(<span class="number">20</span>) <span class="keyword">NOT NULL</span> COMMENT <span class="string">&#x27;修改人&#x27;</span>,</span><br><span class="line">  `is_deleted` <span class="type">int</span>(<span class="number">11</span>) <span class="keyword">NOT NULL</span> <span class="keyword">DEFAULT</span> <span class="string">&#x27;0&#x27;</span> COMMENT <span class="string">&#x27;是否删除&#x27;</span>,</span><br><span class="line">  <span class="keyword">PRIMARY KEY</span> (`id`) <span class="keyword">USING</span> BTREE</span><br><span class="line">) ENGINE<span class="operator">=</span>InnoDB <span class="keyword">DEFAULT</span> CHARSET<span class="operator">=</span>utf8 <span class="keyword">COLLATE</span><span class="operator">=</span>utf8_bin 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="comment">-- Records of dish_flavor</span></span><br><span class="line"><span class="comment">-- ----------------------------</span></span><br><span class="line"><span class="keyword">INSERT INTO</span> `dish_flavor` <span class="keyword">VALUES</span> (<span class="string">&#x27;1397849417888346113&#x27;</span>, <span class="string">&#x27;1397849417854791681&#x27;</span>, <span class="string">&#x27;辣度&#x27;</span>, <span class="string">&#x27;[\&quot;不辣\&quot;,\&quot;微辣\&quot;,\&quot;中辣\&quot;,\&quot;重辣\&quot;]&#x27;</span>, <span class="string">&#x27;2021-05-27 09:37:27&#x27;</span>, <span class="string">&#x27;2021-05-27 09:37:27&#x27;</span>, <span class="string">&#x27;1&#x27;</span>, <span class="string">&#x27;1&#x27;</span>, <span class="string">&#x27;0&#x27;</span>);</span><br><span class="line"><span class="keyword">INSERT INTO</span> `dish_flavor` <span class="keyword">VALUES</span> (<span class="string">&#x27;1397849739297861633&#x27;</span>, <span class="string">&#x27;1397849739276890114&#x27;</span>, <span class="string">&#x27;忌口&#x27;</span>, <span class="string">&#x27;[\&quot;不要葱\&quot;,\&quot;不要蒜\&quot;,\&quot;不要香菜\&quot;,\&quot;不要辣\&quot;]&#x27;</span>, <span class="string">&#x27;2021-05-27 09:38:43&#x27;</span>, <span class="string">&#x27;2021-05-27 09:38:43&#x27;</span>, <span class="string">&#x27;1&#x27;</span>, <span class="string">&#x27;1&#x27;</span>, <span class="string">&#x27;0&#x27;</span>);</span><br><span class="line"><span class="keyword">INSERT INTO</span> `dish_flavor` <span class="keyword">VALUES</span> (<span class="string">&#x27;1397849739323027458&#x27;</span>, <span class="string">&#x27;1397849739276890114&#x27;</span>, <span class="string">&#x27;辣度&#x27;</span>, <span class="string">&#x27;[\&quot;不辣\&quot;,\&quot;微辣\&quot;,\&quot;中辣\&quot;,\&quot;重辣\&quot;]&#x27;</span>, <span class="string">&#x27;2021-05-27 09:38:43&#x27;</span>, <span class="string">&#x27;2021-05-27 09:38:43&#x27;</span>, <span class="string">&#x27;1&#x27;</span>, <span class="string">&#x27;1&#x27;</span>, <span class="string">&#x27;0&#x27;</span>);</span><br><span class="line"><span class="keyword">INSERT INTO</span> `dish_flavor` <span class="keyword">VALUES</span> (<span class="string">&#x27;1397849936421761025&#x27;</span>, <span class="string">&#x27;1397849936404983809&#x27;</span>, <span class="string">&#x27;忌口&#x27;</span>, <span class="string">&#x27;[\&quot;不要葱\&quot;,\&quot;不要蒜\&quot;,\&quot;不要香菜\&quot;,\&quot;不要辣\&quot;]&#x27;</span>, <span class="string">&#x27;2021-05-27 09:39:30&#x27;</span>, <span class="string">&#x27;2021-05-27 09:39:30&#x27;</span>, <span class="string">&#x27;1&#x27;</span>, <span class="string">&#x27;1&#x27;</span>, <span class="string">&#x27;0&#x27;</span>);</span><br><span class="line"><span class="keyword">INSERT INTO</span> `dish_flavor` <span class="keyword">VALUES</span> (<span class="string">&#x27;1397849936438538241&#x27;</span>, <span class="string">&#x27;1397849936404983809&#x27;</span>, <span class="string">&#x27;辣度&#x27;</span>, <span class="string">&#x27;[\&quot;不辣\&quot;,\&quot;微辣\&quot;,\&quot;中辣\&quot;,\&quot;重辣\&quot;]&#x27;</span>, <span class="string">&#x27;2021-05-27 09:39:30&#x27;</span>, <span class="string">&#x27;2021-05-27 09:39:30&#x27;</span>, <span class="string">&#x27;1&#x27;</span>, <span class="string">&#x27;1&#x27;</span>, <span class="string">&#x27;0&#x27;</span>);</span><br><span class="line"><span class="keyword">INSERT INTO</span> `dish_flavor` <span class="keyword">VALUES</span> (<span class="string">&#x27;1397850141015715841&#x27;</span>, <span class="string">&#x27;1397850140982161409&#x27;</span>, <span class="string">&#x27;忌口&#x27;</span>, <span class="string">&#x27;[\&quot;不要葱\&quot;,\&quot;不要蒜\&quot;,\&quot;不要香菜\&quot;,\&quot;不要辣\&quot;]&#x27;</span>, <span class="string">&#x27;2021-05-27 09:40:19&#x27;</span>, <span class="string">&#x27;2021-05-27 09:40:19&#x27;</span>, <span class="string">&#x27;1&#x27;</span>, <span class="string">&#x27;1&#x27;</span>, <span class="string">&#x27;0&#x27;</span>);</span><br><span class="line"><span class="keyword">INSERT INTO</span> `dish_flavor` <span class="keyword">VALUES</span> (<span class="string">&#x27;1397850141040881665&#x27;</span>, <span class="string">&#x27;1397850140982161409&#x27;</span>, <span class="string">&#x27;辣度&#x27;</span>, <span class="string">&#x27;[\&quot;不辣\&quot;,\&quot;微辣\&quot;,\&quot;中辣\&quot;,\&quot;重辣\&quot;]&#x27;</span>, <span class="string">&#x27;2021-05-27 09:40:19&#x27;</span>, <span class="string">&#x27;2021-05-27 09:40:19&#x27;</span>, <span class="string">&#x27;1&#x27;</span>, <span class="string">&#x27;1&#x27;</span>, <span class="string">&#x27;0&#x27;</span>);</span><br><span class="line"><span class="keyword">INSERT INTO</span> `dish_flavor` <span class="keyword">VALUES</span> (<span class="string">&#x27;1397850392120307713&#x27;</span>, <span class="string">&#x27;1397850392090947585&#x27;</span>, <span class="string">&#x27;辣度&#x27;</span>, <span class="string">&#x27;[\&quot;不辣\&quot;,\&quot;微辣\&quot;,\&quot;中辣\&quot;,\&quot;重辣\&quot;]&#x27;</span>, <span class="string">&#x27;2021-05-27 09:41:19&#x27;</span>, <span class="string">&#x27;2021-05-27 09:41:19&#x27;</span>, <span class="string">&#x27;1&#x27;</span>, <span class="string">&#x27;1&#x27;</span>, <span class="string">&#x27;0&#x27;</span>);</span><br><span class="line"><span class="keyword">INSERT INTO</span> `dish_flavor` <span class="keyword">VALUES</span> (<span class="string">&#x27;1397850392137084929&#x27;</span>, <span class="string">&#x27;1397850392090947585&#x27;</span>, <span class="string">&#x27;辣度&#x27;</span>, <span class="string">&#x27;[\&quot;不辣\&quot;,\&quot;微辣\&quot;,\&quot;中辣\&quot;,\&quot;重辣\&quot;]&#x27;</span>, <span class="string">&#x27;2021-05-27 09:41:19&#x27;</span>, <span class="string">&#x27;2021-05-27 09:41:19&#x27;</span>, <span class="string">&#x27;1&#x27;</span>, <span class="string">&#x27;1&#x27;</span>, <span class="string">&#x27;0&#x27;</span>);</span><br><span class="line"><span class="keyword">INSERT INTO</span> `dish_flavor` <span class="keyword">VALUES</span> (<span class="string">&#x27;1397850630734262274&#x27;</span>, <span class="string">&#x27;1397850630700707841&#x27;</span>, <span class="string">&#x27;忌口&#x27;</span>, <span class="string">&#x27;[\&quot;不要葱\&quot;,\&quot;不要蒜\&quot;,\&quot;不要香菜\&quot;,\&quot;不要辣\&quot;]&#x27;</span>, <span class="string">&#x27;2021-05-27 09:42:16&#x27;</span>, <span class="string">&#x27;2021-05-27 09:42:16&#x27;</span>, <span class="string">&#x27;1&#x27;</span>, <span class="string">&#x27;1&#x27;</span>, <span class="string">&#x27;0&#x27;</span>);</span><br><span class="line"><span class="keyword">INSERT INTO</span> `dish_flavor` <span class="keyword">VALUES</span> (<span class="string">&#x27;1397850630755233794&#x27;</span>, <span class="string">&#x27;1397850630700707841&#x27;</span>, <span class="string">&#x27;辣度&#x27;</span>, <span class="string">&#x27;[\&quot;微辣\&quot;,\&quot;中辣\&quot;,\&quot;重辣\&quot;]&#x27;</span>, <span class="string">&#x27;2021-05-27 09:42:16&#x27;</span>, <span class="string">&#x27;2021-05-27 09:42:16&#x27;</span>, <span class="string">&#x27;1&#x27;</span>, <span class="string">&#x27;1&#x27;</span>, <span class="string">&#x27;0&#x27;</span>);</span><br><span class="line"><span class="keyword">INSERT INTO</span> `dish_flavor` <span class="keyword">VALUES</span> (<span class="string">&#x27;1397850851274960898&#x27;</span>, <span class="string">&#x27;1397850851245600769&#x27;</span>, <span class="string">&#x27;忌口&#x27;</span>, <span class="string">&#x27;[\&quot;不要蒜\&quot;,\&quot;不要香菜\&quot;,\&quot;不要辣\&quot;]&#x27;</span>, <span class="string">&#x27;2021-05-27 09:43:08&#x27;</span>, <span class="string">&#x27;2021-05-27 09:43:08&#x27;</span>, <span class="string">&#x27;1&#x27;</span>, <span class="string">&#x27;1&#x27;</span>, <span class="string">&#x27;0&#x27;</span>);</span><br><span class="line"><span class="keyword">INSERT INTO</span> `dish_flavor` <span class="keyword">VALUES</span> (<span class="string">&#x27;1397850851283349505&#x27;</span>, <span class="string">&#x27;1397850851245600769&#x27;</span>, <span class="string">&#x27;辣度&#x27;</span>, <span class="string">&#x27;[\&quot;不辣\&quot;,\&quot;微辣\&quot;,\&quot;中辣\&quot;,\&quot;重辣\&quot;]&#x27;</span>, <span class="string">&#x27;2021-05-27 09:43:08&#x27;</span>, <span class="string">&#x27;2021-05-27 09:43:08&#x27;</span>, <span class="string">&#x27;1&#x27;</span>, <span class="string">&#x27;1&#x27;</span>, <span class="string">&#x27;0&#x27;</span>);</span><br><span class="line"><span class="keyword">INSERT INTO</span> `dish_flavor` <span class="keyword">VALUES</span> (<span class="string">&#x27;1397851099523231745&#x27;</span>, <span class="string">&#x27;1397851099502260226&#x27;</span>, <span class="string">&#x27;忌口&#x27;</span>, <span class="string">&#x27;[\&quot;不要葱\&quot;,\&quot;不要蒜\&quot;,\&quot;不要香菜\&quot;,\&quot;不要辣\&quot;]&#x27;</span>, <span class="string">&#x27;2021-05-27 09:44:08&#x27;</span>, <span class="string">&#x27;2021-05-27 09:44:08&#x27;</span>, <span class="string">&#x27;1&#x27;</span>, <span class="string">&#x27;1&#x27;</span>, <span class="string">&#x27;0&#x27;</span>);</span><br><span class="line"><span class="keyword">INSERT INTO</span> `dish_flavor` <span class="keyword">VALUES</span> (<span class="string">&#x27;1397851099527426050&#x27;</span>, <span class="string">&#x27;1397851099502260226&#x27;</span>, <span class="string">&#x27;辣度&#x27;</span>, <span class="string">&#x27;[\&quot;不辣\&quot;,\&quot;微辣\&quot;,\&quot;中辣\&quot;]&#x27;</span>, <span class="string">&#x27;2021-05-27 09:44:08&#x27;</span>, <span class="string">&#x27;2021-05-27 09:44:08&#x27;</span>, <span class="string">&#x27;1&#x27;</span>, <span class="string">&#x27;1&#x27;</span>, <span class="string">&#x27;0&#x27;</span>);</span><br><span class="line"><span class="keyword">INSERT INTO</span> `dish_flavor` <span class="keyword">VALUES</span> (<span class="string">&#x27;1397851370483658754&#x27;</span>, <span class="string">&#x27;1397851370462687234&#x27;</span>, <span class="string">&#x27;温度&#x27;</span>, <span class="string">&#x27;[\&quot;热饮\&quot;,\&quot;常温\&quot;,\&quot;去冰\&quot;,\&quot;少冰\&quot;,\&quot;多冰\&quot;]&#x27;</span>, <span class="string">&#x27;2021-05-27 09:45:12&#x27;</span>, <span class="string">&#x27;2021-05-27 09:45:12&#x27;</span>, <span class="string">&#x27;1&#x27;</span>, <span class="string">&#x27;1&#x27;</span>, <span class="string">&#x27;0&#x27;</span>);</span><br><span class="line"><span class="keyword">INSERT INTO</span> `dish_flavor` <span class="keyword">VALUES</span> (<span class="string">&#x27;1397851370483658755&#x27;</span>, <span class="string">&#x27;1397851370462687234&#x27;</span>, <span class="string">&#x27;忌口&#x27;</span>, <span class="string">&#x27;[\&quot;不要葱\&quot;,\&quot;不要蒜\&quot;,\&quot;不要香菜\&quot;,\&quot;不要辣\&quot;]&#x27;</span>, <span class="string">&#x27;2021-05-27 09:45:12&#x27;</span>, <span class="string">&#x27;2021-05-27 09:45:12&#x27;</span>, <span class="string">&#x27;1&#x27;</span>, <span class="string">&#x27;1&#x27;</span>, <span class="string">&#x27;0&#x27;</span>);</span><br><span class="line"><span class="keyword">INSERT INTO</span> `dish_flavor` <span class="keyword">VALUES</span> (<span class="string">&#x27;1397851370483658756&#x27;</span>, <span class="string">&#x27;1397851370462687234&#x27;</span>, <span class="string">&#x27;辣度&#x27;</span>, <span class="string">&#x27;[\&quot;不辣\&quot;,\&quot;微辣\&quot;,\&quot;中辣\&quot;,\&quot;重辣\&quot;]&#x27;</span>, <span class="string">&#x27;2021-05-27 09:45:12&#x27;</span>, <span class="string">&#x27;2021-05-27 09:45:12&#x27;</span>, <span class="string">&#x27;1&#x27;</span>, <span class="string">&#x27;1&#x27;</span>, <span class="string">&#x27;0&#x27;</span>);</span><br><span class="line"><span class="keyword">INSERT INTO</span> `dish_flavor` <span class="keyword">VALUES</span> (<span class="string">&#x27;1397851668283437058&#x27;</span>, <span class="string">&#x27;1397851668262465537&#x27;</span>, <span class="string">&#x27;温度&#x27;</span>, <span class="string">&#x27;[\&quot;热饮\&quot;,\&quot;常温\&quot;,\&quot;去冰\&quot;,\&quot;少冰\&quot;,\&quot;多冰\&quot;]&#x27;</span>, <span class="string">&#x27;2021-05-27 09:46:23&#x27;</span>, <span class="string">&#x27;2021-05-27 09:46:23&#x27;</span>, <span class="string">&#x27;1&#x27;</span>, <span class="string">&#x27;1&#x27;</span>, <span class="string">&#x27;0&#x27;</span>);</span><br><span class="line"><span class="keyword">INSERT INTO</span> `dish_flavor` <span class="keyword">VALUES</span> (<span class="string">&#x27;1397852391180120065&#x27;</span>, <span class="string">&#x27;1397852391150759938&#x27;</span>, <span class="string">&#x27;忌口&#x27;</span>, <span class="string">&#x27;[\&quot;不要葱\&quot;,\&quot;不要香菜\&quot;,\&quot;不要辣\&quot;]&#x27;</span>, <span class="string">&#x27;2021-05-27 09:49:16&#x27;</span>, <span class="string">&#x27;2021-05-27 09:49:16&#x27;</span>, <span class="string">&#x27;1&#x27;</span>, <span class="string">&#x27;1&#x27;</span>, <span class="string">&#x27;0&#x27;</span>);</span><br><span class="line"><span class="keyword">INSERT INTO</span> `dish_flavor` <span class="keyword">VALUES</span> (<span class="string">&#x27;1397852391196897281&#x27;</span>, <span class="string">&#x27;1397852391150759938&#x27;</span>, <span class="string">&#x27;辣度&#x27;</span>, <span class="string">&#x27;[\&quot;不辣\&quot;,\&quot;微辣\&quot;,\&quot;重辣\&quot;]&#x27;</span>, <span class="string">&#x27;2021-05-27 09:49:16&#x27;</span>, <span class="string">&#x27;2021-05-27 09:49:16&#x27;</span>, <span class="string">&#x27;1&#x27;</span>, <span class="string">&#x27;1&#x27;</span>, <span class="string">&#x27;0&#x27;</span>);</span><br><span class="line"><span class="keyword">INSERT INTO</span> `dish_flavor` <span class="keyword">VALUES</span> (<span class="string">&#x27;1397853183307984898&#x27;</span>, <span class="string">&#x27;1397853183287013378&#x27;</span>, <span class="string">&#x27;辣度&#x27;</span>, <span class="string">&#x27;[\&quot;不辣\&quot;,\&quot;微辣\&quot;,\&quot;中辣\&quot;,\&quot;重辣\&quot;]&#x27;</span>, <span class="string">&#x27;2021-05-27 09:52:24&#x27;</span>, <span class="string">&#x27;2021-05-27 09:52:24&#x27;</span>, <span class="string">&#x27;1&#x27;</span>, <span class="string">&#x27;1&#x27;</span>, <span class="string">&#x27;0&#x27;</span>);</span><br><span class="line"><span class="keyword">INSERT INTO</span> `dish_flavor` <span class="keyword">VALUES</span> (<span class="string">&#x27;1397853423486414850&#x27;</span>, <span class="string">&#x27;1397853423461249026&#x27;</span>, <span class="string">&#x27;辣度&#x27;</span>, <span class="string">&#x27;[\&quot;不辣\&quot;,\&quot;微辣\&quot;,\&quot;中辣\&quot;,\&quot;重辣\&quot;]&#x27;</span>, <span class="string">&#x27;2021-05-27 09:53:22&#x27;</span>, <span class="string">&#x27;2021-05-27 09:53:22&#x27;</span>, <span class="string">&#x27;1&#x27;</span>, <span class="string">&#x27;1&#x27;</span>, <span class="string">&#x27;0&#x27;</span>);</span><br><span class="line"><span class="keyword">INSERT INTO</span> `dish_flavor` <span class="keyword">VALUES</span> (<span class="string">&#x27;1397853709126905857&#x27;</span>, <span class="string">&#x27;1397853709101740034&#x27;</span>, <span class="string">&#x27;忌口&#x27;</span>, <span class="string">&#x27;[\&quot;不要葱\&quot;,\&quot;不要蒜\&quot;,\&quot;不要香菜\&quot;,\&quot;不要辣\&quot;]&#x27;</span>, <span class="string">&#x27;2021-05-27 09:54:30&#x27;</span>, <span class="string">&#x27;2021-05-27 09:54:30&#x27;</span>, <span class="string">&#x27;1&#x27;</span>, <span class="string">&#x27;1&#x27;</span>, <span class="string">&#x27;0&#x27;</span>);</span><br><span class="line"><span class="keyword">INSERT INTO</span> `dish_flavor` <span class="keyword">VALUES</span> (<span class="string">&#x27;1397853890283089922&#x27;</span>, <span class="string">&#x27;1397853890262118402&#x27;</span>, <span class="string">&#x27;辣度&#x27;</span>, <span class="string">&#x27;[\&quot;不辣\&quot;,\&quot;微辣\&quot;,\&quot;中辣\&quot;,\&quot;重辣\&quot;]&#x27;</span>, <span class="string">&#x27;2021-05-27 09:55:13&#x27;</span>, <span class="string">&#x27;2021-05-27 09:55:13&#x27;</span>, <span class="string">&#x27;1&#x27;</span>, <span class="string">&#x27;1&#x27;</span>, <span class="string">&#x27;0&#x27;</span>);</span><br><span class="line"><span class="keyword">INSERT INTO</span> `dish_flavor` <span class="keyword">VALUES</span> (<span class="string">&#x27;1397854133632413697&#x27;</span>, <span class="string">&#x27;1397854133603053569&#x27;</span>, <span class="string">&#x27;温度&#x27;</span>, <span class="string">&#x27;[\&quot;热饮\&quot;,\&quot;常温\&quot;,\&quot;去冰\&quot;,\&quot;少冰\&quot;,\&quot;多冰\&quot;]&#x27;</span>, <span class="string">&#x27;2021-05-27 09:56:11&#x27;</span>, <span class="string">&#x27;2021-05-27 09:56:11&#x27;</span>, <span class="string">&#x27;1&#x27;</span>, <span class="string">&#x27;1&#x27;</span>, <span class="string">&#x27;0&#x27;</span>);</span><br><span class="line"><span class="keyword">INSERT INTO</span> `dish_flavor` <span class="keyword">VALUES</span> (<span class="string">&#x27;1397854652623007745&#x27;</span>, <span class="string">&#x27;1397854652581064706&#x27;</span>, <span class="string">&#x27;忌口&#x27;</span>, <span class="string">&#x27;[\&quot;不要葱\&quot;,\&quot;不要蒜\&quot;,\&quot;不要香菜\&quot;,\&quot;不要辣\&quot;]&#x27;</span>, <span class="string">&#x27;2021-05-27 09:58:15&#x27;</span>, <span class="string">&#x27;2021-05-27 09:58:15&#x27;</span>, <span class="string">&#x27;1&#x27;</span>, <span class="string">&#x27;1&#x27;</span>, <span class="string">&#x27;0&#x27;</span>);</span><br><span class="line"><span class="keyword">INSERT INTO</span> `dish_flavor` <span class="keyword">VALUES</span> (<span class="string">&#x27;1397854652635590658&#x27;</span>, <span class="string">&#x27;1397854652581064706&#x27;</span>, <span class="string">&#x27;辣度&#x27;</span>, <span class="string">&#x27;[\&quot;不辣\&quot;,\&quot;微辣\&quot;,\&quot;中辣\&quot;,\&quot;重辣\&quot;]&#x27;</span>, <span class="string">&#x27;2021-05-27 09:58:15&#x27;</span>, <span class="string">&#x27;2021-05-27 09:58:15&#x27;</span>, <span class="string">&#x27;1&#x27;</span>, <span class="string">&#x27;1&#x27;</span>, <span class="string">&#x27;0&#x27;</span>);</span><br><span class="line"><span class="keyword">INSERT INTO</span> `dish_flavor` <span class="keyword">VALUES</span> (<span class="string">&#x27;1397854865735593986&#x27;</span>, <span class="string">&#x27;1397854865672679425&#x27;</span>, <span class="string">&#x27;辣度&#x27;</span>, <span class="string">&#x27;[\&quot;不辣\&quot;,\&quot;微辣\&quot;,\&quot;中辣\&quot;,\&quot;重辣\&quot;]&#x27;</span>, <span class="string">&#x27;2021-05-27 09:59:06&#x27;</span>, <span class="string">&#x27;2021-05-27 09:59:06&#x27;</span>, <span class="string">&#x27;1&#x27;</span>, <span class="string">&#x27;1&#x27;</span>, <span class="string">&#x27;0&#x27;</span>);</span><br><span class="line"><span class="keyword">INSERT INTO</span> `dish_flavor` <span class="keyword">VALUES</span> (<span class="string">&#x27;1397855742303186946&#x27;</span>, <span class="string">&#x27;1397855742273826817&#x27;</span>, <span class="string">&#x27;辣度&#x27;</span>, <span class="string">&#x27;[\&quot;不辣\&quot;,\&quot;微辣\&quot;,\&quot;中辣\&quot;,\&quot;重辣\&quot;]&#x27;</span>, <span class="string">&#x27;2021-05-27 10:02:35&#x27;</span>, <span class="string">&#x27;2021-05-27 10:02:35&#x27;</span>, <span class="string">&#x27;1&#x27;</span>, <span class="string">&#x27;1&#x27;</span>, <span class="string">&#x27;0&#x27;</span>);</span><br><span class="line"><span class="keyword">INSERT INTO</span> `dish_flavor` <span class="keyword">VALUES</span> (<span class="string">&#x27;1397855906497605633&#x27;</span>, <span class="string">&#x27;1397855906468245506&#x27;</span>, <span class="string">&#x27;忌口&#x27;</span>, <span class="string">&#x27;[\&quot;不要葱\&quot;,\&quot;不要蒜\&quot;,\&quot;不要香菜\&quot;,\&quot;不要辣\&quot;]&#x27;</span>, <span class="string">&#x27;2021-05-27 10:03:14&#x27;</span>, <span class="string">&#x27;2021-05-27 10:03:14&#x27;</span>, <span class="string">&#x27;1&#x27;</span>, <span class="string">&#x27;1&#x27;</span>, <span class="string">&#x27;0&#x27;</span>);</span><br><span class="line"><span class="keyword">INSERT INTO</span> `dish_flavor` <span class="keyword">VALUES</span> (<span class="string">&#x27;1397856190573621250&#x27;</span>, <span class="string">&#x27;1397856190540066818&#x27;</span>, <span class="string">&#x27;辣度&#x27;</span>, <span class="string">&#x27;[\&quot;不辣\&quot;,\&quot;微辣\&quot;,\&quot;中辣\&quot;,\&quot;重辣\&quot;]&#x27;</span>, <span class="string">&#x27;2021-05-27 10:04:21&#x27;</span>, <span class="string">&#x27;2021-05-27 10:04:21&#x27;</span>, <span class="string">&#x27;1&#x27;</span>, <span class="string">&#x27;1&#x27;</span>, <span class="string">&#x27;0&#x27;</span>);</span><br><span class="line"><span class="keyword">INSERT INTO</span> `dish_flavor` <span class="keyword">VALUES</span> (<span class="string">&#x27;1397859056709316609&#x27;</span>, <span class="string">&#x27;1397859056684150785&#x27;</span>, <span class="string">&#x27;辣度&#x27;</span>, <span class="string">&#x27;[\&quot;不辣\&quot;,\&quot;微辣\&quot;,\&quot;中辣\&quot;,\&quot;重辣\&quot;]&#x27;</span>, <span class="string">&#x27;2021-05-27 10:15:45&#x27;</span>, <span class="string">&#x27;2021-05-27 10:15:45&#x27;</span>, <span class="string">&#x27;1&#x27;</span>, <span class="string">&#x27;1&#x27;</span>, <span class="string">&#x27;0&#x27;</span>);</span><br><span class="line"><span class="keyword">INSERT INTO</span> `dish_flavor` <span class="keyword">VALUES</span> (<span class="string">&#x27;1397859277837217794&#x27;</span>, <span class="string">&#x27;1397859277812051969&#x27;</span>, <span class="string">&#x27;辣度&#x27;</span>, <span class="string">&#x27;[\&quot;不辣\&quot;,\&quot;微辣\&quot;,\&quot;中辣\&quot;,\&quot;重辣\&quot;]&#x27;</span>, <span class="string">&#x27;2021-05-27 10:16:37&#x27;</span>, <span class="string">&#x27;2021-05-27 10:16:37&#x27;</span>, <span class="string">&#x27;1&#x27;</span>, <span class="string">&#x27;1&#x27;</span>, <span class="string">&#x27;0&#x27;</span>);</span><br><span class="line"><span class="keyword">INSERT INTO</span> `dish_flavor` <span class="keyword">VALUES</span> (<span class="string">&#x27;1397859487502086146&#x27;</span>, <span class="string">&#x27;1397859487476920321&#x27;</span>, <span class="string">&#x27;辣度&#x27;</span>, <span class="string">&#x27;[\&quot;不辣\&quot;,\&quot;微辣\&quot;,\&quot;中辣\&quot;,\&quot;重辣\&quot;]&#x27;</span>, <span class="string">&#x27;2021-05-27 10:17:27&#x27;</span>, <span class="string">&#x27;2021-05-27 10:17:27&#x27;</span>, <span class="string">&#x27;1&#x27;</span>, <span class="string">&#x27;1&#x27;</span>, <span class="string">&#x27;0&#x27;</span>);</span><br><span class="line"><span class="keyword">INSERT INTO</span> `dish_flavor` <span class="keyword">VALUES</span> (<span class="string">&#x27;1397859757061615618&#x27;</span>, <span class="string">&#x27;1397859757036449794&#x27;</span>, <span class="string">&#x27;甜味&#x27;</span>, <span class="string">&#x27;[\&quot;无糖\&quot;,\&quot;少糖\&quot;,\&quot;半躺\&quot;,\&quot;多糖\&quot;,\&quot;全糖\&quot;]&#x27;</span>, <span class="string">&#x27;2021-05-27 10:18:32&#x27;</span>, <span class="string">&#x27;2021-05-27 10:18:32&#x27;</span>, <span class="string">&#x27;1&#x27;</span>, <span class="string">&#x27;1&#x27;</span>, <span class="string">&#x27;0&#x27;</span>);</span><br><span class="line"><span class="keyword">INSERT INTO</span> `dish_flavor` <span class="keyword">VALUES</span> (<span class="string">&#x27;1397860242086735874&#x27;</span>, <span class="string">&#x27;1397860242057375745&#x27;</span>, <span class="string">&#x27;辣度&#x27;</span>, <span class="string">&#x27;[\&quot;不辣\&quot;,\&quot;微辣\&quot;,\&quot;中辣\&quot;,\&quot;重辣\&quot;]&#x27;</span>, <span class="string">&#x27;2021-05-27 10:20:27&#x27;</span>, <span class="string">&#x27;2021-05-27 10:20:27&#x27;</span>, <span class="string">&#x27;1&#x27;</span>, <span class="string">&#x27;1&#x27;</span>, <span class="string">&#x27;0&#x27;</span>);</span><br><span class="line"><span class="keyword">INSERT INTO</span> `dish_flavor` <span class="keyword">VALUES</span> (<span class="string">&#x27;1397860963918065665&#x27;</span>, <span class="string">&#x27;1397860963880316929&#x27;</span>, <span class="string">&#x27;辣度&#x27;</span>, <span class="string">&#x27;[\&quot;不辣\&quot;,\&quot;微辣\&quot;,\&quot;中辣\&quot;,\&quot;重辣\&quot;]&#x27;</span>, <span class="string">&#x27;2021-05-27 10:23:19&#x27;</span>, <span class="string">&#x27;2021-05-27 10:23:19&#x27;</span>, <span class="string">&#x27;1&#x27;</span>, <span class="string">&#x27;1&#x27;</span>, <span class="string">&#x27;0&#x27;</span>);</span><br><span class="line"><span class="keyword">INSERT INTO</span> `dish_flavor` <span class="keyword">VALUES</span> (<span class="string">&#x27;1397861135754506242&#x27;</span>, <span class="string">&#x27;1397861135733534722&#x27;</span>, <span class="string">&#x27;甜味&#x27;</span>, <span class="string">&#x27;[\&quot;无糖\&quot;,\&quot;少糖\&quot;,\&quot;半躺\&quot;,\&quot;多糖\&quot;,\&quot;全糖\&quot;]&#x27;</span>, <span class="string">&#x27;2021-05-27 10:24:00&#x27;</span>, <span class="string">&#x27;2021-05-27 10:24:00&#x27;</span>, <span class="string">&#x27;1&#x27;</span>, <span class="string">&#x27;1&#x27;</span>, <span class="string">&#x27;0&#x27;</span>);</span><br><span class="line"><span class="keyword">INSERT INTO</span> `dish_flavor` <span class="keyword">VALUES</span> (<span class="string">&#x27;1397861370035744769&#x27;</span>, <span class="string">&#x27;1397861370010578945&#x27;</span>, <span class="string">&#x27;辣度&#x27;</span>, <span class="string">&#x27;[\&quot;不辣\&quot;,\&quot;微辣\&quot;,\&quot;中辣\&quot;,\&quot;重辣\&quot;]&#x27;</span>, <span class="string">&#x27;2021-05-27 10:24:56&#x27;</span>, <span class="string">&#x27;2021-05-27 10:24:56&#x27;</span>, <span class="string">&#x27;1&#x27;</span>, <span class="string">&#x27;1&#x27;</span>, <span class="string">&#x27;0&#x27;</span>);</span><br><span class="line"><span class="keyword">INSERT INTO</span> `dish_flavor` <span class="keyword">VALUES</span> (<span class="string">&#x27;1397861683459305474&#x27;</span>, <span class="string">&#x27;1397861683434139649&#x27;</span>, <span class="string">&#x27;忌口&#x27;</span>, <span class="string">&#x27;[\&quot;不要葱\&quot;,\&quot;不要蒜\&quot;,\&quot;不要香菜\&quot;,\&quot;不要辣\&quot;]&#x27;</span>, <span class="string">&#x27;2021-05-27 10:26:11&#x27;</span>, <span class="string">&#x27;2021-05-27 10:26:11&#x27;</span>, <span class="string">&#x27;1&#x27;</span>, <span class="string">&#x27;1&#x27;</span>, <span class="string">&#x27;0&#x27;</span>);</span><br><span class="line"><span class="keyword">INSERT INTO</span> `dish_flavor` <span class="keyword">VALUES</span> (<span class="string">&#x27;1397861898467717121&#x27;</span>, <span class="string">&#x27;1397861898438356993&#x27;</span>, <span class="string">&#x27;忌口&#x27;</span>, <span class="string">&#x27;[\&quot;不要葱\&quot;,\&quot;不要蒜\&quot;,\&quot;不要香菜\&quot;,\&quot;不要辣\&quot;]&#x27;</span>, <span class="string">&#x27;2021-05-27 10:27:02&#x27;</span>, <span class="string">&#x27;2021-05-27 10:27:02&#x27;</span>, <span class="string">&#x27;1&#x27;</span>, <span class="string">&#x27;1&#x27;</span>, <span class="string">&#x27;0&#x27;</span>);</span><br><span class="line"><span class="keyword">INSERT INTO</span> `dish_flavor` <span class="keyword">VALUES</span> (<span class="string">&#x27;1397862198054268929&#x27;</span>, <span class="string">&#x27;1397862198033297410&#x27;</span>, <span class="string">&#x27;忌口&#x27;</span>, <span class="string">&#x27;[\&quot;不要葱\&quot;,\&quot;不要蒜\&quot;,\&quot;不要香菜\&quot;,\&quot;不要辣\&quot;]&#x27;</span>, <span class="string">&#x27;2021-05-27 10:28:14&#x27;</span>, <span class="string">&#x27;2021-05-27 10:28:14&#x27;</span>, <span class="string">&#x27;1&#x27;</span>, <span class="string">&#x27;1&#x27;</span>, <span class="string">&#x27;0&#x27;</span>);</span><br><span class="line"><span class="keyword">INSERT INTO</span> `dish_flavor` <span class="keyword">VALUES</span> (<span class="string">&#x27;1397862477835317250&#x27;</span>, <span class="string">&#x27;1397862477831122945&#x27;</span>, <span class="string">&#x27;辣度&#x27;</span>, <span class="string">&#x27;[\&quot;不辣\&quot;,\&quot;微辣\&quot;,\&quot;中辣\&quot;]&#x27;</span>, <span class="string">&#x27;2021-05-27 10:29:20&#x27;</span>, <span class="string">&#x27;2021-05-27 10:29:20&#x27;</span>, <span class="string">&#x27;1&#x27;</span>, <span class="string">&#x27;1&#x27;</span>, <span class="string">&#x27;0&#x27;</span>);</span><br><span class="line"><span class="keyword">INSERT INTO</span> `dish_flavor` <span class="keyword">VALUES</span> (<span class="string">&#x27;1398089545865015297&#x27;</span>, <span class="string">&#x27;1398089545676271617&#x27;</span>, <span class="string">&#x27;温度&#x27;</span>, <span class="string">&#x27;[\&quot;热饮\&quot;,\&quot;常温\&quot;,\&quot;去冰\&quot;,\&quot;少冰\&quot;,\&quot;多冰\&quot;]&#x27;</span>, <span class="string">&#x27;2021-05-28 01:31:38&#x27;</span>, <span class="string">&#x27;2021-05-28 01:31:38&#x27;</span>, <span class="string">&#x27;1&#x27;</span>, <span class="string">&#x27;1&#x27;</span>, <span class="string">&#x27;0&#x27;</span>);</span><br><span class="line"><span class="keyword">INSERT INTO</span> `dish_flavor` <span class="keyword">VALUES</span> (<span class="string">&#x27;1398089782323097601&#x27;</span>, <span class="string">&#x27;1398089782285348866&#x27;</span>, <span class="string">&#x27;辣度&#x27;</span>, <span class="string">&#x27;[\&quot;不辣\&quot;,\&quot;微辣\&quot;,\&quot;中辣\&quot;,\&quot;重辣\&quot;]&#x27;</span>, <span class="string">&#x27;2021-05-28 01:32:34&#x27;</span>, <span class="string">&#x27;2021-05-28 01:32:34&#x27;</span>, <span class="string">&#x27;1&#x27;</span>, <span class="string">&#x27;1&#x27;</span>, <span class="string">&#x27;0&#x27;</span>);</span><br><span class="line"><span class="keyword">INSERT INTO</span> `dish_flavor` <span class="keyword">VALUES</span> (<span class="string">&#x27;1398090003262255106&#x27;</span>, <span class="string">&#x27;1398090003228700673&#x27;</span>, <span class="string">&#x27;忌口&#x27;</span>, <span class="string">&#x27;[\&quot;不要葱\&quot;,\&quot;不要蒜\&quot;,\&quot;不要香菜\&quot;,\&quot;不要辣\&quot;]&#x27;</span>, <span class="string">&#x27;2021-05-28 01:33:27&#x27;</span>, <span class="string">&#x27;2021-05-28 01:33:27&#x27;</span>, <span class="string">&#x27;1&#x27;</span>, <span class="string">&#x27;1&#x27;</span>, <span class="string">&#x27;0&#x27;</span>);</span><br><span class="line"><span class="keyword">INSERT INTO</span> `dish_flavor` <span class="keyword">VALUES</span> (<span class="string">&#x27;1398090264554811394&#x27;</span>, <span class="string">&#x27;1398090264517062657&#x27;</span>, <span class="string">&#x27;忌口&#x27;</span>, <span class="string">&#x27;[\&quot;不要葱\&quot;,\&quot;不要蒜\&quot;,\&quot;不要香菜\&quot;,\&quot;不要辣\&quot;]&#x27;</span>, <span class="string">&#x27;2021-05-28 01:34:29&#x27;</span>, <span class="string">&#x27;2021-05-28 01:34:29&#x27;</span>, <span class="string">&#x27;1&#x27;</span>, <span class="string">&#x27;1&#x27;</span>, <span class="string">&#x27;0&#x27;</span>);</span><br><span class="line"><span class="keyword">INSERT INTO</span> `dish_flavor` <span class="keyword">VALUES</span> (<span class="string">&#x27;1398090455399837698&#x27;</span>, <span class="string">&#x27;1398090455324340225&#x27;</span>, <span class="string">&#x27;辣度&#x27;</span>, <span class="string">&#x27;[\&quot;不辣\&quot;,\&quot;微辣\&quot;,\&quot;中辣\&quot;,\&quot;重辣\&quot;]&#x27;</span>, <span class="string">&#x27;2021-05-28 01:35:14&#x27;</span>, <span class="string">&#x27;2021-05-28 01:35:14&#x27;</span>, <span class="string">&#x27;1&#x27;</span>, <span class="string">&#x27;1&#x27;</span>, <span class="string">&#x27;0&#x27;</span>);</span><br><span class="line"><span class="keyword">INSERT INTO</span> `dish_flavor` <span class="keyword">VALUES</span> (<span class="string">&#x27;1398090685449023490&#x27;</span>, <span class="string">&#x27;1398090685419663362&#x27;</span>, <span class="string">&#x27;温度&#x27;</span>, <span class="string">&#x27;[\&quot;热饮\&quot;,\&quot;常温\&quot;,\&quot;去冰\&quot;,\&quot;少冰\&quot;,\&quot;多冰\&quot;]&#x27;</span>, <span class="string">&#x27;2021-05-28 01:36:09&#x27;</span>, <span class="string">&#x27;2021-05-28 01:36:09&#x27;</span>, <span class="string">&#x27;1&#x27;</span>, <span class="string">&#x27;1&#x27;</span>, <span class="string">&#x27;0&#x27;</span>);</span><br><span class="line"><span class="keyword">INSERT INTO</span> `dish_flavor` <span class="keyword">VALUES</span> (<span class="string">&#x27;1398090825358422017&#x27;</span>, <span class="string">&#x27;1398090825329061889&#x27;</span>, <span class="string">&#x27;忌口&#x27;</span>, <span class="string">&#x27;[\&quot;不要葱\&quot;,\&quot;不要蒜\&quot;,\&quot;不要香菜\&quot;,\&quot;不要辣\&quot;]&#x27;</span>, <span class="string">&#x27;2021-05-28 01:36:43&#x27;</span>, <span class="string">&#x27;2021-05-28 01:36:43&#x27;</span>, <span class="string">&#x27;1&#x27;</span>, <span class="string">&#x27;1&#x27;</span>, <span class="string">&#x27;0&#x27;</span>);</span><br><span class="line"><span class="keyword">INSERT INTO</span> `dish_flavor` <span class="keyword">VALUES</span> (<span class="string">&#x27;1398091007051476993&#x27;</span>, <span class="string">&#x27;1398091007017922561&#x27;</span>, <span class="string">&#x27;辣度&#x27;</span>, <span class="string">&#x27;[\&quot;不辣\&quot;,\&quot;微辣\&quot;,\&quot;中辣\&quot;,\&quot;重辣\&quot;]&#x27;</span>, <span class="string">&#x27;2021-05-28 01:37:26&#x27;</span>, <span class="string">&#x27;2021-05-28 01:37:26&#x27;</span>, <span class="string">&#x27;1&#x27;</span>, <span class="string">&#x27;1&#x27;</span>, <span class="string">&#x27;0&#x27;</span>);</span><br><span class="line"><span class="keyword">INSERT INTO</span> `dish_flavor` <span class="keyword">VALUES</span> (<span class="string">&#x27;1398091296164851713&#x27;</span>, <span class="string">&#x27;1398091296131297281&#x27;</span>, <span class="string">&#x27;辣度&#x27;</span>, <span class="string">&#x27;[\&quot;不辣\&quot;,\&quot;微辣\&quot;,\&quot;中辣\&quot;,\&quot;重辣\&quot;]&#x27;</span>, <span class="string">&#x27;2021-05-28 01:38:35&#x27;</span>, <span class="string">&#x27;2021-05-28 01:38:35&#x27;</span>, <span class="string">&#x27;1&#x27;</span>, <span class="string">&#x27;1&#x27;</span>, <span class="string">&#x27;0&#x27;</span>);</span><br><span class="line"><span class="keyword">INSERT INTO</span> `dish_flavor` <span class="keyword">VALUES</span> (<span class="string">&#x27;1398091546531246081&#x27;</span>, <span class="string">&#x27;1398091546480914433&#x27;</span>, <span class="string">&#x27;忌口&#x27;</span>, <span class="string">&#x27;[\&quot;不要葱\&quot;,\&quot;不要蒜\&quot;,\&quot;不要香菜\&quot;,\&quot;不要辣\&quot;]&#x27;</span>, <span class="string">&#x27;2021-05-28 01:39:35&#x27;</span>, <span class="string">&#x27;2021-05-28 01:39:35&#x27;</span>, <span class="string">&#x27;1&#x27;</span>, <span class="string">&#x27;1&#x27;</span>, <span class="string">&#x27;0&#x27;</span>);</span><br><span class="line"><span class="keyword">INSERT INTO</span> `dish_flavor` <span class="keyword">VALUES</span> (<span class="string">&#x27;1398091729809747969&#x27;</span>, <span class="string">&#x27;1398091729788776450&#x27;</span>, <span class="string">&#x27;辣度&#x27;</span>, <span class="string">&#x27;[\&quot;不辣\&quot;,\&quot;微辣\&quot;,\&quot;中辣\&quot;,\&quot;重辣\&quot;]&#x27;</span>, <span class="string">&#x27;2021-05-28 01:40:18&#x27;</span>, <span class="string">&#x27;2021-05-28 01:40:18&#x27;</span>, <span class="string">&#x27;1&#x27;</span>, <span class="string">&#x27;1&#x27;</span>, <span class="string">&#x27;0&#x27;</span>);</span><br><span class="line"><span class="keyword">INSERT INTO</span> `dish_flavor` <span class="keyword">VALUES</span> (<span class="string">&#x27;1398091889499484161&#x27;</span>, <span class="string">&#x27;1398091889449152513&#x27;</span>, <span class="string">&#x27;辣度&#x27;</span>, <span class="string">&#x27;[\&quot;不辣\&quot;,\&quot;微辣\&quot;,\&quot;中辣\&quot;,\&quot;重辣\&quot;]&#x27;</span>, <span class="string">&#x27;2021-05-28 01:40:56&#x27;</span>, <span class="string">&#x27;2021-05-28 01:40:56&#x27;</span>, <span class="string">&#x27;1&#x27;</span>, <span class="string">&#x27;1&#x27;</span>, <span class="string">&#x27;0&#x27;</span>);</span><br><span class="line"><span class="keyword">INSERT INTO</span> `dish_flavor` <span class="keyword">VALUES</span> (<span class="string">&#x27;1398092095179763713&#x27;</span>, <span class="string">&#x27;1398092095142014978&#x27;</span>, <span class="string">&#x27;辣度&#x27;</span>, <span class="string">&#x27;[\&quot;不辣\&quot;,\&quot;微辣\&quot;,\&quot;中辣\&quot;,\&quot;重辣\&quot;]&#x27;</span>, <span class="string">&#x27;2021-05-28 01:41:45&#x27;</span>, <span class="string">&#x27;2021-05-28 01:41:45&#x27;</span>, <span class="string">&#x27;1&#x27;</span>, <span class="string">&#x27;1&#x27;</span>, <span class="string">&#x27;0&#x27;</span>);</span><br><span class="line"><span class="keyword">INSERT INTO</span> `dish_flavor` <span class="keyword">VALUES</span> (<span class="string">&#x27;1398092283877306370&#x27;</span>, <span class="string">&#x27;1398092283847946241&#x27;</span>, <span class="string">&#x27;辣度&#x27;</span>, <span class="string">&#x27;[\&quot;不辣\&quot;,\&quot;微辣\&quot;,\&quot;中辣\&quot;,\&quot;重辣\&quot;]&#x27;</span>, <span class="string">&#x27;2021-05-28 01:42:30&#x27;</span>, <span class="string">&#x27;2021-05-28 01:42:30&#x27;</span>, <span class="string">&#x27;1&#x27;</span>, <span class="string">&#x27;1&#x27;</span>, <span class="string">&#x27;0&#x27;</span>);</span><br><span class="line"><span class="keyword">INSERT INTO</span> `dish_flavor` <span class="keyword">VALUES</span> (<span class="string">&#x27;1398094018939236354&#x27;</span>, <span class="string">&#x27;1398094018893099009&#x27;</span>, <span class="string">&#x27;辣度&#x27;</span>, <span class="string">&#x27;[\&quot;不辣\&quot;,\&quot;微辣\&quot;,\&quot;中辣\&quot;,\&quot;重辣\&quot;]&#x27;</span>, <span class="string">&#x27;2021-05-28 01:49:24&#x27;</span>, <span class="string">&#x27;2021-05-28 01:49:24&#x27;</span>, <span class="string">&#x27;1&#x27;</span>, <span class="string">&#x27;1&#x27;</span>, <span class="string">&#x27;0&#x27;</span>);</span><br><span class="line"><span class="keyword">INSERT INTO</span> `dish_flavor` <span class="keyword">VALUES</span> (<span class="string">&#x27;1398094391494094850&#x27;</span>, <span class="string">&#x27;1398094391456346113&#x27;</span>, <span class="string">&#x27;辣度&#x27;</span>, <span class="string">&#x27;[\&quot;不辣\&quot;,\&quot;微辣\&quot;,\&quot;中辣\&quot;,\&quot;重辣\&quot;]&#x27;</span>, <span class="string">&#x27;2021-05-28 01:50:53&#x27;</span>, <span class="string">&#x27;2021-05-28 01:50:53&#x27;</span>, <span class="string">&#x27;1&#x27;</span>, <span class="string">&#x27;1&#x27;</span>, <span class="string">&#x27;0&#x27;</span>);</span><br><span class="line"><span class="keyword">INSERT INTO</span> `dish_flavor` <span class="keyword">VALUES</span> (<span class="string">&#x27;1399574026165727233&#x27;</span>, <span class="string">&#x27;1399305325713600514&#x27;</span>, <span class="string">&#x27;辣度&#x27;</span>, <span class="string">&#x27;[\&quot;不辣\&quot;,\&quot;微辣\&quot;,\&quot;中辣\&quot;,\&quot;重辣\&quot;]&#x27;</span>, <span class="string">&#x27;2021-06-01 03:50:25&#x27;</span>, <span class="string">&#x27;2021-06-01 03:50:25&#x27;</span>, <span class="string">&#x27;1399309715396669441&#x27;</span>, <span class="string">&#x27;1399309715396669441&#x27;</span>, <span class="string">&#x27;0&#x27;</span>);</span><br><span class="line"><span class="keyword">INSERT INTO</span> `dish_flavor` <span class="keyword">VALUES</span> (<span class="string">&#x27;1413389540592263169&#x27;</span>, <span class="string">&#x27;1413384757047271425&#x27;</span>, <span class="string">&#x27;温度&#x27;</span>, <span class="string">&#x27;[\&quot;常温\&quot;,\&quot;冷藏\&quot;]&#x27;</span>, <span class="string">&#x27;2021-07-12 09:09:16&#x27;</span>, <span class="string">&#x27;2021-07-12 09:09:16&#x27;</span>, <span class="string">&#x27;1&#x27;</span>, <span class="string">&#x27;1&#x27;</span>, <span class="string">&#x27;0&#x27;</span>);</span><br><span class="line"><span class="keyword">INSERT INTO</span> `dish_flavor` <span class="keyword">VALUES</span> (<span class="string">&#x27;1413389684020682754&#x27;</span>, <span class="string">&#x27;1413342036832100354&#x27;</span>, <span class="string">&#x27;温度&#x27;</span>, <span class="string">&#x27;[\&quot;常温\&quot;,\&quot;冷藏\&quot;]&#x27;</span>, <span class="string">&#x27;2021-07-09 15:12:18&#x27;</span>, <span class="string">&#x27;2021-07-09 15:12:18&#x27;</span>, <span class="string">&#x27;1&#x27;</span>, <span class="string">&#x27;1&#x27;</span>, <span class="string">&#x27;0&#x27;</span>);</span><br><span class="line"></span><br><span class="line"><span class="comment">-- ----------------------------</span></span><br><span class="line"><span class="comment">-- Table structure for employee</span></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> `employee`;</span><br><span class="line"><span class="keyword">CREATE TABLE</span> `employee` (</span><br><span class="line">  `id` <span class="type">bigint</span>(<span class="number">20</span>) <span class="keyword">NOT NULL</span> COMMENT <span class="string">&#x27;主键&#x27;</span>,</span><br><span class="line">  `name` <span class="type">varchar</span>(<span class="number">32</span>) <span class="keyword">COLLATE</span> utf8_bin <span class="keyword">NOT NULL</span> COMMENT <span class="string">&#x27;姓名&#x27;</span>,</span><br><span class="line">  `username` <span class="type">varchar</span>(<span class="number">32</span>) <span class="keyword">COLLATE</span> utf8_bin <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">64</span>) <span class="keyword">COLLATE</span> utf8_bin <span class="keyword">NOT NULL</span> COMMENT <span class="string">&#x27;密码&#x27;</span>,</span><br><span class="line">  `phone` <span class="type">varchar</span>(<span class="number">11</span>) <span class="keyword">COLLATE</span> utf8_bin <span class="keyword">NOT NULL</span> COMMENT <span class="string">&#x27;手机号&#x27;</span>,</span><br><span class="line">  `sex` <span class="type">varchar</span>(<span class="number">2</span>) <span class="keyword">COLLATE</span> utf8_bin <span class="keyword">NOT NULL</span> COMMENT <span class="string">&#x27;性别&#x27;</span>,</span><br><span class="line">  `id_number` <span class="type">varchar</span>(<span class="number">18</span>) <span class="keyword">COLLATE</span> utf8_bin <span class="keyword">NOT NULL</span> COMMENT <span class="string">&#x27;身份证号&#x27;</span>,</span><br><span class="line">  `status` <span class="type">int</span>(<span class="number">11</span>) <span class="keyword">NOT NULL</span> <span class="keyword">DEFAULT</span> <span class="string">&#x27;1&#x27;</span> COMMENT <span class="string">&#x27;状态 0:禁用，1:正常&#x27;</span>,</span><br><span class="line">  `create_time` datetime <span class="keyword">NOT NULL</span> COMMENT <span class="string">&#x27;创建时间&#x27;</span>,</span><br><span class="line">  `update_time` datetime <span class="keyword">NOT NULL</span> COMMENT <span class="string">&#x27;更新时间&#x27;</span>,</span><br><span class="line">  `create_user` <span class="type">bigint</span>(<span class="number">20</span>) <span class="keyword">NOT NULL</span> COMMENT <span class="string">&#x27;创建人&#x27;</span>,</span><br><span class="line">  `update_user` <span class="type">bigint</span>(<span class="number">20</span>) <span class="keyword">NOT NULL</span> COMMENT <span class="string">&#x27;修改人&#x27;</span>,</span><br><span class="line">  <span class="keyword">PRIMARY KEY</span> (`id`) <span class="keyword">USING</span> BTREE,</span><br><span class="line">  <span class="keyword">UNIQUE</span> KEY `idx_username` (`username`)</span><br><span class="line">) ENGINE<span class="operator">=</span>InnoDB <span class="keyword">DEFAULT</span> CHARSET<span class="operator">=</span>utf8 <span class="keyword">COLLATE</span><span class="operator">=</span>utf8_bin 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="comment">-- Records of employee</span></span><br><span class="line"><span class="comment">-- ----------------------------</span></span><br><span class="line"><span class="keyword">INSERT INTO</span> `employee` <span class="keyword">VALUES</span> (<span class="string">&#x27;1&#x27;</span>, <span class="string">&#x27;管理员&#x27;</span>, <span class="string">&#x27;admin&#x27;</span>, <span class="string">&#x27;e10adc3949ba59abbe56e057f20f883e&#x27;</span>, <span class="string">&#x27;13812312312&#x27;</span>, <span class="string">&#x27;1&#x27;</span>, <span class="string">&#x27;110101199001010047&#x27;</span>, <span class="string">&#x27;1&#x27;</span>, <span class="string">&#x27;2021-05-06 17:20:07&#x27;</span>, <span class="string">&#x27;2021-05-10 02:24:09&#x27;</span>, <span class="string">&#x27;1&#x27;</span>, <span class="string">&#x27;1&#x27;</span>);</span><br><span class="line"></span><br><span class="line"><span class="comment">-- ----------------------------</span></span><br><span class="line"><span class="comment">-- Table structure for orders</span></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> `orders`;</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="number">20</span>) <span class="keyword">NOT NULL</span> COMMENT <span class="string">&#x27;主键&#x27;</span>,</span><br><span class="line">  `number` <span class="type">varchar</span>(<span class="number">50</span>) <span class="keyword">COLLATE</span> utf8_bin <span class="keyword">DEFAULT</span> <span class="keyword">NULL</span> COMMENT <span class="string">&#x27;订单号&#x27;</span>,</span><br><span class="line">  `status` <span class="type">int</span>(<span class="number">11</span>) <span class="keyword">NOT NULL</span> <span class="keyword">DEFAULT</span> <span class="string">&#x27;1&#x27;</span> COMMENT <span class="string">&#x27;订单状态 1待付款，2待派送，3已派送，4已完成，5已取消&#x27;</span>,</span><br><span class="line">  `user_id` <span class="type">bigint</span>(<span class="number">20</span>) <span class="keyword">NOT NULL</span> COMMENT <span class="string">&#x27;下单用户&#x27;</span>,</span><br><span class="line">  `address_book_id` <span class="type">bigint</span>(<span class="number">20</span>) <span class="keyword">NOT NULL</span> COMMENT <span class="string">&#x27;地址id&#x27;</span>,</span><br><span class="line">  `order_time` datetime <span class="keyword">NOT NULL</span> COMMENT <span class="string">&#x27;下单时间&#x27;</span>,</span><br><span class="line">  `checkout_time` datetime <span class="keyword">NOT NULL</span> COMMENT <span class="string">&#x27;结账时间&#x27;</span>,</span><br><span class="line">  `pay_method` <span class="type">int</span>(<span class="number">11</span>) <span class="keyword">NOT NULL</span> <span class="keyword">DEFAULT</span> <span class="string">&#x27;1&#x27;</span> COMMENT <span class="string">&#x27;支付方式 1微信,2支付宝&#x27;</span>,</span><br><span class="line">  `amount` <span class="type">decimal</span>(<span class="number">10</span>,<span class="number">2</span>) <span class="keyword">NOT NULL</span> COMMENT <span class="string">&#x27;实收金额&#x27;</span>,</span><br><span class="line">  `remark` <span class="type">varchar</span>(<span class="number">100</span>) <span class="keyword">COLLATE</span> utf8_bin <span class="keyword">DEFAULT</span> <span class="keyword">NULL</span> COMMENT <span class="string">&#x27;备注&#x27;</span>,</span><br><span class="line">  `phone` <span class="type">varchar</span>(<span class="number">255</span>) <span class="keyword">COLLATE</span> utf8_bin <span class="keyword">DEFAULT</span> <span class="keyword">NULL</span>,</span><br><span class="line">  `address` <span class="type">varchar</span>(<span class="number">255</span>) <span class="keyword">COLLATE</span> utf8_bin <span class="keyword">DEFAULT</span> <span class="keyword">NULL</span>,</span><br><span class="line">  `user_name` <span class="type">varchar</span>(<span class="number">255</span>) <span class="keyword">COLLATE</span> utf8_bin <span class="keyword">DEFAULT</span> <span class="keyword">NULL</span>,</span><br><span class="line">  `consignee` <span class="type">varchar</span>(<span class="number">255</span>) <span class="keyword">COLLATE</span> utf8_bin <span class="keyword">DEFAULT</span> <span class="keyword">NULL</span>,</span><br><span class="line">  <span class="keyword">PRIMARY KEY</span> (`id`) <span class="keyword">USING</span> BTREE</span><br><span class="line">) ENGINE<span class="operator">=</span>InnoDB <span class="keyword">DEFAULT</span> CHARSET<span class="operator">=</span>utf8 <span class="keyword">COLLATE</span><span class="operator">=</span>utf8_bin 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="comment">-- Records of orders</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="comment">-- Table structure for order_detail</span></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> `order_detail`;</span><br><span class="line"><span class="keyword">CREATE TABLE</span> `order_detail` (</span><br><span class="line">  `id` <span class="type">bigint</span>(<span class="number">20</span>) <span class="keyword">NOT NULL</span> COMMENT <span class="string">&#x27;主键&#x27;</span>,</span><br><span class="line">  `name` <span class="type">varchar</span>(<span class="number">50</span>) <span class="keyword">COLLATE</span> utf8_bin <span class="keyword">DEFAULT</span> <span class="keyword">NULL</span> COMMENT <span class="string">&#x27;名字&#x27;</span>,</span><br><span class="line">  `image` <span class="type">varchar</span>(<span class="number">100</span>) <span class="keyword">COLLATE</span> utf8_bin <span class="keyword">DEFAULT</span> <span class="keyword">NULL</span> COMMENT <span class="string">&#x27;图片&#x27;</span>,</span><br><span class="line">  `order_id` <span class="type">bigint</span>(<span class="number">20</span>) <span class="keyword">NOT NULL</span> COMMENT <span class="string">&#x27;订单id&#x27;</span>,</span><br><span class="line">  `dish_id` <span class="type">bigint</span>(<span class="number">20</span>) <span class="keyword">DEFAULT</span> <span class="keyword">NULL</span> COMMENT <span class="string">&#x27;菜品id&#x27;</span>,</span><br><span class="line">  `setmeal_id` <span class="type">bigint</span>(<span class="number">20</span>) <span class="keyword">DEFAULT</span> <span class="keyword">NULL</span> COMMENT <span class="string">&#x27;套餐id&#x27;</span>,</span><br><span class="line">  `dish_flavor` <span class="type">varchar</span>(<span class="number">50</span>) <span class="keyword">COLLATE</span> utf8_bin <span class="keyword">DEFAULT</span> <span class="keyword">NULL</span> COMMENT <span class="string">&#x27;口味&#x27;</span>,</span><br><span class="line">  `number` <span class="type">int</span>(<span class="number">11</span>) <span class="keyword">NOT NULL</span> <span class="keyword">DEFAULT</span> <span class="string">&#x27;1&#x27;</span> COMMENT <span class="string">&#x27;数量&#x27;</span>,</span><br><span class="line">  `amount` <span class="type">decimal</span>(<span class="number">10</span>,<span class="number">2</span>) <span class="keyword">NOT NULL</span> COMMENT <span class="string">&#x27;金额&#x27;</span>,</span><br><span class="line">  <span class="keyword">PRIMARY KEY</span> (`id`) <span class="keyword">USING</span> BTREE</span><br><span class="line">) ENGINE<span class="operator">=</span>InnoDB <span class="keyword">DEFAULT</span> CHARSET<span class="operator">=</span>utf8 <span class="keyword">COLLATE</span><span class="operator">=</span>utf8_bin 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="comment">-- Records of order_detail</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="comment">-- Table structure for setmeal</span></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> `setmeal`;</span><br><span class="line"><span class="keyword">CREATE TABLE</span> `setmeal` (</span><br><span class="line">  `id` <span class="type">bigint</span>(<span class="number">20</span>) <span class="keyword">NOT NULL</span> COMMENT <span class="string">&#x27;主键&#x27;</span>,</span><br><span class="line">  `category_id` <span class="type">bigint</span>(<span class="number">20</span>) <span class="keyword">NOT NULL</span> COMMENT <span class="string">&#x27;菜品分类id&#x27;</span>,</span><br><span class="line">  `name` <span class="type">varchar</span>(<span class="number">64</span>) <span class="keyword">COLLATE</span> utf8_bin <span class="keyword">NOT NULL</span> COMMENT <span class="string">&#x27;套餐名称&#x27;</span>,</span><br><span class="line">  `price` <span class="type">decimal</span>(<span class="number">10</span>,<span class="number">2</span>) <span class="keyword">NOT NULL</span> COMMENT <span class="string">&#x27;套餐价格&#x27;</span>,</span><br><span class="line">  `status` <span class="type">int</span>(<span class="number">11</span>) <span class="keyword">DEFAULT</span> <span class="keyword">NULL</span> COMMENT <span class="string">&#x27;状态 0:停用 1:启用&#x27;</span>,</span><br><span class="line">  `code` <span class="type">varchar</span>(<span class="number">32</span>) <span class="keyword">COLLATE</span> utf8_bin <span class="keyword">DEFAULT</span> <span class="keyword">NULL</span> COMMENT <span class="string">&#x27;编码&#x27;</span>,</span><br><span class="line">  `description` <span class="type">varchar</span>(<span class="number">512</span>) <span class="keyword">COLLATE</span> utf8_bin <span class="keyword">DEFAULT</span> <span class="keyword">NULL</span> COMMENT <span class="string">&#x27;描述信息&#x27;</span>,</span><br><span class="line">  `image` <span class="type">varchar</span>(<span class="number">255</span>) <span class="keyword">COLLATE</span> utf8_bin <span class="keyword">DEFAULT</span> <span class="keyword">NULL</span> COMMENT <span class="string">&#x27;图片&#x27;</span>,</span><br><span class="line">  `create_time` datetime <span class="keyword">NOT NULL</span> COMMENT <span class="string">&#x27;创建时间&#x27;</span>,</span><br><span class="line">  `update_time` datetime <span class="keyword">NOT NULL</span> COMMENT <span class="string">&#x27;更新时间&#x27;</span>,</span><br><span class="line">  `create_user` <span class="type">bigint</span>(<span class="number">20</span>) <span class="keyword">NOT NULL</span> COMMENT <span class="string">&#x27;创建人&#x27;</span>,</span><br><span class="line">  `update_user` <span class="type">bigint</span>(<span class="number">20</span>) <span class="keyword">NOT NULL</span> COMMENT <span class="string">&#x27;修改人&#x27;</span>,</span><br><span class="line">  `is_deleted` <span class="type">int</span>(<span class="number">11</span>) <span class="keyword">NOT NULL</span> <span class="keyword">DEFAULT</span> <span class="string">&#x27;0&#x27;</span> COMMENT <span class="string">&#x27;是否删除&#x27;</span>,</span><br><span class="line">  <span class="keyword">PRIMARY KEY</span> (`id`) <span class="keyword">USING</span> BTREE,</span><br><span class="line">  <span class="keyword">UNIQUE</span> KEY `idx_setmeal_name` (`name`)</span><br><span class="line">) ENGINE<span class="operator">=</span>InnoDB <span class="keyword">DEFAULT</span> CHARSET<span class="operator">=</span>utf8 <span class="keyword">COLLATE</span><span class="operator">=</span>utf8_bin 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="comment">-- Records of setmeal</span></span><br><span class="line"><span class="comment">-- ----------------------------</span></span><br><span class="line"><span class="keyword">INSERT INTO</span> `setmeal` <span class="keyword">VALUES</span> (<span class="string">&#x27;1415580119015145474&#x27;</span>, <span class="string">&#x27;1413386191767674881&#x27;</span>, <span class="string">&#x27;儿童套餐A计划&#x27;</span>, <span class="string">&#x27;4000.00&#x27;</span>, <span class="string">&#x27;1&#x27;</span>, <span class="string">&#x27;&#x27;</span>, <span class="string">&#x27;&#x27;</span>, <span class="string">&#x27;61d20592-b37f-4d72-a864-07ad5bb8f3bb.jpg&#x27;</span>, <span class="string">&#x27;2021-07-15 15:52:55&#x27;</span>, <span class="string">&#x27;2021-07-15 15:52:55&#x27;</span>, <span class="string">&#x27;1415576781934608386&#x27;</span>, <span class="string">&#x27;1415576781934608386&#x27;</span>, <span class="string">&#x27;0&#x27;</span>);</span><br><span class="line"></span><br><span class="line"><span class="comment">-- ----------------------------</span></span><br><span class="line"><span class="comment">-- Table structure for setmeal_dish</span></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> `setmeal_dish`;</span><br><span class="line"><span class="keyword">CREATE TABLE</span> `setmeal_dish` (</span><br><span class="line">  `id` <span class="type">bigint</span>(<span class="number">20</span>) <span class="keyword">NOT NULL</span> COMMENT <span class="string">&#x27;主键&#x27;</span>,</span><br><span class="line">  `setmeal_id` <span class="type">varchar</span>(<span class="number">32</span>) <span class="keyword">COLLATE</span> utf8_bin <span class="keyword">NOT NULL</span> COMMENT <span class="string">&#x27;套餐id &#x27;</span>,</span><br><span class="line">  `dish_id` <span class="type">varchar</span>(<span class="number">32</span>) <span class="keyword">COLLATE</span> utf8_bin <span class="keyword">NOT NULL</span> COMMENT <span class="string">&#x27;菜品id&#x27;</span>,</span><br><span class="line">  `name` <span class="type">varchar</span>(<span class="number">32</span>) <span class="keyword">COLLATE</span> utf8_bin <span class="keyword">DEFAULT</span> <span class="keyword">NULL</span> COMMENT <span class="string">&#x27;菜品名称 （冗余字段）&#x27;</span>,</span><br><span class="line">  `price` <span class="type">decimal</span>(<span class="number">10</span>,<span class="number">2</span>) <span class="keyword">DEFAULT</span> <span class="keyword">NULL</span> COMMENT <span class="string">&#x27;菜品原价（冗余字段）&#x27;</span>,</span><br><span class="line">  `copies` <span class="type">int</span>(<span class="number">11</span>) <span class="keyword">NOT NULL</span> COMMENT <span class="string">&#x27;份数&#x27;</span>,</span><br><span class="line">  `sort` <span class="type">int</span>(<span class="number">11</span>) <span class="keyword">NOT NULL</span> <span class="keyword">DEFAULT</span> <span class="string">&#x27;0&#x27;</span> COMMENT <span class="string">&#x27;排序&#x27;</span>,</span><br><span class="line">  `create_time` datetime <span class="keyword">NOT NULL</span> COMMENT <span class="string">&#x27;创建时间&#x27;</span>,</span><br><span class="line">  `update_time` datetime <span class="keyword">NOT NULL</span> COMMENT <span class="string">&#x27;更新时间&#x27;</span>,</span><br><span class="line">  `create_user` <span class="type">bigint</span>(<span class="number">20</span>) <span class="keyword">NOT NULL</span> COMMENT <span class="string">&#x27;创建人&#x27;</span>,</span><br><span class="line">  `update_user` <span class="type">bigint</span>(<span class="number">20</span>) <span class="keyword">NOT NULL</span> COMMENT <span class="string">&#x27;修改人&#x27;</span>,</span><br><span class="line">  `is_deleted` <span class="type">int</span>(<span class="number">11</span>) <span class="keyword">NOT NULL</span> <span class="keyword">DEFAULT</span> <span class="string">&#x27;0&#x27;</span> COMMENT <span class="string">&#x27;是否删除&#x27;</span>,</span><br><span class="line">  <span class="keyword">PRIMARY KEY</span> (`id`) <span class="keyword">USING</span> BTREE</span><br><span class="line">) ENGINE<span class="operator">=</span>InnoDB <span class="keyword">DEFAULT</span> CHARSET<span class="operator">=</span>utf8 <span class="keyword">COLLATE</span><span class="operator">=</span>utf8_bin 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="comment">-- Records of setmeal_dish</span></span><br><span class="line"><span class="comment">-- ----------------------------</span></span><br><span class="line"><span class="keyword">INSERT INTO</span> `setmeal_dish` <span class="keyword">VALUES</span> (<span class="string">&#x27;1415580119052894209&#x27;</span>, <span class="string">&#x27;1415580119015145474&#x27;</span>, <span class="string">&#x27;1397862198033297410&#x27;</span>, <span class="string">&#x27;老火靓汤&#x27;</span>, <span class="string">&#x27;49800.00&#x27;</span>, <span class="string">&#x27;1&#x27;</span>, <span class="string">&#x27;0&#x27;</span>, <span class="string">&#x27;2021-07-15 15:52:55&#x27;</span>, <span class="string">&#x27;2021-07-15 15:52:55&#x27;</span>, <span class="string">&#x27;1415576781934608386&#x27;</span>, <span class="string">&#x27;1415576781934608386&#x27;</span>, <span class="string">&#x27;0&#x27;</span>);</span><br><span class="line"><span class="keyword">INSERT INTO</span> `setmeal_dish` <span class="keyword">VALUES</span> (<span class="string">&#x27;1415580119061282817&#x27;</span>, <span class="string">&#x27;1415580119015145474&#x27;</span>, <span class="string">&#x27;1413342036832100354&#x27;</span>, <span class="string">&#x27;北冰洋&#x27;</span>, <span class="string">&#x27;500.00&#x27;</span>, <span class="string">&#x27;1&#x27;</span>, <span class="string">&#x27;0&#x27;</span>, <span class="string">&#x27;2021-07-15 15:52:55&#x27;</span>, <span class="string">&#x27;2021-07-15 15:52:55&#x27;</span>, <span class="string">&#x27;1415576781934608386&#x27;</span>, <span class="string">&#x27;1415576781934608386&#x27;</span>, <span class="string">&#x27;0&#x27;</span>);</span><br><span class="line"><span class="keyword">INSERT INTO</span> `setmeal_dish` <span class="keyword">VALUES</span> (<span class="string">&#x27;1415580119069671426&#x27;</span>, <span class="string">&#x27;1415580119015145474&#x27;</span>, <span class="string">&#x27;1413385247889891330&#x27;</span>, <span class="string">&#x27;米饭&#x27;</span>, <span class="string">&#x27;200.00&#x27;</span>, <span class="string">&#x27;1&#x27;</span>, <span class="string">&#x27;0&#x27;</span>, <span class="string">&#x27;2021-07-15 15:52:55&#x27;</span>, <span class="string">&#x27;2021-07-15 15:52:55&#x27;</span>, <span class="string">&#x27;1415576781934608386&#x27;</span>, <span class="string">&#x27;1415576781934608386&#x27;</span>, <span class="string">&#x27;0&#x27;</span>);</span><br><span class="line"></span><br><span class="line"><span class="comment">-- ----------------------------</span></span><br><span class="line"><span class="comment">-- Table structure for shopping_cart</span></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> `shopping_cart`;</span><br><span class="line"><span class="keyword">CREATE TABLE</span> `shopping_cart` (</span><br><span class="line">  `id` <span class="type">bigint</span>(<span class="number">20</span>) <span class="keyword">NOT NULL</span> COMMENT <span class="string">&#x27;主键&#x27;</span>,</span><br><span class="line">  `name` <span class="type">varchar</span>(<span class="number">50</span>) <span class="keyword">COLLATE</span> utf8_bin <span class="keyword">DEFAULT</span> <span class="keyword">NULL</span> COMMENT <span class="string">&#x27;名称&#x27;</span>,</span><br><span class="line">  `image` <span class="type">varchar</span>(<span class="number">100</span>) <span class="keyword">COLLATE</span> utf8_bin <span class="keyword">DEFAULT</span> <span class="keyword">NULL</span> COMMENT <span class="string">&#x27;图片&#x27;</span>,</span><br><span class="line">  `user_id` <span class="type">bigint</span>(<span class="number">20</span>) <span class="keyword">NOT NULL</span> COMMENT <span class="string">&#x27;主键&#x27;</span>,</span><br><span class="line">  `dish_id` <span class="type">bigint</span>(<span class="number">20</span>) <span class="keyword">DEFAULT</span> <span class="keyword">NULL</span> COMMENT <span class="string">&#x27;菜品id&#x27;</span>,</span><br><span class="line">  `setmeal_id` <span class="type">bigint</span>(<span class="number">20</span>) <span class="keyword">DEFAULT</span> <span class="keyword">NULL</span> COMMENT <span class="string">&#x27;套餐id&#x27;</span>,</span><br><span class="line">  `dish_flavor` <span class="type">varchar</span>(<span class="number">50</span>) <span class="keyword">COLLATE</span> utf8_bin <span class="keyword">DEFAULT</span> <span class="keyword">NULL</span> COMMENT <span class="string">&#x27;口味&#x27;</span>,</span><br><span class="line">  `number` <span class="type">int</span>(<span class="number">11</span>) <span class="keyword">NOT NULL</span> <span class="keyword">DEFAULT</span> <span class="string">&#x27;1&#x27;</span> COMMENT <span class="string">&#x27;数量&#x27;</span>,</span><br><span class="line">  `amount` <span class="type">decimal</span>(<span class="number">10</span>,<span class="number">2</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="keyword">NULL</span> COMMENT <span class="string">&#x27;创建时间&#x27;</span>,</span><br><span class="line">  <span class="keyword">PRIMARY KEY</span> (`id`) <span class="keyword">USING</span> BTREE</span><br><span class="line">) ENGINE<span class="operator">=</span>InnoDB <span class="keyword">DEFAULT</span> CHARSET<span class="operator">=</span>utf8 <span class="keyword">COLLATE</span><span class="operator">=</span>utf8_bin 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="comment">-- Records of shopping_cart</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="comment">-- Table structure for user</span></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> `<span class="keyword">user</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="number">20</span>) <span class="keyword">NOT NULL</span> COMMENT <span class="string">&#x27;主键&#x27;</span>,</span><br><span class="line">  `name` <span class="type">varchar</span>(<span class="number">50</span>) <span class="keyword">COLLATE</span> utf8_bin <span class="keyword">DEFAULT</span> <span class="keyword">NULL</span> COMMENT <span class="string">&#x27;姓名&#x27;</span>,</span><br><span class="line">  `phone` <span class="type">varchar</span>(<span class="number">100</span>) <span class="keyword">COLLATE</span> utf8_bin <span class="keyword">NOT NULL</span> COMMENT <span class="string">&#x27;手机号&#x27;</span>,</span><br><span class="line">  `sex` <span class="type">varchar</span>(<span class="number">2</span>) <span class="keyword">COLLATE</span> utf8_bin <span class="keyword">DEFAULT</span> <span class="keyword">NULL</span> COMMENT <span class="string">&#x27;性别&#x27;</span>,</span><br><span class="line">  `id_number` <span class="type">varchar</span>(<span class="number">18</span>) <span class="keyword">COLLATE</span> utf8_bin <span class="keyword">DEFAULT</span> <span class="keyword">NULL</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">COLLATE</span> utf8_bin <span class="keyword">DEFAULT</span> <span class="keyword">NULL</span> COMMENT <span class="string">&#x27;头像&#x27;</span>,</span><br><span class="line">  `status` <span class="type">int</span>(<span class="number">11</span>) <span class="keyword">DEFAULT</span> <span class="string">&#x27;0&#x27;</span> COMMENT <span class="string">&#x27;状态 0:禁用，1:正常&#x27;</span>,</span><br><span class="line">  <span class="keyword">PRIMARY KEY</span> (`id`) <span class="keyword">USING</span> BTREE</span><br><span class="line">) ENGINE<span class="operator">=</span>InnoDB <span class="keyword">DEFAULT</span> CHARSET<span class="operator">=</span>utf8 <span class="keyword">COLLATE</span><span class="operator">=</span>utf8_bin COMMENT<span class="operator">=</span><span class="string">&#x27;用户信息&#x27;</span>;</span><br></pre></td></tr></table></figure><button type="button" class="tab-to-top" aria-label="scroll to top"><i class="fas fa-arrow-up"></i></button></div><div class="tab-item-content" id="准备工作-建表-2"><div class="img-wrap"><div class="img-bg"><img class="img" src="https://raw.githubusercontent.com/silvan2077/markdown_pic/main/Picgo/20251031125951.png"/></div></div><button type="button" class="tab-to-top" aria-label="scroll to top"><i class="fas fa-arrow-up"></i></button></div></div></div></li><li><p>创建一个SpringBoot项目，勾选上SpringWeb，Lombok，MySQL，然后在<code>pom.xml</code>中导入<code>druid</code>和<code>MyBatisPlus</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></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-spring-boot3-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.11<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>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-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>1.2.6<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></li></ul><p>导入黑马提供的前端资源，放在<code>resources/static</code>目录下<br>如果直接放在<code>resources</code>目录下，会无法查找到，此时需要配置一下资源映射<br><div class="note warning no-icon flat"><p><strong>注：</strong> 要么放到static目录下要么配置资源映射，执行其一即可，不可以同时配置(从后面的结果来看，此处推荐配置资源映射)</p></div></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="meta">@Configuration</span></span><br><span class="line"><span class="meta">@Slf4j</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">WebMvcConfig</span> <span class="keyword">extends</span> <span class="title class_">WebMvcConfigurationSupport</span> &#123;</span><br><span class="line">    <span class="comment">// 注：如果放到static目录下则不需要该步骤</span></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_">addResourceHandlers</span><span class="params">(ResourceHandlerRegistry registry)</span> &#123;</span><br><span class="line">        log.info(<span class="string">&quot;开始进行静态资源映射...&quot;</span>);</span><br><span class="line">        registry.addResourceHandler(<span class="string">&quot;/backend/**&quot;</span>).addResourceLocations(<span class="string">&quot;classpath:/backend/&quot;</span>);</span><br><span class="line">        registry.addResourceHandler(<span class="string">&quot;/front/**&quot;</span>).addResourceLocations(<span class="string">&quot;classpath:/front/&quot;</span>);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>然后配置一下端口号和数据库连接四要素就能访问静态页面了,这里别忘了更改数据库的名称和密码</p><figure class="highlight yml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><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="attr">server:</span></span><br><span class="line">  <span class="attr">port:</span> <span class="number">8080</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/reggie?serverTimezone=Asia</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">password</span></span><br><span class="line">    <span class="attr">type:</span> <span class="string">com.alibaba.druid.pool.DruidDataSource</span></span><br><span class="line">    </span><br><span class="line">    </span><br><span class="line"><span class="attr">mybatis-plus:</span></span><br><span class="line">  <span class="attr">configuration:</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="attr">log-impl:</span> <span class="string">org.apache.ibatis.logging.stdout.StdOutImpl</span></span><br><span class="line">  <span class="attr">global-config:</span></span><br><span class="line">    <span class="attr">db-config:</span></span><br><span class="line">      <span class="attr">id-type:</span> <span class="string">ASSIGN_ID</span></span><br><span class="line"></span><br><span class="line"></span><br></pre></td></tr></table></figure><p>打开浏览器，访问 <a href="http://localhost:8080/backend/page/login/login.html">http://localhost:8080/backend/page/login/login.html</a> 就可以看到登录页面了，不过此时还无法登录</p><div class="img-wrap"><div class="img-bg"><img class="img" src="https://raw.githubusercontent.com/silvan2077/markdown_pic/main/Picgo/20251018130255.png"/></div></div><h2 id="后台系统登录功能"><a href="#后台系统登录功能" class="headerlink" title="后台系统登录功能"></a>后台系统登录功能</h2><div class="note info no-icon flat"><p>数据库的数据和简单的SQL语句都不用我们管，数据已经提供好了，简单的SQL语句直接使用MyBatisPlus，所以需要自己编写的SQL语句非常少。</p></div><p>在准备工作已经将所有前端页面放在了<code>resources/static</code>目录下，可以直接在<code>resource/static/backend/page/login.html</code>看到登录页面的代码</p><h3 id="创建实体类"><a href="#创建实体类" class="headerlink" title="创建实体类"></a>创建实体类</h3><p>登录功能比较简单，只需要输入账号密码后点击登录就应当能登陆成功<br><div class="img-wrap"><div class="img-bg"><img class="img" src="https://raw.githubusercontent.com/silvan2077/markdown_pic/main/Picgo/20251018131600.png"/></div></div><br>此时使用驼峰命名法的属性名，可以通过在配置文件中设置的<code>map-underscore-to-camel-case</code>属性，将数据库中表名和字段名中的下划线去掉，按照驼峰命名法映射(<strong>现在MybatisPlus默认开启</strong>)<br><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><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="meta">@Data</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">Employee</span> <span class="keyword">implements</span> <span class="title class_">Serializable</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> <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> 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="keyword">private</span> String name;</span><br><span class="line"></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> String phone;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">private</span> String sex;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">private</span> String idNumber;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">private</span> Integer status;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">private</span> LocalDateTime createTime;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">private</span> LocalDateTime updateTime;</span><br><span class="line"></span><br><span class="line">    <span class="comment">//这两个注解是MyBatisPlus提供的，用于实现实体类字段的自动填充（Auto-Fill）功能。</span></span><br><span class="line">    <span class="meta">@TableField(fill = FieldFill.INSERT)</span></span><br><span class="line">    <span class="keyword">private</span> Long createUser;</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> Long updateUser;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></p><h3 id="创建Mapper、Service、Controller"><a href="#创建Mapper、Service、Controller" class="headerlink" title="创建Mapper、Service、Controller"></a>创建Mapper、Service、Controller</h3><div class="tabs" id="创建对应的mapper和service"><ul class="nav-tabs"><li class="tab active"><button type="button" data-href="#创建对应的mapper和service-1">EmployeeMapper</button></li><li class="tab"><button type="button" data-href="#创建对应的mapper和service-2">EmployeeService</button></li><li class="tab"><button type="button" data-href="#创建对应的mapper和service-3">EmployeeServiceImpl</button></li><li class="tab"><button type="button" data-href="#创建对应的mapper和service-4">EmployeeController</button></li></ul><div class="tab-contents"><div class="tab-item-content active" id="创建对应的mapper和service-1"><p>直接继承<code>BaseMapper</code>就行了，别忘了<code>@Mapper</code>注解<br><figure class="highlight java"><table><tr><td class="gutter"><pre><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="meta">@Mapper</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">interface</span> <span class="title class_">EmployeeMapper</span> <span class="keyword">extends</span> <span class="title class_">BaseMapper</span>&lt;Employee&gt; &#123;</span><br><span class="line"></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></p><button type="button" class="tab-to-top" aria-label="scroll to top"><i class="fas fa-arrow-up"></i></button></div><div class="tab-item-content" id="创建对应的mapper和service-2"><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="keyword">public</span> <span class="keyword">interface</span> <span class="title class_">EmployeeService</span> <span class="keyword">extends</span> <span class="title class_">IService</span>&lt;Employee&gt; &#123;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><button type="button" class="tab-to-top" aria-label="scroll to top"><i class="fas fa-arrow-up"></i></button></div><div class="tab-item-content" id="创建对应的mapper和service-3"><p>继承ServiceImpl，实现<code>EmployeeService</code>接口，别忘了<code>@Service</code>注解<br><figure class="highlight java"><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="meta">@Service</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">EmployeeServiceImpl</span> <span class="keyword">extends</span> <span class="title class_">ServiceImpl</span>&lt;EmployeeMapper, Employee&gt; <span class="keyword">implements</span> <span class="title class_">EmployeeService</span> &#123;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></p><button type="button" class="tab-to-top" aria-label="scroll to top"><i class="fas fa-arrow-up"></i></button></div><div class="tab-item-content" id="创建对应的mapper和service-4"><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><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">@Slf4j</span></span><br><span class="line"><span class="meta">@RestController</span></span><br><span class="line"><span class="meta">@RequestMapping(&quot;/employee&quot;)</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">EmployeeController</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> EmployeeService employeeService;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><button type="button" class="tab-to-top" aria-label="scroll to top"><i class="fas fa-arrow-up"></i></button></div></div></div><h3 id="编写Controller"><a href="#编写Controller" class="headerlink" title="编写Controller"></a>编写Controller</h3><div class="note info no-icon flat"><p>登录处理逻辑：</p><ol><li>接受页面提交的密码并对其进行加密处理</li><li>根据页面提交的用户名查询数据库</li><li>如果没有查询到就返回登录失败</li><li>查询到，则对比密码是否一致，不一致返回失败</li><li>查看员工状态，如果状态为已禁用则返回已禁用的结果</li><li>登录成功，将员工Id存入Session并返回登陆成功</li></ol></div><p>给EmployeeController层添加<code>login</code>方法</p><ul><li><code>@RequestBody</code> 用于接收前端传递给后端的json字符串（请求体中的数据）</li><li><code>HttpServletRequest</code> 作用：如果登录成功，将员工对应的id存到session一份，这样浏览器可以获得登录员工的数据</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="comment">//通过表单提交，所以发送post请求</span></span><br><span class="line"><span class="meta">@PostMapping(&quot;/login&quot;)</span></span><br><span class="line"><span class="keyword">public</span> Result&lt;Employee&gt; <span class="title function_">login</span><span class="params">(HttpServletRequest request, <span class="meta">@RequestBody</span> Employee employee)</span> &#123;</span><br><span class="line">    <span class="type">String</span> <span class="variable">password</span> <span class="operator">=</span> employee.getPassword();</span><br><span class="line">    password = DigestUtils.md5DigestAsHex(password.getBytes());</span><br><span class="line">    <span class="comment">//这部分就是MP</span></span><br><span class="line">    LambdaQueryWrapper&lt;Employee&gt; lqw = <span class="keyword">new</span> <span class="title class_">LambdaQueryWrapper</span>&lt;&gt;();</span><br><span class="line">    lqw.eq(Employee::getUsername, employee.getUsername());</span><br><span class="line">    <span class="type">Employee</span> <span class="variable">emp</span> <span class="operator">=</span> employeeService.getOne(lqw);</span><br><span class="line">    <span class="keyword">if</span> (emp == <span class="literal">null</span>) &#123;</span><br><span class="line">        <span class="keyword">return</span> Result.error(<span class="string">&quot;登陆失败&quot;</span>);</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">if</span> (!emp.getPassword().equals(password)) &#123;</span><br><span class="line">        <span class="keyword">return</span> Result.error(<span class="string">&quot;登录失败&quot;</span>);</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">if</span> (emp.getStatus() == <span class="number">0</span>) &#123;</span><br><span class="line">        <span class="keyword">return</span> Result.error(<span class="string">&quot;该用户已被禁用&quot;</span>);</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="comment">//存个Session，只存个id就行了</span></span><br><span class="line">    request.getSession().setAttribute(<span class="string">&quot;employee&quot;</span>,emp.getId());</span><br><span class="line">    <span class="keyword">return</span> Result.success(emp);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h3 id="统一结果封装"><a href="#统一结果封装" class="headerlink" title="统一结果封装"></a>统一结果封装</h3><p>为了方便前端接收数据，编写一个Result结果类，统一返回数据的格式</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="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_">Result</span>&lt;T&gt; &#123;</span><br><span class="line">    <span class="keyword">private</span> Integer code;  <span class="comment">// 编码：1成功。0和其他数字失败</span></span><br><span class="line">    <span class="keyword">private</span> String errMsg;  <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 class="keyword">private</span> <span class="type">Map</span> <span class="variable">map</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">HashMap</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> &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; r = <span class="keyword">new</span> <span class="title class_">Result</span>&lt;&gt;();</span><br><span class="line">        r.code = <span class="number">1</span>;  <span class="comment">//成功状态码</span></span><br><span class="line">        r.data = data;</span><br><span class="line">        <span class="keyword">return</span> r;</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> &lt;T&gt; Result&lt;T&gt; <span class="title function_">error</span><span class="params">(String errMsg)</span> &#123;</span><br><span class="line">        Result&lt;T&gt; r = <span class="keyword">new</span> <span class="title class_">Result</span>&lt;&gt;();</span><br><span class="line">        r.errMsg = errMsg; <span class="comment">//设置错误信息</span></span><br><span class="line">        r.code = <span class="number">0</span>;  <span class="comment">//默认失败状态码，后期我们可以根据自己的需求来设置其他状态码</span></span><br><span class="line">        <span class="keyword">return</span> r;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">public</span> Result&lt;T&gt; <span class="title function_">add</span><span class="params">(String msg, String value)</span> &#123;</span><br><span class="line">        <span class="built_in">this</span>.map.put(msg, value);</span><br><span class="line">        <span class="keyword">return</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="登出功能"><a href="#登出功能" class="headerlink" title="登出功能"></a>登出功能</h3><div class="note info no-icon flat"><p>登出处理逻辑：</p><ol><li>清理Session中保存的当前登录员工的id</li><li>返回结果</li></ol></div><p>在Controller层添加<code>Logout</code>方法<br><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><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 class="doctag">@param</span> request</span></span><br><span class="line"><span class="comment">    * <span class="doctag">@return</span></span></span><br><span class="line"><span class="comment">    */</span></span><br><span class="line"><span class="meta">@PostMapping(&quot;/logout&quot;)</span></span><br><span class="line"><span class="keyword">public</span> Result&lt;String&gt; <span class="title function_">logout</span><span class="params">(HttpServletRequest request)</span> &#123;</span><br><span class="line">    request.getSession().removeAttribute(<span class="string">&quot;employee&quot;</span>);</span><br><span class="line">    <span class="keyword">return</span> Result.success(<span class="string">&quot;退出成功&quot;</span>);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></p><p>登出的功能在index页面，右上角有一个按钮，点击就能登出</p><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></pre></td><td class="code"><pre><span class="line"><span class="tag">&lt;<span class="name">div</span> <span class="attr">class</span>=<span class="string">&quot;right-menu&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">div</span> <span class="attr">class</span>=<span class="string">&quot;avatar-wrapper&quot;</span>&gt;</span>&#123;&#123; userInfo.name &#125;&#125;<span class="tag">&lt;/<span class="name">div</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">img</span> <span class="attr">src</span>=<span class="string">&quot;images/icons/btn_close@2x.png&quot;</span> <span class="attr">class</span>=<span class="string">&quot;outLogin&quot;</span> <span class="attr">alt</span>=<span class="string">&quot;退出&quot;</span> @<span class="attr">click</span>=<span class="string">&quot;logout&quot;</span> /&gt;</span></span><br><span class="line"><span class="tag">&lt;/<span class="name">div</span>&gt;</span></span><br></pre></td></tr></table></figure><p>对应的函数如下，这里的logoutApi用来发送post请求</p><div class="tabs" id="登出"><ul class="nav-tabs"><li class="tab active"><button type="button" data-href="#登出-1">logout</button></li><li class="tab"><button type="button" data-href="#登出-2">logoutApi</button></li></ul><div class="tab-contents"><div class="tab-item-content active" id="登出-1"><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><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="title function_">logout</span>(<span class="params"></span>) &#123;</span><br><span class="line">    <span class="title function_">logoutApi</span>().<span class="title function_">then</span>(<span class="function">(<span class="params">res</span>)=&gt;</span>&#123;</span><br><span class="line">        <span class="keyword">if</span>(res.<span class="property">code</span> === <span class="number">1</span>)&#123;</span><br><span class="line">        <span class="variable language_">localStorage</span>.<span class="title function_">removeItem</span>(<span class="string">&#x27;userInfo&#x27;</span>)</span><br><span class="line">        <span class="variable language_">window</span>.<span class="property">location</span>.<span class="property">href</span> = <span class="string">&#x27;/backend/page/login/login.html&#x27;</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><button type="button" class="tab-to-top" aria-label="scroll to top"><i class="fas fa-arrow-up"></i></button></div><div class="tab-item-content" id="登出-2"><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><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="keyword">function</span> <span class="title function_">logoutApi</span>(<span class="params"></span>)&#123;</span><br><span class="line">  <span class="keyword">return</span> $axios(&#123;</span><br><span class="line">    <span class="string">&#x27;url&#x27;</span>: <span class="string">&#x27;/employee/logout&#x27;</span>,</span><br><span class="line">    <span class="string">&#x27;method&#x27;</span>: <span class="string">&#x27;post&#x27;</span>,</span><br><span class="line">  &#125;)</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><button type="button" class="tab-to-top" aria-label="scroll to top"><i class="fas fa-arrow-up"></i></button></div></div></div><h3 id="登录测试"><a href="#登录测试" class="headerlink" title="登录测试"></a>登录测试</h3><p>数据库中目前只有一条用户信息，username为<code>admin</code>，password为<code>123456</code>（已经经过MD5加密了）<br>现在访问 <a href="http://localhost/backend/page/login/login.html">http://localhost/backend/page/login/login.html</a><br>当输入正确的用户名和密码时，可以跳转至<code>http://localhost/backend/index.html</code>页面<br>当输入错误的用户名或密码，会显示错误信息<br><div class="tabs" id="后台登录测试"><ul class="nav-tabs"><li class="tab active"><button type="button" data-href="#后台登录测试-1">登陆成功</button></li><li class="tab"><button type="button" data-href="#后台登录测试-2">登陆失败</button></li></ul><div class="tab-contents"><div class="tab-item-content active" id="后台登录测试-1"><div class="img-wrap"><div class="img-bg"><img class="img" src="https://raw.githubusercontent.com/silvan2077/markdown_pic/main/Picgo/20251031153103.png"/></div></div><button type="button" class="tab-to-top" aria-label="scroll to top"><i class="fas fa-arrow-up"></i></button></div><div class="tab-item-content" id="后台登录测试-2"><div class="img-wrap"><div class="img-bg"><img class="img" src="https://raw.githubusercontent.com/silvan2077/markdown_pic/main/Picgo/20251031153201.png"/></div></div><button type="button" class="tab-to-top" aria-label="scroll to top"><i class="fas fa-arrow-up"></i></button></div></div></div><br>对应的HTML代码如下</p><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><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="attr">methods</span>: &#123;</span><br><span class="line">    <span class="keyword">async</span> <span class="title function_">handleLogin</span>(<span class="params"></span>) &#123;</span><br><span class="line">        <span class="variable language_">this</span>.<span class="property">$refs</span>.<span class="property">loginForm</span>.<span class="title function_">validate</span>(<span class="title function_">async</span> (valid) =&gt; &#123;</span><br><span class="line">        <span class="keyword">if</span> (valid) &#123;</span><br><span class="line">            <span class="variable language_">this</span>.<span class="property">loading</span> = <span class="literal">true</span></span><br><span class="line">            <span class="keyword">let</span> res = <span class="keyword">await</span> <span class="title function_">loginApi</span>(<span class="variable language_">this</span>.<span class="property">loginForm</span>)</span><br><span class="line">            <span class="keyword">if</span> (<span class="title class_">String</span>(res.<span class="property">code</span>) === <span class="string">&#x27;1&#x27;</span>) &#123;</span><br><span class="line">            <span class="variable language_">localStorage</span>.<span class="title function_">setItem</span>(<span class="string">&#x27;userInfo&#x27;</span>,<span class="title class_">JSON</span>.<span class="title function_">stringify</span>(res.<span class="property">data</span>))</span><br><span class="line">            <span class="variable language_">window</span>.<span class="property">location</span>.<span class="property">href</span>= <span class="string">&#x27;/backend/index.html&#x27;</span></span><br><span class="line">            &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">            <span class="variable language_">this</span>.<span class="property">$message</span>.<span class="title function_">error</span>(res.<span class="property">msg</span>)</span><br><span class="line">            <span class="variable language_">this</span>.<span class="property">loading</span> = <span class="literal">false</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">&#125;</span><br></pre></td></tr></table></figure><p>对应的JS代码如下</p><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><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">function</span> <span class="title function_">loginApi</span>(<span class="params">data</span>) &#123;</span><br><span class="line">  <span class="keyword">return</span> $axios(&#123;</span><br><span class="line">    <span class="string">&#x27;url&#x27;</span>: <span class="string">&#x27;/employee/login&#x27;</span>,</span><br><span class="line">    <span class="string">&#x27;method&#x27;</span>: <span class="string">&#x27;post&#x27;</span>,</span><br><span class="line">    data</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">function</span> <span class="title function_">logoutApi</span>(<span class="params"></span>)&#123;</span><br><span class="line">  <span class="keyword">return</span> $axios(&#123;</span><br><span class="line">    <span class="string">&#x27;url&#x27;</span>: <span class="string">&#x27;/employee/logout&#x27;</span>,</span><br><span class="line">    <span class="string">&#x27;method&#x27;</span>: <span class="string">&#x27;post&#x27;</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="tabs" id=""><ul class="nav-tabs"></ul><div class="tab-contents"></div></div><h4 id="使用过滤器来拦截请求"><a href="#使用过滤器来拦截请求" class="headerlink" title="使用过滤器来拦截请求"></a>使用过滤器来拦截请求</h4><div class="note info no-icon flat"><p>实现步骤：</p><ol><li>创建自定义过滤器LoginCheckFilter</li><li>在启动类上添加<code>@ServletComponentScan</code>注解，添加该注解才会对WebServlet注解的类进行扫描</li><li>完善过滤器的处理逻辑</li></ol></div><ul><li>创建过滤器,设置所有请求全部拦截<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><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="meta">@Slf4j</span></span><br><span class="line"><span class="meta">@WebFilter(filterName = &quot;loginCheckFilter&quot;, urlPatterns = &quot;/*&quot;)</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">LoginCheckFilter</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 servletRequest, ServletResponse servletResponse, FilterChain filterChain)</span> <span class="keyword">throws</span> IOException, ServletException &#123;</span><br><span class="line">        <span class="type">HttpServletRequest</span> <span class="variable">request</span> <span class="operator">=</span> (HttpServletRequest) servletRequest;</span><br><span class="line">        <span class="type">HttpServletResponse</span> <span class="variable">response</span> <span class="operator">=</span> (HttpServletResponse) servletResponse;</span><br><span class="line">        <span class="comment">//将拦截到的URI输出到日志，这是新的输出方式，&#123;&#125;是占位符，将自动填充request.getRequestURI()的内容</span></span><br><span class="line">        log.info(<span class="string">&quot;拦截到的URI：&#123;&#125;&quot;</span>, request.getRequestURI());</span><br><span class="line">        filterChain.doFilter(request, response);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></li></ul><p>并在启动类上加入注解<code>@ServletComponentScan</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></pre></td><td class="code"><pre><span class="line"><span class="meta">@SpringBootApplication</span></span><br><span class="line"><span class="meta">@ServletComponentScan</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">ReggieApplication</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(ReggieApplication.class, args);</span><br><span class="line">&#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>启动服务器，访问index页面，查看日志，现在可以拦截到URI了</p><blockquote><p>2022-09-29 18:05:53.190 …… : 拦截到的URI：/backend/index.html<br>2022-09-29 18:06:01.174 …… : 拦截到的URI：/employee/page</p></blockquote><h4 id="编写Filter处理逻辑"><a href="#编写Filter处理逻辑" class="headerlink" title="编写Filter处理逻辑"></a>编写Filter处理逻辑</h4><div class="note info no-icon flat"><p>过滤器具体处理逻辑：</p><ol><li>获取本次请求的URI</li><li>判断该请求是否需要处理</li><li>不需要处理则直接放行</li><li>判断登录状态，如果已登录则放行</li><li>未登录返回未登录结果</li></ol></div><ol><li><p>获取本次请求的URI</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">//获取本次请求的URI</span></span><br><span class="line"><span class="type">String</span> <span class="variable">uri</span> <span class="operator">=</span> request.getRequestURI();</span><br><span class="line"><span class="comment">//定义不需要被拦截的请求</span></span><br><span class="line">String[] urls = <span class="keyword">new</span> <span class="title class_">String</span>[]&#123;</span><br><span class="line">        <span class="string">&quot;/employee/login.html&quot;</span>,</span><br><span class="line">        <span class="string">&quot;/employee/logout.html&quot;</span>,</span><br><span class="line">        <span class="string">&quot;/backend/**&quot;</span>,</span><br><span class="line">        <span class="string">&quot;/front/**&quot;</span></span><br><span class="line">&#125;;</span><br></pre></td></tr></table></figure></li><li><p>判断本次请求是否需要处理<br>使用Spring 的<code>PathMatcher</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></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">final</span> <span class="type">AntPathMatcher</span> <span class="variable">PATH_MATCHER</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">AntPathMatcher</span>();</span><br><span class="line"></span><br><span class="line"><span class="keyword">private</span> <span class="type">boolean</span> <span class="title function_">check</span><span class="params">(String[] urls, String uri)</span> &#123;</span><br><span class="line">    <span class="keyword">for</span> (String url : urls) &#123;</span><br><span class="line">        <span class="type">boolean</span> <span class="variable">match</span> <span class="operator">=</span> PATH_MATCHER.match(url, uri);</span><br><span class="line">        <span class="keyword">if</span> (match)</span><br><span class="line">            <span class="keyword">return</span> <span class="literal">true</span>;</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><br></pre></td></tr></table></figure></li></ol><ol><li>如果不需要处理，则直接放行<figure class="highlight java"><table><tr><td class="gutter"><pre><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="keyword">if</span> (check) &#123;</span><br><span class="line">    filterChain.doFilter(request, response);</span><br><span class="line">    <span class="keyword">return</span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></li></ol><ol><li><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></pre></td><td class="code"><pre><span class="line"><span class="comment">//我们当初存的session是employee，所以这里就拿它判断</span></span><br><span class="line"><span class="keyword">if</span> (request.getSession().getAttribute(<span class="string">&quot;employee&quot;</span>) != <span class="literal">null</span>) &#123;</span><br><span class="line">    filterChain.doFilter(request,response);</span><br><span class="line">    <span class="keyword">return</span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></li><li><p>如果未登录则返回未登录结果</p></li></ol><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="comment">//未登录状态为什么要返回一个error呢？而且msg为NOTLOGIN</span></span><br><span class="line">response.getWriter().write(JSON.toJSONString(Result.error(<span class="string">&quot;NOTLOGIN&quot;</span>)));</span><br></pre></td></tr></table></figure><p>从JS代码可以看出，当符合未登录状态的条件时，会自动重定向到登录页面</p><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><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">service.<span class="property">interceptors</span>.<span class="property">response</span>.<span class="title function_">use</span>(<span class="function"><span class="params">res</span> =&gt;</span> &#123;</span><br><span class="line">    <span class="keyword">if</span> (res.<span class="property">data</span>.<span class="property">code</span> === <span class="number">0</span> &amp;&amp; res.<span class="property">data</span>.<span class="property">msg</span> === <span class="string">&#x27;NOTLOGIN&#x27;</span>) &#123;<span class="comment">// 返回登录页面</span></span><br><span class="line">    <span class="variable language_">console</span>.<span class="title function_">log</span>(<span class="string">&#x27;---/backend/page/login/login.html---&#x27;</span>)</span><br><span class="line">    <span class="variable language_">localStorage</span>.<span class="title function_">removeItem</span>(<span class="string">&#x27;userInfo&#x27;</span>)</span><br><span class="line">    <span class="variable language_">window</span>.<span class="property">top</span>.<span class="property">location</span>.<span class="property">href</span> = <span class="string">&#x27;/backend/page/login/login.html&#x27;</span></span><br><span class="line">    &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">    <span class="keyword">return</span> res.<span class="property">data</span></span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>这里需要导入fastjson的坐标<br><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.fastjson2<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>fastjson2<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.59<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><ol><li><p>完整代码</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><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="meta">@WebFilter(filterName = &quot;loginCheckFilter&quot;,urlPatterns = &quot;/*&quot;)</span></span><br><span class="line"><span class="meta">@Slf4j</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">LoginCheckFilter</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="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">AntPathMatcher</span> <span class="variable">PATH_MATCHER</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">AntPathMatcher</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_">doFilter</span><span class="params">(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain)</span> <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">      <span class="type">HttpServletRequest</span> <span class="variable">request</span> <span class="operator">=</span> (HttpServletRequest) servletRequest;</span><br><span class="line">      <span class="type">HttpServletResponse</span> <span class="variable">response</span> <span class="operator">=</span> (HttpServletResponse) servletResponse;</span><br><span class="line"></span><br><span class="line">      <span class="comment">//1.获取本次请求的URI</span></span><br><span class="line">      <span class="type">String</span> <span class="variable">requestURI</span> <span class="operator">=</span> request.getRequestURI();</span><br><span class="line">      log.info(<span class="string">&quot;拦截到请求：&#123;&#125;&quot;</span>,requestURI);</span><br><span class="line"></span><br><span class="line">      <span class="comment">//定义不需要处理的请求</span></span><br><span class="line">      String[] urls = <span class="keyword">new</span> <span class="title class_">String</span>[]&#123;</span><br><span class="line">              <span class="string">&quot;/employee/login&quot;</span>,</span><br><span class="line">              <span class="string">&quot;/employee/logout&quot;</span>,</span><br><span class="line">              <span class="string">&quot;/backend/**&quot;</span>,</span><br><span class="line">              <span class="string">&quot;/front/**&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="type">boolean</span> <span class="variable">check</span> <span class="operator">=</span> check(urls, requestURI);</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> (check) &#123;</span><br><span class="line">          log.info(<span class="string">&quot;本次请求：&#123;&#125;，不需要处理&quot;</span>,requestURI);</span><br><span class="line">          filterChain.doFilter(request,response);</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="comment">//4.判断登录状态，如果已登录，则直接放行</span></span><br><span class="line">      <span class="keyword">if</span> (request.getSession().getAttribute(<span class="string">&quot;employee&quot;</span>) != <span class="literal">null</span>) &#123;</span><br><span class="line">          log.info(<span class="string">&quot;用户已登录，id为&#123;&#125;&quot;</span>,request.getSession().getAttribute(<span class="string">&quot;employee&quot;</span>));</span><br><span class="line">          filterChain.doFilter(request,response);</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="comment">//5.如果未登录则返回未登录结果,通过输出流方式向客户端页面响应数据</span></span><br><span class="line">      log.info(<span class="string">&quot;用户未登录&quot;</span>);</span><br><span class="line">      log.info(<span class="string">&quot;用户id&#123;&#125;&quot;</span>,request.getSession().getAttribute(<span class="string">&quot;employee&quot;</span>));</span><br><span class="line">      response.getWriter().write(JSON.toJSONString(Result.error(<span class="string">&quot;NOTLOGIN&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="keyword">public</span> <span class="type">boolean</span> <span class="title function_">check</span><span class="params">(String[] urls, String requestURI)</span>&#123;</span><br><span class="line">      <span class="keyword">for</span> (String url : urls) &#123;</span><br><span class="line">          <span class="type">boolean</span> <span class="variable">match</span> <span class="operator">=</span> PATH_MATCHER.match(url, requestURI);</span><br><span class="line">          <span class="keyword">if</span> (match) &#123;</span><br><span class="line">              <span class="comment">//匹配</span></span><br><span class="line">              <span class="keyword">return</span> <span class="literal">true</span>;</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">      <span class="keyword">return</span> <span class="literal">false</span>;</span><br><span class="line">  &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></li></ol><h4 id="测试登录"><a href="#测试登录" class="headerlink" title="测试登录"></a>测试登录</h4><p>当我们直接访问 <a href="http://localhost/backend/index.html">http://localhost/backend/index.html</a> 时，可以发现已经自动跳转到登录页面并输出以下日志：</p><div class="img-wrap"><div class="img-bg"><img class="img" src="https://raw.githubusercontent.com/silvan2077/markdown_pic/main/Picgo/20251031162803.png"/></div></div><h2 id="添加员工"><a href="#添加员工" class="headerlink" title="添加员工"></a>添加员工</h2><h3 id="流程分析"><a href="#流程分析" class="headerlink" title="流程分析"></a>流程分析</h3><div class="note info no-icon flat"><p>执行流程：</p><ol><li>页面发送ajax请求，将新增员工页面中输入的数据以json的形式提交到服务端</li><li>服务端Controller接收页面提交的数据并调用Service将数据进行保存</li><li>Service调用Mapper操作数据库，保存数据</li></ol></div><div class="img-wrap"><div class="img-bg"><img class="img" src="https://raw.githubusercontent.com/silvan2077/markdown_pic/main/Picgo/20251031164137.png"/></div></div><p>简单看一下前端的代码，当点击添加员工按钮时，就会调用<code>addEmployee</code>方法，跳转到发送post请求，此时的界面信息，数据模型绑定的是<code>ruleForm</code>，通过双向绑定来接受用户的信息，输入信息后保存并添加按钮绑定了<code>submitForm</code>函数，调用相关方法并返回操作信息<br><div class="tabs" id="新增员工流程分析"><ul class="nav-tabs"><li class="tab active"><button type="button" data-href="#新增员工流程分析-1">HTML</button></li><li class="tab"><button type="button" data-href="#新增员工流程分析-2">数据模型</button></li><li class="tab"><button type="button" data-href="#新增员工流程分析-3">submitForm函数</button></li><li class="tab"><button type="button" data-href="#新增员工流程分析-4">addEmployee函数</button></li></ul><div class="tab-contents"><div class="tab-item-content active" id="新增员工流程分析-1"><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><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="tag">&lt;<span class="name">el-form</span></span></span><br><span class="line"><span class="tag">    <span class="attr">ref</span>=<span class="string">&quot;ruleForm&quot;</span></span></span><br><span class="line"><span class="tag">    <span class="attr">:model</span>=<span class="string">&quot;ruleForm&quot;</span></span></span><br><span class="line"><span class="tag">    <span class="attr">:rules</span>=<span class="string">&quot;rules&quot;</span></span></span><br><span class="line"><span class="tag">    <span class="attr">:inline</span>=<span class="string">&quot;false&quot;</span></span></span><br><span class="line"><span class="tag">    <span class="attr">label-width</span>=<span class="string">&quot;180px&quot;</span></span></span><br><span class="line"><span class="tag">    <span class="attr">class</span>=<span class="string">&quot;demo-ruleForm&quot;</span></span></span><br><span class="line"><span class="tag">&gt;</span></span><br><span class="line"><span class="tag">&lt;<span class="name">el-form-item</span> <span class="attr">label</span>=<span class="string">&quot;账号:&quot;</span> <span class="attr">prop</span>=<span class="string">&quot;username&quot;</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">el-input</span> <span class="attr">v-model</span>=<span class="string">&quot;ruleForm.username&quot;</span> <span class="attr">placeholder</span>=<span class="string">&quot;请输入账号&quot;</span> <span class="attr">maxlength</span>=<span class="string">&quot;20&quot;</span>/&gt;</span></span><br><span class="line"><span class="tag">&lt;/<span class="name">el-form-item</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;<span class="name">el-form-item</span></span></span><br><span class="line"><span class="tag">    <span class="attr">label</span>=<span class="string">&quot;员工姓名:&quot;</span></span></span><br><span class="line"><span class="tag">    <span class="attr">prop</span>=<span class="string">&quot;name&quot;</span></span></span><br><span class="line"><span class="tag">&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">el-input</span></span></span><br><span class="line"><span class="tag">    <span class="attr">v-model</span>=<span class="string">&quot;ruleForm.name&quot;</span></span></span><br><span class="line"><span class="tag">    <span class="attr">placeholder</span>=<span class="string">&quot;请输入员工姓名&quot;</span></span></span><br><span class="line"><span class="tag">    <span class="attr">maxlength</span>=<span class="string">&quot;20&quot;</span></span></span><br><span class="line"><span class="tag">    /&gt;</span></span><br><span class="line"><span class="tag">&lt;/<span class="name">el-form-item</span>&gt;</span></span><br><span class="line"></span><br><span class="line"><span class="tag">&lt;<span class="name">el-form-item</span></span></span><br><span class="line"><span class="tag">    <span class="attr">label</span>=<span class="string">&quot;手机号:&quot;</span></span></span><br><span class="line"><span class="tag">    <span class="attr">prop</span>=<span class="string">&quot;phone&quot;</span></span></span><br><span class="line"><span class="tag">&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">el-input</span></span></span><br><span class="line"><span class="tag">    <span class="attr">v-model</span>=<span class="string">&quot;ruleForm.phone&quot;</span></span></span><br><span class="line"><span class="tag">    <span class="attr">placeholder</span>=<span class="string">&quot;请输入手机号&quot;</span></span></span><br><span class="line"><span class="tag">    <span class="attr">maxlength</span>=<span class="string">&quot;20&quot;</span></span></span><br><span class="line"><span class="tag">    /&gt;</span></span><br><span class="line"><span class="tag">&lt;/<span class="name">el-form-item</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;<span class="name">el-form-item</span></span></span><br><span class="line"><span class="tag">    <span class="attr">label</span>=<span class="string">&quot;性别:&quot;</span></span></span><br><span class="line"><span class="tag">    <span class="attr">prop</span>=<span class="string">&quot;sex&quot;</span></span></span><br><span class="line"><span class="tag">&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">el-radio-group</span> <span class="attr">v-model</span>=<span class="string">&quot;ruleForm.sex&quot;</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">el-radio</span> <span class="attr">label</span>=<span class="string">&quot;男&quot;</span>&gt;</span><span class="tag">&lt;/<span class="name">el-radio</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">el-radio</span> <span class="attr">label</span>=<span class="string">&quot;女&quot;</span>&gt;</span><span class="tag">&lt;/<span class="name">el-radio</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;/<span class="name">el-radio-group</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;/<span class="name">el-form-item</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;<span class="name">el-form-item</span></span></span><br><span class="line"><span class="tag">    <span class="attr">label</span>=<span class="string">&quot;身份证号:&quot;</span></span></span><br><span class="line"><span class="tag">    <span class="attr">prop</span>=<span class="string">&quot;idNumber&quot;</span></span></span><br><span class="line"><span class="tag">&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">el-input</span></span></span><br><span class="line"><span class="tag">    <span class="attr">v-model</span>=<span class="string">&quot;ruleForm.idNumber&quot;</span></span></span><br><span class="line"><span class="tag">    <span class="attr">placeholder</span>=<span class="string">&quot;请输入身份证号&quot;</span></span></span><br><span class="line"><span class="tag">    <span class="attr">maxlength</span>=<span class="string">&quot;20&quot;</span></span></span><br><span class="line"><span class="tag">    /&gt;</span></span><br><span class="line"><span class="tag">&lt;/<span class="name">el-form-item</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;subBox address&quot;</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">el-form-item</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">el-button</span>  @<span class="attr">click</span>=<span class="string">&quot;goBack()&quot;</span>&gt;</span></span><br><span class="line">        取消</span><br><span class="line">    <span class="tag">&lt;/<span class="name">el-button</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">el-button</span></span></span><br><span class="line"><span class="tag">        <span class="attr">type</span>=<span class="string">&quot;primary&quot;</span></span></span><br><span class="line"><span class="tag">        @<span class="attr">click</span>=<span class="string">&quot;submitForm(&#x27;ruleForm&#x27;, false)&quot;</span></span></span><br><span class="line"><span class="tag">    &gt;</span></span><br><span class="line">        保存</span><br><span class="line">    <span class="tag">&lt;/<span class="name">el-button</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">el-button</span></span></span><br><span class="line"><span class="tag">        <span class="attr">v-if</span>=<span class="string">&quot;actionType == &#x27;add&#x27;&quot;</span></span></span><br><span class="line"><span class="tag">        <span class="attr">type</span>=<span class="string">&quot;primary&quot;</span></span></span><br><span class="line"><span class="tag">        <span class="attr">class</span>=<span class="string">&quot;continue&quot;</span></span></span><br><span class="line"><span class="tag">        @<span class="attr">click</span>=<span class="string">&quot;submitForm(&#x27;ruleForm&#x27;, true)&quot;</span></span></span><br><span class="line"><span class="tag">    &gt;</span></span><br><span class="line">        保存并继续添加</span><br><span class="line">    <span class="tag">&lt;/<span class="name">el-button</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;/<span class="name">el-form-item</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 class="tag">&lt;/<span class="name">el-form</span>&gt;</span></span><br></pre></td></tr></table></figure><button type="button" class="tab-to-top" aria-label="scroll to top"><i class="fas fa-arrow-up"></i></button></div><div class="tab-item-content" id="新增员工流程分析-2"><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><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">ruleForm : &#123;</span><br><span class="line">    <span class="string">&#x27;name&#x27;</span>: <span class="string">&#x27;&#x27;</span>,</span><br><span class="line">    <span class="string">&#x27;phone&#x27;</span>: <span class="string">&#x27;&#x27;</span>,</span><br><span class="line">    <span class="string">&#x27;sex&#x27;</span>: <span class="string">&#x27;男&#x27;</span>,</span><br><span class="line">    <span class="string">&#x27;idNumber&#x27;</span>: <span class="string">&#x27;&#x27;</span>,</span><br><span class="line">    <span class="attr">username</span>: <span class="string">&#x27;&#x27;</span></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><button type="button" class="tab-to-top" aria-label="scroll to top"><i class="fas fa-arrow-up"></i></button></div><div class="tab-item-content" id="新增员工流程分析-3"><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><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="title function_">submitForm</span> (formName, st) &#123;</span><br><span class="line">    <span class="variable language_">this</span>.<span class="property">$refs</span>[formName].<span class="title function_">validate</span>(<span class="function">(<span class="params">valid</span>) =&gt;</span> &#123;</span><br><span class="line">        <span class="keyword">if</span> (valid) &#123;</span><br><span class="line">        <span class="keyword">if</span> (<span class="variable language_">this</span>.<span class="property">actionType</span> === <span class="string">&#x27;add&#x27;</span>) &#123;</span><br><span class="line">            <span class="keyword">const</span> params = &#123;</span><br><span class="line">            ...<span class="variable language_">this</span>.<span class="property">ruleForm</span>,</span><br><span class="line">            <span class="attr">sex</span>: <span class="variable language_">this</span>.<span class="property">ruleForm</span>.<span class="property">sex</span> === <span class="string">&#x27;女&#x27;</span> ? <span class="string">&#x27;0&#x27;</span> : <span class="string">&#x27;1&#x27;</span></span><br><span class="line">            &#125;</span><br><span class="line">            <span class="title function_">addEmployee</span>(params).<span class="title function_">then</span>(<span class="function"><span class="params">res</span> =&gt;</span> &#123;</span><br><span class="line">            <span class="keyword">if</span> (res.<span class="property">code</span> === <span class="number">1</span>) &#123;</span><br><span class="line">                <span class="variable language_">this</span>.<span class="property">$message</span>.<span class="title function_">success</span>(<span class="string">&#x27;员工添加成功！&#x27;</span>)</span><br><span class="line">                <span class="keyword">if</span> (!st) &#123;</span><br><span class="line">                <span class="variable language_">this</span>.<span class="title function_">goBack</span>()</span><br><span class="line">                &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">                <span class="variable language_">this</span>.<span class="property">ruleForm</span> = &#123;</span><br><span class="line">                    <span class="attr">username</span>: <span class="string">&#x27;&#x27;</span>,</span><br><span class="line">                    <span class="string">&#x27;name&#x27;</span>: <span class="string">&#x27;&#x27;</span>,</span><br><span class="line">                    <span class="string">&#x27;phone&#x27;</span>: <span class="string">&#x27;&#x27;</span>,</span><br><span class="line">                    <span class="comment">// &#x27;password&#x27;: &#x27;&#x27;,</span></span><br><span class="line">                    <span class="comment">// &#x27;rePassword&#x27;: &#x27;&#x27;,/</span></span><br><span class="line">                    <span class="string">&#x27;sex&#x27;</span>: <span class="string">&#x27;男&#x27;</span>,</span><br><span class="line">                    <span class="string">&#x27;idNumber&#x27;</span>: <span class="string">&#x27;&#x27;</span></span><br><span class="line">                &#125;</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="variable language_">this</span>.<span class="property">$message</span>.<span class="title function_">error</span>(res.<span class="property">msg</span> || <span class="string">&#x27;操作失败&#x27;</span>)</span><br><span class="line">            &#125;</span><br><span class="line">            &#125;).<span class="title function_">catch</span>(<span class="function"><span class="params">err</span> =&gt;</span> &#123;</span><br><span class="line">            <span class="variable language_">this</span>.<span class="property">$message</span>.<span class="title function_">error</span>(<span class="string">&#x27;请求出错了：&#x27;</span> + err)</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">const</span> params = &#123;</span><br><span class="line">            ...<span class="variable language_">this</span>.<span class="property">ruleForm</span>,</span><br><span class="line">            <span class="attr">sex</span>: <span class="variable language_">this</span>.<span class="property">ruleForm</span>.<span class="property">sex</span> === <span class="string">&#x27;女&#x27;</span> ? <span class="string">&#x27;0&#x27;</span> : <span class="string">&#x27;1&#x27;</span></span><br><span class="line">            &#125;</span><br><span class="line">            <span class="title function_">editEmployee</span>(params).<span class="title function_">then</span>(<span class="function"><span class="params">res</span> =&gt;</span> &#123;</span><br><span class="line">            <span class="keyword">if</span> (res.<span class="property">code</span> === <span class="number">1</span>) &#123;</span><br><span class="line">                <span class="variable language_">this</span>.<span class="property">$message</span>.<span class="title function_">success</span>(<span class="string">&#x27;员工信息修改成功！&#x27;</span>)</span><br><span class="line">                <span class="variable language_">this</span>.<span class="title function_">goBack</span>()</span><br><span class="line">            &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">                <span class="variable language_">this</span>.<span class="property">$message</span>.<span class="title function_">error</span>(res.<span class="property">msg</span> || <span class="string">&#x27;操作失败&#x27;</span>)</span><br><span class="line">            &#125;</span><br><span class="line">            &#125;).<span class="title function_">catch</span>(<span class="function"><span class="params">err</span> =&gt;</span> &#123;</span><br><span class="line">            <span class="variable language_">this</span>.<span class="property">$message</span>.<span class="title function_">error</span>(<span class="string">&#x27;请求出错了：&#x27;</span> + err)</span><br><span class="line">            &#125;)</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="variable language_">console</span>.<span class="title function_">log</span>(<span class="string">&#x27;error submit!!&#x27;</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">    &#125;)</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><button type="button" class="tab-to-top" aria-label="scroll to top"><i class="fas fa-arrow-up"></i></button></div><div class="tab-item-content" id="新增员工流程分析-4"><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><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="keyword">function</span> <span class="title function_">addEmployee</span> (<span class="params">params</span>) &#123;</span><br><span class="line">  <span class="keyword">return</span> $axios(&#123;</span><br><span class="line">    <span class="attr">url</span>: <span class="string">&#x27;/employee&#x27;</span>,</span><br><span class="line">    <span class="attr">method</span>: <span class="string">&#x27;post&#x27;</span>,</span><br><span class="line">    <span class="attr">data</span>: &#123; ...params &#125;</span><br><span class="line">  &#125;)</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><button type="button" class="tab-to-top" aria-label="scroll to top"><i class="fas fa-arrow-up"></i></button></div></div></div></p><hr><p>接下来来完善后端方法，先通过日志来测试能否接收到提交的员工信息<br><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><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="meta">@PostMapping</span></span><br><span class="line"><span class="keyword">public</span> Result&lt;String&gt; <span class="title function_">save</span><span class="params">(<span class="meta">@RequestBody</span> Employee employee)</span>&#123;</span><br><span class="line">    log.info(<span class="string">&quot;新增的员工信息：&#123;&#125;&quot;</span>,employee.toString());</span><br><span class="line">    <span class="keyword">return</span> <span class="literal">null</span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></p><ul><li>启动服务器后，按照要求填写员工信息<div class="img-wrap"><div class="img-bg"><img class="img" src="https://raw.githubusercontent.com/silvan2077/markdown_pic/main/Picgo/20251031170433.png"/></div></div></li><li>可以看到能接受到员工的相关信息，但有一些信息是null<blockquote><p>新增的员工信息：新增的员工信息：Employee(id=null, username=111, name=111, password=null, phone=15845678912, sex=1, idNumber=371312222222222222, status=null, createTime=null, updateTime=null, createUser=null, updateUser=null)</p></blockquote></li></ul><p>分析员工信息：</p><ul><li><code>id</code>:可以通过雪花算法或自动递增来自动生成</li><li><code>password</code>:可以设置为默认值123456，但是需要进行MD5加密后存储（数据库中应当设置为加密后的密码，因为在登录时对比的就是加密后的密码）</li><li><code>status</code>:设定员工的状态，1表示启用，0表示禁用，设置默认值为1</li><li><code>createTime</code>:创建时间，这个可以指定当前系统时间</li><li><code>updateTime</code>:作用同上</li><li><code>createUser</code>:保存创建员工信息的人的名称，以免随意创建员工账号</li><li><code>updateUser</code>:作用同上</li></ul><h3 id="具体实现"><a href="#具体实现" class="headerlink" title="具体实现"></a>具体实现</h3><p>综上所述，我们设置创建时间和更新时间，创建人ID和修改人ID，其他采用默认值即可，但是密码需要将默认值加密后再存储到数据库中，所以也需要手动编写代码添加<br><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><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="meta">@PostMapping</span></span><br><span class="line"><span class="keyword">public</span> Result&lt;String&gt; <span class="title function_">save</span><span class="params">(HttpServletRequest request, <span class="meta">@RequestBody</span> Employee employee)</span> &#123;</span><br><span class="line">    log.info(<span class="string">&quot;新增的员工信息：&#123;&#125;&quot;</span>, employee.toString());</span><br><span class="line">    <span class="comment">//设置默认密码为123456，并采用MD5加密</span></span><br><span class="line">    employee.setPassword(DigestUtils.md5DigestAsHex(<span class="string">&quot;123456&quot;</span>.getBytes()));</span><br><span class="line">    <span class="comment">//设置createTime和updateTime</span></span><br><span class="line">    employee.setCreateTime(LocalDateTime.now());</span><br><span class="line">    employee.setUpdateTime(LocalDateTime.now());</span><br><span class="line">    <span class="comment">//根据session来获取创建人的id</span></span><br><span class="line">    <span class="type">Long</span> <span class="variable">empId</span> <span class="operator">=</span> (Long) request.getSession().getAttribute(<span class="string">&quot;employee&quot;</span>);</span><br><span class="line">    <span class="comment">//并设置</span></span><br><span class="line">    employee.setCreateUser(empId);</span><br><span class="line">    employee.setUpdateUser(empId);</span><br><span class="line">    <span class="comment">//存入数据库</span></span><br><span class="line">    employeeService.save(employee);</span><br><span class="line">    <span class="keyword">return</span> Result.success(<span class="string">&quot;添加员工成功&quot;</span>);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></p><div class="note warning no-icon flat"><p>现在添加员工的功能大致完成，但在网页中依旧会返回错误，因为分页查询的操作还没有添加，此时可以在数据库看到员工信息已经被添加上去了。</p></div><h3 id="完善全局异常处理器并测试"><a href="#完善全局异常处理器并测试" class="headerlink" title="完善全局异常处理器并测试"></a>完善全局异常处理器并测试</h3><div class="note info no-icon flat"><p>需求：因为username是唯一的，所以在添加员工时，如果输入的username已经存在，则会报错，所以需要对这种异常情况进行处理</p></div><p>解决方案：</p><ol><li>在Controller层中添加try、catch进行异常捕获</li><li>使用异常处理器进行全局异常捕获<div class="note warning no-icon flat"><p>推荐使用异常处理器来进行异常捕获，因为随着程序开发，功能会越来越多，可能出现异常的情况也越多，如果使用try、catch来捕获异常，可能会频繁书写重复的语句。</p></div></li></ol><p>在<code>com.blog.common</code>包下创建一个全局异常处理类<code>GlobalExceptionHandler</code>，并添加<code>ExceptionHandler</code>方法用来捕获异常，并返回结果,在方法上添加<code>@ExceptionHandler</code>来表示该方法专门处理该种异常<br><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><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">@Slf4j</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_">GlobalExceptionHandler</span> &#123;</span><br><span class="line">    <span class="meta">@ExceptionHandler(SQLIntegrityConstraintViolationException.class)</span></span><br><span class="line">    <span class="keyword">public</span> Result&lt;String&gt; <span class="title function_">exceptionHandler</span><span class="params">(SQLIntegrityConstraintViolationException exception)</span> &#123;</span><br><span class="line">        log.error(exception.getMessage());</span><br><span class="line">        <span class="keyword">return</span> Result.error(<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><p>先用日志输出一下看看能不能正常运行，这也是代码开发的一个好习惯<br>再次添加重复用户名的员工信息，发现这次会报错就会出现<code>未知错误</code>的弹窗了<br>控制台日志输出的错误信息为<code>Duplicate entry &#39;Kyle&#39; for key &#39;employee.idx_username&#39;</code><br><div class="img-wrap"><div class="img-bg"><img class="img" src="https://raw.githubusercontent.com/silvan2077/markdown_pic/main/Picgo/20251031173834.png"/></div></div></p><ul><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></pre></td><td class="code"><pre><span class="line"><span class="meta">@Slf4j</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_">GlobalExceptionHandler</span> &#123;</span><br><span class="line">    <span class="meta">@ExceptionHandler(SQLIntegrityConstraintViolationException.class)</span></span><br><span class="line">    <span class="keyword">public</span> Result&lt;String&gt; <span class="title function_">exceptionHandler</span><span class="params">(SQLIntegrityConstraintViolationException exception)</span> &#123;</span><br><span class="line">        log.error(exception.getMessage());</span><br><span class="line">        <span class="comment">//如果包含Duplicate entry，则说明有条目重复</span></span><br><span class="line">        <span class="keyword">if</span> (exception.getMessage().contains(<span class="string">&quot;Duplicate entry&quot;</span>)) &#123;</span><br><span class="line">            <span class="comment">//对字符串切片</span></span><br><span class="line">            String[] split = exception.getMessage().split(<span class="string">&quot; &quot;</span>);</span><br><span class="line">            <span class="comment">//字符串格式是固定的，所以这个位置必然是username</span></span><br><span class="line">            <span class="type">String</span> <span class="variable">username</span> <span class="operator">=</span> split[<span class="number">2</span>];</span><br><span class="line">            <span class="comment">//拼串作为错误信息返回</span></span><br><span class="line">            <span class="keyword">return</span> Result.error(<span class="string">&quot;用户名&quot;</span> + username + <span class="string">&quot;已存在&quot;</span>);</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">return</span> Result.error(<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>接下来重启服务器，测试添加功能，输入已经存在的username，此时的报错信息就更加准确了</p><div class="img-wrap"><div class="img-bg"><img class="img" src="https://raw.githubusercontent.com/silvan2077/markdown_pic/main/Picgo/20251031174534.png"/></div></div><h2 id="员工信息分页查询"><a href="#员工信息分页查询" class="headerlink" title="员工信息分页查询"></a>员工信息分页查询</h2><div class="note info no-icon flat"><p>分页查询执行过程</p><ol><li>页面发送ajax请求，将分页查询参数(page、pageSize、name)提交到服务</li><li>服务端Controller接收页面提交的数据并调用Service查询数据</li><li>Service调用Mapper操作数据库，查询分页数据</li><li>Controller将查询到的分页数据响应给页面</li><li>页面接收到分页数据并通过ElementUI的Table组件展示到页面上</li></ol></div><h3 id="配置MyBatisPlus分页插件"><a href="#配置MyBatisPlus分页插件" class="headerlink" title="配置MyBatisPlus分页插件"></a>配置MyBatisPlus分页插件</h3><p><strong>温馨提示：</strong></p><ul><li>这里如果使用的<code>Mybatis-Plus</code>版本是3.5.9+，那么想使用分页功能需要额外添加一个依赖管理和一个依赖，<a href="https://baomidou.com/getting-started/install/">官方说明</a><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="tag">&lt;<span class="name">dependencyManagement</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">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-bom<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.11<span class="tag">&lt;/<span class="name">version</span>&gt;</span></span><br><span class="line">            <span class="tag">&lt;<span class="name">type</span>&gt;</span>pom<span class="tag">&lt;/<span class="name">type</span>&gt;</span></span><br><span class="line">            <span class="tag">&lt;<span class="name">scope</span>&gt;</span>import<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 class="tag">&lt;/<span class="name">dependencies</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;/<span class="name">dependencyManagement</span>&gt;</span></span><br><span class="line"></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-jsqlparser<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></li></ul><p>新建<code>com.blog.config</code>包，并在其中新建<code>MybatisPlusConfig</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></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_">MybatisPlusConfig</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> MybatisPlusInterceptor <span class="title function_">mybatisPlusInterceptor</span><span class="params">()</span> &#123;</span><br><span class="line">        <span class="type">MybatisPlusInterceptor</span> <span class="variable">mybatisPlusInterceptor</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">MybatisPlusInterceptor</span>();</span><br><span class="line">        mybatisPlusInterceptor.addInnerInterceptor(<span class="keyword">new</span> <span class="title class_">PaginationInnerInterceptor</span>());</span><br><span class="line">        <span class="keyword">return</span> mybatisPlusInterceptor;</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><p>使用GET发送的请求，请求参数在URL中</p><div class="img-wrap"><div class="img-bg"><img class="img" src="https://raw.githubusercontent.com/silvan2077/markdown_pic/main/Picgo/20251031180324.png"/></div></div><p>分析前端代码：<br><div class="tabs" id="分页查询分析前端代码"><ul class="nav-tabs"><li class="tab active"><button type="button" data-href="#分页查询分析前端代码-1">init()</button></li><li class="tab"><button type="button" data-href="#分页查询分析前端代码-2">之前写的PageBean</button></li><li class="tab"><button type="button" data-href="#分页查询分析前端代码-3">getMemberList</button></li><li class="tab"><button type="button" data-href="#分页查询分析前端代码-4">Request拦截器</button></li></ul><div class="tab-contents"><div class="tab-item-content active" id="分页查询分析前端代码-1"><p>这段代码位于<code>resource/backend/page/member/list.html</code><br>这段生命周期函数会在页面初始化时调用来构造数据<br><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><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">async</span> <span class="title function_">init</span> () &#123;</span><br><span class="line">    <span class="keyword">const</span> params = &#123;</span><br><span class="line">        <span class="attr">page</span>: <span class="variable language_">this</span>.<span class="property">page</span>,</span><br><span class="line">        <span class="attr">pageSize</span>: <span class="variable language_">this</span>.<span class="property">pageSize</span>,</span><br><span class="line">        <span class="attr">name</span>: <span class="variable language_">this</span>.<span class="property">input</span> ? <span class="variable language_">this</span>.<span class="property">input</span> : <span class="literal">undefined</span></span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">await</span> <span class="title function_">getMemberList</span>(params).<span class="title function_">then</span>(<span class="function"><span class="params">res</span> =&gt;</span> &#123;</span><br><span class="line">        <span class="keyword">if</span> (<span class="title class_">String</span>(res.<span class="property">code</span>) === <span class="string">&#x27;1&#x27;</span>) &#123;</span><br><span class="line">        <span class="variable language_">this</span>.<span class="property">tableData</span> = res.<span class="property">data</span>.<span class="property">records</span> || []</span><br><span class="line">        <span class="variable language_">this</span>.<span class="property">counts</span> = res.<span class="property">data</span>.<span class="property">total</span></span><br><span class="line">        &#125;</span><br><span class="line">    &#125;).<span class="title function_">catch</span>(<span class="function"><span class="params">err</span> =&gt;</span> &#123;</span><br><span class="line">        <span class="variable language_">this</span>.<span class="property">$message</span>.<span class="title function_">error</span>(<span class="string">&#x27;请求出错了：&#x27;</span> + err)</span><br><span class="line">    &#125;)</span><br><span class="line">&#125;S</span><br></pre></td></tr></table></figure></p><button type="button" class="tab-to-top" aria-label="scroll to top"><i class="fas fa-arrow-up"></i></button></div><div class="tab-item-content" id="分页查询分析前端代码-2"><p>这里的rows对应tableData，totalCount对应counts<br><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><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">//分页查询的JavaBean</span></span><br><span class="line">public <span class="keyword">class</span> <span class="title class_">PageBean</span>&lt;T&gt; &#123;</span><br><span class="line">    <span class="comment">// 总记录数</span></span><br><span class="line">    private int totalCount;</span><br><span class="line">    <span class="comment">// 当前页数据</span></span><br><span class="line">    private <span class="title class_">List</span>&lt;T&gt; rows;</span><br><span class="line"></span><br><span class="line"></span><br><span class="line">    public int <span class="title function_">getTotalCount</span>(<span class="params"></span>) &#123;</span><br><span class="line">        <span class="keyword">return</span> totalCount;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    public <span class="keyword">void</span> <span class="title function_">setTotalCount</span>(<span class="params">int totalCount</span>) &#123;</span><br><span class="line">        <span class="variable language_">this</span>.<span class="property">totalCount</span> = totalCount;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    public <span class="title class_">List</span>&lt;T&gt; <span class="title function_">getRows</span>(<span class="params"></span>) &#123;</span><br><span class="line">        <span class="keyword">return</span> rows;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    public <span class="keyword">void</span> <span class="title function_">setRows</span>(<span class="params">List&lt;T&gt; rows</span>) &#123;</span><br><span class="line">        <span class="variable language_">this</span>.<span class="property">rows</span> = rows;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></p><button type="button" class="tab-to-top" aria-label="scroll to top"><i class="fas fa-arrow-up"></i></button></div><div class="tab-item-content" id="分页查询分析前端代码-3"><p>发送的是GET请求，请求路径为/employee/page，请求参数为前面初始化的params对象<br><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><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="keyword">function</span> <span class="title function_">getMemberList</span> (<span class="params">params</span>) &#123;</span><br><span class="line">  <span class="keyword">return</span> $axios(&#123;</span><br><span class="line">    <span class="attr">url</span>: <span class="string">&#x27;/employee/page&#x27;</span>,</span><br><span class="line">    <span class="attr">method</span>: <span class="string">&#x27;get&#x27;</span>,</span><br><span class="line">    params</span><br><span class="line">  &#125;)</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></p><button type="button" class="tab-to-top" aria-label="scroll to top"><i class="fas fa-arrow-up"></i></button></div><div class="tab-item-content" id="分页查询分析前端代码-4"><p>这是前端提供的拦截器，因为前面的params是json格式的，通过该拦截器将请求参数使用拼串的方式拼接到URL上<br><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><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">// request拦截器</span></span><br><span class="line">service.<span class="property">interceptors</span>.<span class="property">request</span>.<span class="title function_">use</span>(<span class="function"><span class="params">config</span> =&gt;</span> &#123;</span><br><span class="line"><span class="comment">// 是否需要设置 token</span></span><br><span class="line"><span class="comment">// const isToken = (config.headers || &#123;&#125;).isToken === false</span></span><br><span class="line"><span class="comment">// if (getToken() &amp;&amp; !isToken) &#123;</span></span><br><span class="line"><span class="comment">//   config.headers[&#x27;Authorization&#x27;] = &#x27;Bearer &#x27; + getToken() // 让每个请求携带自定义token 请根据实际情况自行修改</span></span><br><span class="line"><span class="comment">// &#125;</span></span><br><span class="line"><span class="comment">// get请求映射params参数</span></span><br><span class="line"><span class="keyword">if</span> (config.<span class="property">method</span> === <span class="string">&#x27;get&#x27;</span> &amp;&amp; config.<span class="property">params</span>) &#123;</span><br><span class="line">    <span class="keyword">let</span> url = config.<span class="property">url</span> + <span class="string">&#x27;?&#x27;</span>;</span><br><span class="line">    <span class="keyword">for</span> (<span class="keyword">const</span> propName <span class="keyword">of</span> <span class="title class_">Object</span>.<span class="title function_">keys</span>(config.<span class="property">params</span>)) &#123;</span><br><span class="line">    <span class="keyword">const</span> value = config.<span class="property">params</span>[propName];</span><br><span class="line">    <span class="keyword">var</span> part = <span class="built_in">encodeURIComponent</span>(propName) + <span class="string">&quot;=&quot;</span>;</span><br><span class="line">    <span class="keyword">if</span> (value !== <span class="literal">null</span> &amp;&amp; <span class="title function_">typeof</span>(value) !== <span class="string">&quot;undefined&quot;</span>) &#123;</span><br><span class="line">        <span class="keyword">if</span> (<span class="keyword">typeof</span> value === <span class="string">&#x27;object&#x27;</span>) &#123;</span><br><span class="line">        <span class="keyword">for</span> (<span class="keyword">const</span> key <span class="keyword">of</span> <span class="title class_">Object</span>.<span class="title function_">keys</span>(value)) &#123;</span><br><span class="line">            <span class="keyword">let</span> params = propName + <span class="string">&#x27;[&#x27;</span> + key + <span class="string">&#x27;]&#x27;</span>;</span><br><span class="line">            <span class="keyword">var</span> subPart = <span class="built_in">encodeURIComponent</span>(params) + <span class="string">&quot;=&quot;</span>;</span><br><span class="line">            url += subPart + <span class="built_in">encodeURIComponent</span>(value[key]) + <span class="string">&quot;&amp;&quot;</span>;</span><br><span class="line">        &#125;</span><br><span class="line">        &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">        url += part + <span class="built_in">encodeURIComponent</span>(value) + <span class="string">&quot;&amp;&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">    url = url.<span class="title function_">slice</span>(<span class="number">0</span>, -<span class="number">1</span>);</span><br><span class="line">    config.<span class="property">params</span> = &#123;&#125;;</span><br><span class="line">    config.<span class="property">url</span> = url;</span><br><span class="line">&#125;</span><br><span class="line"><span class="keyword">return</span> config</span><br><span class="line">&#125;, <span class="function"><span class="params">error</span> =&gt;</span> &#123;</span><br><span class="line">    <span class="variable language_">console</span>.<span class="title function_">log</span>(error)</span><br><span class="line">    <span class="title class_">Promise</span>.<span class="title function_">reject</span>(error)</span><br><span class="line">&#125;)</span><br></pre></td></tr></table></figure></p><button type="button" class="tab-to-top" aria-label="scroll to top"><i class="fas fa-arrow-up"></i></button></div></div></div></p><h3 id="编写具体的业务逻辑"><a href="#编写具体的业务逻辑" class="headerlink" title="编写具体的业务逻辑"></a>编写具体的业务逻辑</h3><p>先通过日志来检查是否能正常接收到数据，因为这里的需求是<strong>服务端Controller接收页面提交的数据并调用Service查询数据</strong>，所以返回值中应当包含当前页数，总页数等数据，因此返回值的泛型不是<code>Employee</code>而是<code>Page</code><br><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><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="meta">@GetMapping(&quot;/page&quot;)</span></span><br><span class="line"><span class="keyword">public</span> Result&lt;Page&gt; <span class="title function_">page</span><span class="params">(<span class="type">int</span> page, <span class="type">int</span> pageSize, String name)</span> &#123;</span><br><span class="line">    log.info(<span class="string">&quot;page=&#123;&#125;,pageSize=&#123;&#125;,name=&#123;&#125;&quot;</span>, page, pageSize, name);</span><br><span class="line">    <span class="keyword">return</span> <span class="literal">null</span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></p><p>此时在搜索框输入123，发现日志输出如下，符合预期</p><blockquote><p>: page=1,pageSize=10,name=123</p></blockquote><p>继续完善业务逻辑<br><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><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">@GetMapping(&quot;/page&quot;)</span></span><br><span class="line">    <span class="keyword">public</span> Result&lt;Page&gt; <span class="title function_">page</span><span class="params">(<span class="type">int</span> page, <span class="type">int</span> pageSize, String name)</span> &#123;</span><br><span class="line">        log.info(<span class="string">&quot;page=&#123;&#125;,pageSize=&#123;&#125;,name=&#123;&#125;&quot;</span>, page, pageSize, name);</span><br><span class="line">        <span class="comment">//构造分页构造器</span></span><br><span class="line">        Page&lt;Employee&gt; pageConstructor = <span class="keyword">new</span> <span class="title class_">Page</span>&lt;&gt;(page, pageSize);</span><br><span class="line">        <span class="comment">//构造条件构造器,动态封装查询条件</span></span><br><span class="line">        LambdaQueryWrapper&lt;Employee&gt; wrapper = <span class="keyword">new</span> <span class="title class_">LambdaQueryWrapper</span>&lt;&gt;();</span><br><span class="line">        <span class="comment">//添加过滤条件（当我们没有输入name时，就相当于查询所有了）</span></span><br><span class="line">        wrapper.like(!(name == <span class="literal">null</span> || <span class="string">&quot;&quot;</span>.equals(name)), Employee::getName, name);</span><br><span class="line">        <span class="comment">//并对查询的结果进行降序排序，根据更新时间</span></span><br><span class="line">        wrapper.orderByDesc(Employee::getUpdateTime);</span><br><span class="line">        <span class="comment">//执行查询，查询之后不需要返回，他会直接将查询结果封装到pageConstructor对象中</span></span><br><span class="line">        employeeService.page(pageConstructor, wrapper);</span><br><span class="line">        <span class="keyword">return</span> Result.success(pageConstructor);</span><br><span class="line">    &#125;</span><br></pre></td></tr></table></figure></p><div class="note info no-icon flat"><p>此时页面的数据就可以正常展示了，还支持根据名称模糊查询</p><div class="img-wrap"><div class="img-bg"><img class="img" src="https://raw.githubusercontent.com/silvan2077/markdown_pic/main/Picgo/20251031185109.png"/></div></div></div><h3 id="补充说明"><a href="#补充说明" class="headerlink" title="补充说明"></a>补充说明</h3><ul><li><p>为什么后端传给页面的status数据为Integer类型，到页面展示效果的时候显示的是已禁用或者正常？</p><ul><li><p>看一下源码就知道了</p><p>三目运算符+插值表达式</p><figure class="highlight handlebars"><table><tr><td class="gutter"><pre><span class="line">1</span><br><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="language-xml"><span class="tag">&lt;<span class="name">el-table-column</span> <span class="attr">label</span>=<span class="string">&quot;账号状态&quot;</span>&gt;</span></span></span><br><span class="line"><span class="language-xml">    <span class="tag">&lt;<span class="name">template</span> <span class="attr">slot-scope</span>=<span class="string">&quot;scope&quot;</span>&gt;</span></span></span><br><span class="line"><span class="language-xml">    </span><span class="template-variable">&#123;&#123; <span class="name">String</span>(<span class="name">scope.row.status</span>) === <span class="string">&#x27;0&#x27;</span> ? <span class="string">&#x27;已禁用&#x27;</span> : <span class="string">&#x27;正常&#x27;</span> &#125;&#125;</span><span class="language-xml"></span></span><br><span class="line"><span class="language-xml">    <span class="tag">&lt;/<span class="name">template</span>&gt;</span></span></span><br><span class="line"><span class="language-xml"><span class="tag">&lt;/<span class="name">el-table-column</span>&gt;</span></span></span><br></pre></td></tr></table></figure></li></ul></li></ul><h2 id="启用-禁用员工账号"><a href="#启用-禁用员工账号" class="headerlink" title="启用/禁用员工账号"></a>启用/禁用员工账号</h2><h3 id="需求分析"><a href="#需求分析" class="headerlink" title="需求分析"></a>需求分析</h3><ol><li>在员工管理列表页面，可以对某个员工账号进行启用或者禁用操作。账号状态为禁用的员工不能登录系统，启用后的员工可以正常登录。</li><li>需要注意，只有管理员（admin用户）可以对其他普通用户进行启用、禁用操作，所以普通用户登录系统后启用、禁用按钮不显示。</li><li>管理员admin登录系统可以对所有员工账号进行启用、禁用操作。</li><li>如果某个员工账号状态为正常，则按钮显示为“禁用”，如果员工账号状态为已禁用，则按钮显示为“启用”</li></ol><div class="tabs" id="禁用账号图片"><ul class="nav-tabs"><li class="tab active"><button type="button" data-href="#禁用账号图片-1">管理员账号</button></li><li class="tab"><button type="button" data-href="#禁用账号图片-2">普通账号</button></li></ul><div class="tab-contents"><div class="tab-item-content active" id="禁用账号图片-1"><div class="img-wrap"><div class="img-bg"><img class="img" src="https://raw.githubusercontent.com/silvan2077/markdown_pic/main/Picgo/20251031190045.png"/></div></div><button type="button" class="tab-to-top" aria-label="scroll to top"><i class="fas fa-arrow-up"></i></button></div><div class="tab-item-content" id="禁用账号图片-2"><div class="img-wrap"><div class="img-bg"><img class="img" src="https://raw.githubusercontent.com/silvan2077/markdown_pic/main/Picgo/20251031190230.png"/></div></div><button type="button" class="tab-to-top" aria-label="scroll to top"><i class="fas fa-arrow-up"></i></button></div></div></div><h3 id="动态按钮显示分析"><a href="#动态按钮显示分析" class="headerlink" title="动态按钮显示分析"></a>动态按钮显示分析</h3><p>怎么才能做到：只有当登录的是管理员账号时，才能看到启用/禁用按钮呢？</p><ul><li>当我们加载完页面的时候，获取一下当前登录账号的用户名，也就是username</li></ul><figure class="highlight js"><table><tr><td class="gutter"><pre><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="title function_">created</span>(<span class="params"></span>) &#123;</span><br><span class="line">    <span class="variable language_">this</span>.<span class="title function_">init</span>()</span><br><span class="line">    <span class="variable language_">this</span>.<span class="property">user</span> = <span class="title class_">JSON</span>.<span class="title function_">parse</span>(<span class="variable language_">localStorage</span>.<span class="title function_">getItem</span>(<span class="string">&#x27;userInfo&#x27;</span>)).<span class="property">username</span></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><ul><li><p>随后判断一下这个用户名是不是<code>admin</code>，如果是的话就显示启用/禁用，否则不显示,通过<code>v-if</code>来判断</p><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><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">&lt;el-button</span><br><span class="line">    type=<span class="string">&quot;text&quot;</span></span><br><span class="line">    size=<span class="string">&quot;small&quot;</span></span><br><span class="line">    <span class="keyword">class</span>=<span class="string">&quot;delBut non&quot;</span></span><br><span class="line">    @click=<span class="string">&quot;statusHandle(scope.row)&quot;</span></span><br><span class="line">    v-<span class="keyword">if</span>=<span class="string">&quot;user === &#x27;admin&#x27;&quot;</span></span><br><span class="line">&gt;</span><br><span class="line">    &#123;&#123; scope.<span class="property">row</span>.<span class="property">status</span> == <span class="string">&#x27;1&#x27;</span> ? <span class="string">&#x27;禁用&#x27;</span> : <span class="string">&#x27;启用&#x27;</span> &#125;&#125;</span><br><span class="line">&lt;/el-button&gt;</span><br></pre></td></tr></table></figure></li></ul><h3 id="Ajax请求发送过程"><a href="#Ajax请求发送过程" class="headerlink" title="Ajax请求发送过程"></a>Ajax请求发送过程</h3><div class="note info no-icon flat"><p>发送过程：</p><ol><li>点击禁用/启用后，页面发送ajax请求，将参数(id、status)提交到服务端</li><li>服务端Controller接收页面提交的数据并调用Service更新数据</li><li>Service调用Mapper操作数据库</li></ol></div><div class="img-wrap"><div class="img-bg"><img class="img" src="https://raw.githubusercontent.com/silvan2077/markdown_pic/main/Picgo/20251031193349.png"/></div></div><ul><li>前端代码：<div class="tabs" id="禁用前端代码分析"><ul class="nav-tabs"><li class="tab active"><button type="button" data-href="#禁用前端代码分析-1">button</button></li><li class="tab"><button type="button" data-href="#禁用前端代码分析-2">statusHandle</button></li><li class="tab"><button type="button" data-href="#禁用前端代码分析-3">enableOrDisableEmployee</button></li></ul><div class="tab-contents"><div class="tab-item-content active" id="禁用前端代码分析-1"><p>在按钮上绑定了<code>statusHandle(scope.row)</code>方法<br><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></pre></td><td class="code"><pre><span class="line"><span class="tag">&lt;<span class="name">el-button</span></span></span><br><span class="line"><span class="tag">    <span class="attr">type</span>=<span class="string">&quot;text&quot;</span></span></span><br><span class="line"><span class="tag">    <span class="attr">size</span>=<span class="string">&quot;small&quot;</span></span></span><br><span class="line"><span class="tag">    <span class="attr">class</span>=<span class="string">&quot;delBut non&quot;</span></span></span><br><span class="line"><span class="tag">    @<span class="attr">click</span>=<span class="string">&quot;statusHandle(scope.row)&quot;</span></span></span><br><span class="line"><span class="tag">    <span class="attr">v-if</span>=<span class="string">&quot;user === &#x27;admin&#x27;&quot;</span></span></span><br><span class="line"><span class="tag">&gt;</span></span><br><span class="line">    &#123;&#123; scope.row.status == &#x27;1&#x27; ? &#x27;禁用&#x27; : &#x27;启用&#x27; &#125;&#125;</span><br><span class="line"><span class="tag">&lt;/<span class="name">el-button</span>&gt;</span></span><br></pre></td></tr></table></figure></p><button type="button" class="tab-to-top" aria-label="scroll to top"><i class="fas fa-arrow-up"></i></button></div><div class="tab-item-content" id="禁用前端代码分析-2"><p>该方法先获得当前行的id与status，接着弹出提示框确认是否要修改状态，确认后调用<code>enableOrDisableEmployee</code>将其状态取反<br><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><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="title function_">statusHandle</span> (row) &#123;</span><br><span class="line">    <span class="variable language_">this</span>.<span class="property">id</span> = row.<span class="property">id</span></span><br><span class="line">    <span class="variable language_">this</span>.<span class="property">status</span> = row.<span class="property">status</span></span><br><span class="line">    <span class="variable language_">this</span>.$confirm(<span class="string">&#x27;确认调整该账号的状态?&#x27;</span>, <span class="string">&#x27;提示&#x27;</span>, &#123;</span><br><span class="line">        <span class="string">&#x27;confirmButtonText&#x27;</span>: <span class="string">&#x27;确定&#x27;</span>,</span><br><span class="line">        <span class="string">&#x27;cancelButtonText&#x27;</span>: <span class="string">&#x27;取消&#x27;</span>,</span><br><span class="line">        <span class="string">&#x27;type&#x27;</span>: <span class="string">&#x27;warning&#x27;</span></span><br><span class="line">        &#125;).<span class="title function_">then</span>(<span class="function">() =&gt;</span> &#123;</span><br><span class="line">        <span class="title function_">enableOrDisableEmployee</span>(&#123; <span class="string">&#x27;id&#x27;</span>: <span class="variable language_">this</span>.<span class="property">id</span>, <span class="string">&#x27;status&#x27;</span>: !<span class="variable language_">this</span>.<span class="property">status</span> ? <span class="number">1</span> : <span class="number">0</span> &#125;).<span class="title function_">then</span>(<span class="function"><span class="params">res</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;enableOrDisableEmployee&#x27;</span>,res)</span><br><span class="line">        <span class="keyword">if</span> (<span class="title class_">String</span>(res.<span class="property">code</span>) === <span class="string">&#x27;1&#x27;</span>) &#123;</span><br><span class="line">            <span class="variable language_">this</span>.<span class="property">$message</span>.<span class="title function_">success</span>(<span class="string">&#x27;账号状态更改成功！&#x27;</span>)</span><br><span class="line">            <span class="variable language_">this</span>.<span class="title function_">handleQuery</span>()</span><br><span class="line">        &#125;</span><br><span class="line">        &#125;).<span class="title function_">catch</span>(<span class="function"><span class="params">err</span> =&gt;</span> &#123;</span><br><span class="line">        <span class="variable language_">this</span>.<span class="property">$message</span>.<span class="title function_">error</span>(<span class="string">&#x27;请求出错了：&#x27;</span> + err)</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><button type="button" class="tab-to-top" aria-label="scroll to top"><i class="fas fa-arrow-up"></i></button></div><div class="tab-item-content" id="禁用前端代码分析-3"><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><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="keyword">function</span> <span class="title function_">enableOrDisableEmployee</span> (<span class="params">params</span>) &#123;</span><br><span class="line">  <span class="keyword">return</span> $axios(&#123;</span><br><span class="line">    <span class="attr">url</span>: <span class="string">&#x27;/employee&#x27;</span>,</span><br><span class="line">    <span class="attr">method</span>: <span class="string">&#x27;put&#x27;</span>,</span><br><span class="line">    <span class="attr">data</span>: &#123; ...params &#125;</span><br><span class="line">  &#125;)</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><button type="button" class="tab-to-top" aria-label="scroll to top"><i class="fas fa-arrow-up"></i></button></div></div></div></li></ul><ul><li>后端代码分析<br>启用、禁用员工账号，本质上是更新操作，也就是对status状态字段进行修改<br>养成先用日志测试的习惯<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><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="meta">@PutMapping</span></span><br><span class="line"><span class="keyword">public</span> Result&lt;String&gt; <span class="title function_">update</span><span class="params">(<span class="meta">@RequestBody</span> Employee employee)</span> &#123;</span><br><span class="line">    log.info(employee.toString());</span><br><span class="line">    <span class="keyword">return</span> <span class="literal">null</span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></li></ul><div class="img-wrap"><div class="img-bg"><img class="img" src="https://raw.githubusercontent.com/silvan2077/markdown_pic/main/Picgo/20251031194636.png"/></div></div><ul><li>通过日志可以看到能够正常接收到employee对象数据，接着完善update方法，在更新用户信息时自动更新修改时间和修改用户</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="meta">@PutMapping</span></span><br><span class="line"><span class="keyword">public</span> Result&lt;String&gt; <span class="title function_">update</span><span class="params">(<span class="meta">@RequestBody</span> Employee employee, HttpServletRequest request)</span> &#123;</span><br><span class="line">    log.info(employee.toString());</span><br><span class="line">    <span class="type">Long</span> <span class="variable">id</span> <span class="operator">=</span> (Long) request.getSession().getAttribute(<span class="string">&quot;employee&quot;</span>);</span><br><span class="line">    employee.setUpdateUser(id);</span><br><span class="line">    employee.setUpdateTime(LocalDateTime.now());</span><br><span class="line">    employeeService.updateById(employee);</span><br><span class="line">    log.info(<span class="string">&quot;员工信息修改成功&quot;</span>);</span><br><span class="line">    <span class="keyword">return</span> Result.success(<span class="string">&quot;员工信息修改成功&quot;</span>);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><ul><li>通过日志可以看到更新语句输出了，但没有输出更新语句，数据库中的status字段也没有被修改<div class="img-wrap"><div class="img-bg"><img class="img" src="https://raw.githubusercontent.com/silvan2077/markdown_pic/main/Picgo/20251101133738.png"/></div></div></li></ul><h3 id="配置消息转换器"><a href="#配置消息转换器" class="headerlink" title="配置消息转换器"></a>配置消息转换器</h3><p>更新失败的原因是JS对Long型数据进行处理时丢失了精度(前端接受数据类型，超过16位会精度损失)，导致提交的id和数据库中的id不一致，导致更新失效，可以通过配置消息转换器将Long型数据转为String型数据，再进行数据库更新</p><div class="note info no-icon flat"><p>具体实现步骤：</p><ol><li>提供对象转换器JacksonObjectMapper,基于Jackson进行对象到Json数据的转换（资料中有，直接复制）</li><li>在WebMvcConfig配置类中扩展SpringMvc的消息转换器，在此消息转换器中使用提供的对象转换器进行Java对象到Json数据的转换</li></ol></div><p>直接复制下面的对象转换器放到common包下<br><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><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><br><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * 对象映射器:基于jackson将Java对象转为json，或者将json转为Java对象</span></span><br><span class="line"><span class="comment"> * 将JSON解析为Java对象的过程称为 [从JSON反序列化Java对象]</span></span><br><span class="line"><span class="comment"> * 从Java对象生成JSON的过程称为 [序列化Java对象到JSON]</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_">JacksonObjectMapper</span> <span class="keyword">extends</span> <span class="title class_">ObjectMapper</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">DEFAULT_DATE_FORMAT</span> <span class="operator">=</span> <span class="string">&quot;yyyy-MM-dd&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">DEFAULT_DATE_TIME_FORMAT</span> <span class="operator">=</span> <span class="string">&quot;yyyy-MM-dd HH:mm:ss&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">DEFAULT_TIME_FORMAT</span> <span class="operator">=</span> <span class="string">&quot;HH:mm:ss&quot;</span>;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">public</span> <span class="title function_">JacksonObjectMapper</span><span class="params">()</span> &#123;</span><br><span class="line">        <span class="built_in">super</span>();</span><br><span class="line">        <span class="comment">//收到未知属性时不报异常</span></span><br><span class="line">        <span class="built_in">this</span>.configure(FAIL_ON_UNKNOWN_PROPERTIES, <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="built_in">this</span>.getDeserializationConfig().withoutFeatures(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES);</span><br><span class="line"></span><br><span class="line"></span><br><span class="line">        <span class="type">SimpleModule</span> <span class="variable">simpleModule</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">SimpleModule</span>()</span><br><span class="line">                .addDeserializer(LocalDateTime.class, <span class="keyword">new</span> <span class="title class_">LocalDateTimeDeserializer</span>(DateTimeFormatter.ofPattern(DEFAULT_DATE_TIME_FORMAT)))</span><br><span class="line">                .addDeserializer(LocalDate.class, <span class="keyword">new</span> <span class="title class_">LocalDateDeserializer</span>(DateTimeFormatter.ofPattern(DEFAULT_DATE_FORMAT)))</span><br><span class="line">                .addDeserializer(LocalTime.class, <span class="keyword">new</span> <span class="title class_">LocalTimeDeserializer</span>(DateTimeFormatter.ofPattern(DEFAULT_TIME_FORMAT)))</span><br><span class="line"></span><br><span class="line">                .addSerializer(BigInteger.class, ToStringSerializer.instance)</span><br><span class="line">                .addSerializer(Long.class, ToStringSerializer.instance)</span><br><span class="line">                .addSerializer(LocalDateTime.class, <span class="keyword">new</span> <span class="title class_">LocalDateTimeSerializer</span>(DateTimeFormatter.ofPattern(DEFAULT_DATE_TIME_FORMAT)))</span><br><span class="line">                .addSerializer(LocalDate.class, <span class="keyword">new</span> <span class="title class_">LocalDateSerializer</span>(DateTimeFormatter.ofPattern(DEFAULT_DATE_FORMAT)))</span><br><span class="line">                .addSerializer(LocalTime.class, <span class="keyword">new</span> <span class="title class_">LocalTimeSerializer</span>(DateTimeFormatter.ofPattern(DEFAULT_TIME_FORMAT)));</span><br><span class="line"></span><br><span class="line">        <span class="comment">//注册功能模块 例如，可以添加自定义序列化器和反序列化器</span></span><br><span class="line">        <span class="built_in">this</span>.registerModule(simpleModule);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></p><p>扩展Mvc框架的消息转换器<br><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><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">@Configuration</span></span><br><span class="line"><span class="meta">@Slf4j</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">WebMvcConfig</span> <span class="keyword">extends</span> <span class="title class_">WebMvcConfigurationSupport</span> &#123;</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_">extendMessageConverters</span><span class="params">(List&lt;HttpMessageConverter&lt;?&gt;&gt; converters)</span> &#123;</span><br><span class="line">        <span class="type">MappingJackson2HttpMessageConverter</span> <span class="variable">messageConverter</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">MappingJackson2HttpMessageConverter</span>();</span><br><span class="line">        <span class="comment">//设置对象转化器，底层使用jackson将java对象转为json</span></span><br><span class="line">        messageConverter.setObjectMapper(<span class="keyword">new</span> <span class="title class_">JacksonObjectMapper</span>());</span><br><span class="line">        <span class="comment">//将上面的消息转换器对象追加到mvc框架的转换器集合当中(index设置为0，表示设置在第一个位置，避免被其它转换器接收，从而达不到想要的功能)</span></span><br><span class="line">        converters.add(<span class="number">0</span>, messageConverter);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><br><div class="note warning no-icon flat"><p>这里如果前面在一开始没有配置资源映射器，则需要添加，并将前端和后端的静态文件直接放在resource目录下，否则会报以下错误：</p><div class="img-wrap"><div class="img-bg"><img class="img" src="https://raw.githubusercontent.com/silvan2077/markdown_pic/main/Picgo/20251101143539.png"/></div></div></div></p><h3 id="再次测试"><a href="#再次测试" class="headerlink" title="再次测试"></a>再次测试</h3><p>再次点击禁用按钮，此时数据库中的status字段数据发生了改变，且页面上也显示已禁用，再次点击启用，也能正常操作<br><div class="img-wrap"><div class="img-bg"><img class="img" src="https://raw.githubusercontent.com/silvan2077/markdown_pic/main/Picgo/20251101143758.png"/></div></div></p><h2 id="编辑员工信息"><a href="#编辑员工信息" class="headerlink" title="编辑员工信息"></a>编辑员工信息</h2><h3 id="流程分析-1"><a href="#流程分析-1" class="headerlink" title="流程分析"></a>流程分析</h3><p>在开发代码之前先梳理一下整个操作流程与对应程序的执行顺序：</p><ol><li>点击编辑按钮时，页面将跳转到<code>add.html</code>，并在url中携带参数<code>员工id</code></li><li>在<code>add.html</code>页面中获取url中的参数<code>员工id</code></li><li>发送<code>ajax</code>请求，请求服务端，同时提交<code>员工id</code>参数</li><li>服务端接受请求，并根据<code>员工id</code>查询员工信息，并将员工信息以<code>json</code>形式响应给页面</li><li>页面接收服务端响应的<code>json</code>数据，并通过Vue的<code>双向绑定</code>进行员工信息回显</li><li>点击保存按钮，发送ajax请求，将页面中的员工信息以json形式提交给服务端</li><li>服务端接受员工信息，并进行处理，完成后给页面响应</li><li>页面接收到服务端响应信息后进行相应处理</li></ol><div class="img-wrap"><div class="img-bg"><img class="img" src="https://raw.githubusercontent.com/silvan2077/markdown_pic/main/Picgo/20251101143958.png"/></div></div><h3 id="具体实现-1"><a href="#具体实现-1" class="headerlink" title="具体实现"></a>具体实现</h3><div class="note danger no-icon flat"><p>add.html是添加员工和修改员工共用的页面</p></div><ol><li><p>点击编辑按钮时，页面将跳转到<code>add.html</code>，并在url中携带参数<code>员工id</code>,编辑按钮绑定的点击事件为<code>addMemberHandle(scope.row.id)</code></p><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></pre></td><td class="code"><pre><span class="line"><span class="tag">&lt;<span class="name">el-button</span></span></span><br><span class="line"><span class="tag">    <span class="attr">type</span>=<span class="string">&quot;text&quot;</span></span></span><br><span class="line"><span class="tag">    <span class="attr">size</span>=<span class="string">&quot;small&quot;</span></span></span><br><span class="line"><span class="tag">    <span class="attr">class</span>=<span class="string">&quot;blueBug&quot;</span></span></span><br><span class="line"><span class="tag">    @<span class="attr">click</span>=<span class="string">&quot;addMemberHandle(scope.row.id)&quot;</span></span></span><br><span class="line"><span class="tag">    <span class="attr">:class</span>=<span class="string">&quot;&#123;notAdmin:user !== &#x27;admin&#x27;&#125;&quot;</span></span></span><br><span class="line"><span class="tag">&gt;</span></span><br><span class="line">    编辑</span><br><span class="line"><span class="tag">&lt;/<span class="name">el-button</span>&gt;</span></span><br></pre></td></tr></table></figure></li><li><p>在<code>add.html</code>页面中获取url中的参数<code>员工id</code></p><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><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="title function_">addMemberHandle</span> (st) &#123;</span><br><span class="line">    <span class="keyword">if</span> (st === <span class="string">&#x27;add&#x27;</span>)&#123;</span><br><span class="line">        <span class="variable language_">window</span>.<span class="property">parent</span>.<span class="title function_">menuHandle</span>(&#123;</span><br><span class="line">        <span class="attr">id</span>: <span class="string">&#x27;2&#x27;</span>,</span><br><span class="line">        <span class="attr">url</span>: <span class="string">&#x27;/backend/page/member/add.html&#x27;</span>,</span><br><span class="line">        <span class="attr">name</span>: <span class="string">&#x27;添加员工&#x27;</span></span><br><span class="line">        &#125;,<span class="literal">true</span>)</span><br><span class="line">    &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">        <span class="variable language_">window</span>.<span class="property">parent</span>.<span class="title function_">menuHandle</span>(&#123;</span><br><span class="line">        <span class="attr">id</span>: <span class="string">&#x27;2&#x27;</span>,</span><br><span class="line">        <span class="attr">url</span>: <span class="string">&#x27;/backend/page/member/add.html?id=&#x27;</span>+st,</span><br><span class="line">        <span class="attr">name</span>: <span class="string">&#x27;修改员工&#x27;</span></span><br><span class="line">        &#125;,<span class="literal">true</span>)</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></li><li><p>发送<code>ajax</code>请求，请求服务端，同时提交<code>员工id</code>参数</p><p>当add.html加载完毕之后，调用钩子函数，当参数存在时，说明是编辑员工，否则是添加员工</p></li></ol><div class="tabs" id="编辑员工调用钩子函数"><ul class="nav-tabs"><li class="tab active"><button type="button" data-href="#编辑员工调用钩子函数-1">created()</button></li><li class="tab"><button type="button" data-href="#编辑员工调用钩子函数-2">requestUrlParam()</button></li></ul><div class="tab-contents"><div class="tab-item-content active" id="编辑员工调用钩子函数-1"><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><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="title function_">created</span>(<span class="params"></span>) &#123;</span><br><span class="line">    <span class="variable language_">this</span>.<span class="property">id</span> = <span class="title function_">requestUrlParam</span>(<span class="string">&#x27;id&#x27;</span>)</span><br><span class="line">    <span class="variable language_">this</span>.<span class="property">actionType</span> = <span class="variable language_">this</span>.<span class="property">id</span> ? <span class="string">&#x27;edit&#x27;</span> : <span class="string">&#x27;add&#x27;</span></span><br><span class="line">    <span class="keyword">if</span> (<span class="variable language_">this</span>.<span class="property">id</span>) &#123;</span><br><span class="line">    <span class="variable language_">this</span>.<span class="title function_">init</span>()</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><button type="button" class="tab-to-top" aria-label="scroll to top"><i class="fas fa-arrow-up"></i></button></div><div class="tab-item-content" id="编辑员工调用钩子函数-2"><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><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">function</span> <span class="title function_">requestUrlParam</span>(<span class="params">argname</span>)&#123;</span><br><span class="line">  <span class="keyword">var</span> url = location.<span class="property">href</span></span><br><span class="line">  <span class="keyword">var</span> arrStr = url.<span class="title function_">substring</span>(url.<span class="title function_">indexOf</span>(<span class="string">&quot;?&quot;</span>)+<span class="number">1</span>).<span class="title function_">split</span>(<span class="string">&quot;&amp;&quot;</span>)</span><br><span class="line">  <span class="keyword">for</span>(<span class="keyword">var</span> i =<span class="number">0</span>;i&lt;arrStr.<span class="property">length</span>;i++)</span><br><span class="line">  &#123;</span><br><span class="line">      <span class="keyword">var</span> loc = arrStr[i].<span class="title function_">indexOf</span>(argname+<span class="string">&quot;=&quot;</span>)</span><br><span class="line">      <span class="keyword">if</span>(loc!=-<span class="number">1</span>)&#123;</span><br><span class="line">          <span class="keyword">return</span> arrStr[i].<span class="title function_">replace</span>(argname+<span class="string">&quot;=&quot;</span>,<span class="string">&quot;&quot;</span>).<span class="title function_">replace</span>(<span class="string">&quot;?&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">  <span class="keyword">return</span> <span class="string">&quot;&quot;</span></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><button type="button" class="tab-to-top" aria-label="scroll to top"><i class="fas fa-arrow-up"></i></button></div></div></div><ol><li>服务端接受请求，并根据<code>员工id</code>查询员工信息，并将员工信息以<code>json</code>形式响应给页面</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="meta">@GetMapping(&quot;/&#123;id&#125;&quot;)</span></span><br><span class="line"><span class="keyword">public</span> Result&lt;Employee&gt; <span class="title function_">getById</span><span class="params">(<span class="meta">@PathVariable</span> Long id)</span> &#123;</span><br><span class="line">    log.info(<span class="string">&quot;根据id查询员工信息..&quot;</span>);</span><br><span class="line">    <span class="type">Employee</span> <span class="variable">employee</span> <span class="operator">=</span> employeeService.getById(id);</span><br><span class="line">    <span class="keyword">if</span> (employee != <span class="literal">null</span>) &#123;</span><br><span class="line">        <span class="keyword">return</span> Result.success(employee);</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">return</span> Result.error(<span class="string">&quot;未查询到该员工信息&quot;</span>);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><ol><li>add.html的钩子函数中调用了init函数，接收到服务端响应的Json数据后，判断状态码，如果操作成功则将获取到的数据赋给表单，并通过双向绑定来实现数据回显</li></ol><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><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">async</span> <span class="title function_">init</span> () &#123;</span><br><span class="line">    <span class="title function_">queryEmployeeById</span>(<span class="variable language_">this</span>.<span class="property">id</span>).<span class="title function_">then</span>(<span class="function"><span class="params">res</span> =&gt;</span> &#123;</span><br><span class="line">        <span class="variable language_">console</span>.<span class="title function_">log</span>(res)</span><br><span class="line">        <span class="keyword">if</span> (<span class="title class_">String</span>(res.<span class="property">code</span>) === <span class="string">&#x27;1&#x27;</span>) &#123;</span><br><span class="line">        <span class="variable language_">console</span>.<span class="title function_">log</span>(res.<span class="property">data</span>)</span><br><span class="line">        <span class="variable language_">this</span>.<span class="property">ruleForm</span> = res.<span class="property">data</span></span><br><span class="line">        <span class="variable language_">this</span>.<span class="property">ruleForm</span>.<span class="property">sex</span> = res.<span class="property">data</span>.<span class="property">sex</span> === <span class="string">&#x27;0&#x27;</span> ? <span class="string">&#x27;女&#x27;</span> : <span class="string">&#x27;男&#x27;</span></span><br><span class="line">        <span class="comment">// this.ruleForm.password = &#x27;&#x27;</span></span><br><span class="line">        &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">        <span class="variable language_">this</span>.<span class="property">$message</span>.<span class="title function_">error</span>(res.<span class="property">msg</span> || <span class="string">&#x27;操作失败&#x27;</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><ol><li>点击保存按钮，发送ajax请求，将页面中的员工信息以json形式提交给服务端</li></ol><div class="tabs" id="编辑员工信息保存"><ul class="nav-tabs"><li class="tab active"><button type="button" data-href="#编辑员工信息保存-1">保存按钮</button></li><li class="tab"><button type="button" data-href="#编辑员工信息保存-2">submitForm</button></li><li class="tab"><button type="button" data-href="#编辑员工信息保存-3">editEmployee</button></li></ul><div class="tab-contents"><div class="tab-item-content active" id="编辑员工信息保存-1"><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><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">&lt;el-button</span><br><span class="line">    type=<span class="string">&quot;primary&quot;</span></span><br><span class="line">    @click=<span class="string">&quot;submitForm(&#x27;ruleForm&#x27;, false)&quot;</span></span><br><span class="line">&gt;</span><br><span class="line">    保存</span><br><span class="line">&lt;/el-button&gt;</span><br></pre></td></tr></table></figure><button type="button" class="tab-to-top" aria-label="scroll to top"><i class="fas fa-arrow-up"></i></button></div><div class="tab-item-content" id="编辑员工信息保存-2"><p>可以看出添加和修改用的是一个表单来提交数据<br><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><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="title function_">submitForm</span> (formName, st) &#123;</span><br><span class="line">    <span class="variable language_">this</span>.<span class="property">$refs</span>[formName].<span class="title function_">validate</span>(<span class="function">(<span class="params">valid</span>) =&gt;</span> &#123;</span><br><span class="line">        <span class="keyword">if</span> (valid) &#123;</span><br><span class="line">        <span class="keyword">if</span> (<span class="variable language_">this</span>.<span class="property">actionType</span> === <span class="string">&#x27;add&#x27;</span>) &#123;</span><br><span class="line">            <span class="keyword">const</span> params = &#123;</span><br><span class="line">            ...<span class="variable language_">this</span>.<span class="property">ruleForm</span>,</span><br><span class="line">            <span class="attr">sex</span>: <span class="variable language_">this</span>.<span class="property">ruleForm</span>.<span class="property">sex</span> === <span class="string">&#x27;女&#x27;</span> ? <span class="string">&#x27;0&#x27;</span> : <span class="string">&#x27;1&#x27;</span></span><br><span class="line">            &#125;</span><br><span class="line">            <span class="title function_">addEmployee</span>(params).<span class="title function_">then</span>(<span class="function"><span class="params">res</span> =&gt;</span> &#123;</span><br><span class="line">            <span class="keyword">if</span> (res.<span class="property">code</span> === <span class="number">1</span>) &#123;</span><br><span class="line">                <span class="variable language_">this</span>.<span class="property">$message</span>.<span class="title function_">success</span>(<span class="string">&#x27;员工添加成功！&#x27;</span>)</span><br><span class="line">                <span class="keyword">if</span> (!st) &#123;</span><br><span class="line">                <span class="variable language_">this</span>.<span class="title function_">goBack</span>()</span><br><span class="line">                &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">                <span class="variable language_">this</span>.<span class="property">ruleForm</span> = &#123;</span><br><span class="line">                    <span class="attr">username</span>: <span class="string">&#x27;&#x27;</span>,</span><br><span class="line">                    <span class="string">&#x27;name&#x27;</span>: <span class="string">&#x27;&#x27;</span>,</span><br><span class="line">                    <span class="string">&#x27;phone&#x27;</span>: <span class="string">&#x27;&#x27;</span>,</span><br><span class="line">                    <span class="comment">// &#x27;password&#x27;: &#x27;&#x27;,</span></span><br><span class="line">                    <span class="comment">// &#x27;rePassword&#x27;: &#x27;&#x27;,/</span></span><br><span class="line">                    <span class="string">&#x27;sex&#x27;</span>: <span class="string">&#x27;男&#x27;</span>,</span><br><span class="line">                    <span class="string">&#x27;idNumber&#x27;</span>: <span class="string">&#x27;&#x27;</span></span><br><span class="line">                &#125;</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="variable language_">this</span>.<span class="property">$message</span>.<span class="title function_">error</span>(res.<span class="property">msg</span> || <span class="string">&#x27;操作失败&#x27;</span>)</span><br><span class="line">            &#125;</span><br><span class="line">            &#125;).<span class="title function_">catch</span>(<span class="function"><span class="params">err</span> =&gt;</span> &#123;</span><br><span class="line">            <span class="variable language_">this</span>.<span class="property">$message</span>.<span class="title function_">error</span>(<span class="string">&#x27;请求出错了：&#x27;</span> + err)</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">const</span> params = &#123;</span><br><span class="line">            ...<span class="variable language_">this</span>.<span class="property">ruleForm</span>,</span><br><span class="line">            <span class="attr">sex</span>: <span class="variable language_">this</span>.<span class="property">ruleForm</span>.<span class="property">sex</span> === <span class="string">&#x27;女&#x27;</span> ? <span class="string">&#x27;0&#x27;</span> : <span class="string">&#x27;1&#x27;</span></span><br><span class="line">            &#125;</span><br><span class="line">            <span class="title function_">editEmployee</span>(params).<span class="title function_">then</span>(<span class="function"><span class="params">res</span> =&gt;</span> &#123;</span><br><span class="line">            <span class="keyword">if</span> (res.<span class="property">code</span> === <span class="number">1</span>) &#123;</span><br><span class="line">                <span class="variable language_">this</span>.<span class="property">$message</span>.<span class="title function_">success</span>(<span class="string">&#x27;员工信息修改成功！&#x27;</span>)</span><br><span class="line">                <span class="variable language_">this</span>.<span class="title function_">goBack</span>()</span><br><span class="line">            &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">                <span class="variable language_">this</span>.<span class="property">$message</span>.<span class="title function_">error</span>(res.<span class="property">msg</span> || <span class="string">&#x27;操作失败&#x27;</span>)</span><br><span class="line">            &#125;</span><br><span class="line">            &#125;).<span class="title function_">catch</span>(<span class="function"><span class="params">err</span> =&gt;</span> &#123;</span><br><span class="line">            <span class="variable language_">this</span>.<span class="property">$message</span>.<span class="title function_">error</span>(<span class="string">&#x27;请求出错了：&#x27;</span> + err)</span><br><span class="line">            &#125;)</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="variable language_">console</span>.<span class="title function_">log</span>(<span class="string">&#x27;error submit!!&#x27;</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">    &#125;)</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></p><button type="button" class="tab-to-top" aria-label="scroll to top"><i class="fas fa-arrow-up"></i></button></div><div class="tab-item-content" id="编辑员工信息保存-3"><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><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="keyword">function</span> <span class="title function_">editEmployee</span> (<span class="params">params</span>) &#123;</span><br><span class="line">  <span class="keyword">return</span> $axios(&#123;</span><br><span class="line">    <span class="attr">url</span>: <span class="string">&#x27;/employee&#x27;</span>,</span><br><span class="line">    <span class="attr">method</span>: <span class="string">&#x27;put&#x27;</span>,</span><br><span class="line">    <span class="attr">data</span>: &#123; ...params &#125;</span><br><span class="line">  &#125;)</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><button type="button" class="tab-to-top" aria-label="scroll to top"><i class="fas fa-arrow-up"></i></button></div></div></div><ol><li>服务端接受信息后，再次调用前面写的update方法，对员工信息进行修改</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="meta">@PutMapping</span></span><br><span class="line"><span class="keyword">public</span> Result&lt;String&gt; <span class="title function_">update</span><span class="params">(<span class="meta">@RequestBody</span> Employee employee, HttpServletRequest request)</span> &#123;</span><br><span class="line">    log.info(employee.toString());</span><br><span class="line">    <span class="type">Long</span> <span class="variable">id</span> <span class="operator">=</span> (Long) request.getSession().getAttribute(<span class="string">&quot;employee&quot;</span>);</span><br><span class="line">    employee.setUpdateUser(id);</span><br><span class="line">    employee.setUpdateTime(LocalDateTime.now());</span><br><span class="line">    employeeService.updateById(employee);</span><br><span class="line">    <span class="keyword">return</span> Result.success(<span class="string">&quot;员工信息修改成功&quot;</span>);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><ol><li>员工信息修改成功之后，调用<code>goBack</code>函数，跳转至员工管理页面<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><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="title function_">goBack</span>(<span class="params"></span>)&#123;</span><br><span class="line">    <span class="variable language_">window</span>.<span class="property">parent</span>.<span class="title function_">menuHandle</span>(&#123;</span><br><span class="line">        <span class="attr">id</span>: <span class="string">&#x27;2&#x27;</span>,</span><br><span class="line">        <span class="attr">url</span>: <span class="string">&#x27;/backend/page/member/list.html&#x27;</span>,</span><br><span class="line">        <span class="attr">name</span>: <span class="string">&#x27;员工管理&#x27;</span></span><br><span class="line">    &#125;,<span class="literal">false</span>)</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></li></ol><h2 id="公共字段自动填充"><a href="#公共字段自动填充" class="headerlink" title="公共字段自动填充"></a>公共字段自动填充</h2><h3 id="问题引出"><a href="#问题引出" class="headerlink" title="问题引出"></a>问题引出</h3><ul><li>前面完成了对员工数据的添加与修改，在添加/修改员工数据的时候，需要指定创建人、创建时间、修改人、修改时间等字段，而这些字段又属于公共字段，不仅员工表有这些字段，在菜品表、分类表等其他表中，也拥有这些字段。</li><li>有没有办法让这些字段在一个地方统一管理，以此来简化开发呢？<ul><li>答案是使用<code>MybatisPlus</code>提供的公共字段自动填充功能</li></ul></li></ul><h3 id="代码实现"><a href="#代码实现" class="headerlink" title="代码实现"></a>代码实现</h3><ul><li><p>实现步骤</p><ol><li><p>在实体类的属性上方加入<code>@TableFiled</code>注解，指定自动填充的策略</p><div class="tabs" id="公共字段添加注解"><ul class="nav-tabs"><li class="tab active"><button type="button" data-href="#公共字段添加注解-1">修改实体类</button></li><li class="tab"><button type="button" data-href="#公共字段添加注解-2">FieldFill</button></li></ul><div class="tab-contents"><div class="tab-item-content active" id="公共字段添加注解-1"><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><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="meta">@Data</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">Employee</span> <span class="keyword">implements</span> <span class="title class_">Serializable</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> <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> 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="keyword">private</span> String name;</span><br><span class="line"></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> String phone;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">private</span> String sex;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">private</span> String idNumber;<span class="comment">//身份证号码</span></span><br><span class="line"></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">@TableField(fill = FieldFill.INSERT)</span></span><br><span class="line">    <span class="keyword">private</span> LocalDateTime createTime;</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 updateTime;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@TableField(fill = FieldFill.INSERT)</span></span><br><span class="line">    <span class="keyword">private</span> Long createUser;</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> Long updateUser;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><button type="button" class="tab-to-top" aria-label="scroll to top"><i class="fas fa-arrow-up"></i></button></div><div class="tab-item-content" id="公共字段添加注解-2"><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><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">public</span> <span class="keyword">enum</span> <span class="title class_">FieldFill</span> &#123;</span><br><span class="line">    DEFAULT,</span><br><span class="line">    INSERT,</span><br><span class="line">    UPDATE,</span><br><span class="line">    INSERT_UPDATE;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">private</span> <span class="title function_">FieldFill</span><span class="params">()</span> &#123;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><button type="button" class="tab-to-top" aria-label="scroll to top"><i class="fas fa-arrow-up"></i></button></div></div></div></li><li><p>按照框架要求编写元数据对象处理器，在此类中统一对公共字段赋值，此类需要实现<code>MetaObjectHandler</code>接口,实现接口之后，重写两个方法，一个是插入时填充，一个是修改时填充<br>关于字段填充方式，使用metaObject的<code>setValue</code>来实现<br>关于id的获取，我们之前是存到session里的，但在<code>MyMetaObjectHandler</code>类中不能获得HttpSession对象，所以我们需要用其他方式来获取登录用户Id</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></pre></td><td class="code"><pre><span class="line"><span class="meta">@Component</span></span><br><span class="line"><span class="meta">@Slf4j</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">MyMetaObjectHandler</span> <span class="keyword">implements</span> <span class="title class_">MetaObjectHandler</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_">insertFill</span><span class="params">(MetaObject metaObject)</span> &#123;</span><br><span class="line">        log.info(<span class="string">&quot;公共字段自动填充(insert)...&quot;</span>);</span><br><span class="line">        log.info(metaObject.toString());</span><br><span class="line">        metaObject.setValue(<span class="string">&quot;createTime&quot;</span>, LocalDateTime.now());</span><br><span class="line">        metaObject.setValue(<span class="string">&quot;updateTime&quot;</span>, LocalDateTime.now());</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_">updateFill</span><span class="params">(MetaObject metaObject)</span> &#123;</span><br><span class="line">        log.info(<span class="string">&quot;公共字段自动填充(update)...&quot;</span>);</span><br><span class="line">        log.info(metaObject.toString());</span><br><span class="line">        metaObject.setValue(<span class="string">&quot;updateTime&quot;</span>, LocalDateTime.now());</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></li></ol></li></ul><h3 id="功能完善"><a href="#功能完善" class="headerlink" title="功能完善"></a>功能完善</h3><ul><li>现在已经能够填充时间和创建人两个公共字段了，但是还不能添加用户的id，因为之前是通过httpSession对象来获取的，但是现在不能使用session对象了，此时我们需要使用<code>ThreadLocal</code>来解决</li><li>在学习ThreadLocal之前，需要先确认一个事情，就是客户端发送的每次http请求，对应的在服务端都会分配一个新的线程来处理，在处理过程中涉及到下面类中的方法都属于相同的一个线程:<ol><li><code>LocalCheekFilter</code>中的<code>doFilter</code>方法</li><li><code>EmployeeController</code>中的<code>update</code>方法</li><li><code>MyMetaObjectHandler</code>中的<code>updateFill</code>方法</li></ol></li></ul><p>可以通过在这三个方法中添加日志输出测试，此处省略</p><p><strong>什么是ThreadLocal?</strong></p><ul><li>ThreadLocal并不是一个Thread，而是Thread的局部变量</li><li>当使用ThreadLocal维护变量时，ThreadLocal为每个使用该变量的线程提供独立的变量副本</li><li>所以每一个线程都可以独立地改变自己的副本，而不会影响其它线程所对应的副本</li><li>ThreadLocal为每个线程提供单独一份存储空间，具有<code>线程隔离</code>的效果，只有在线程内才能获取到对应的值，线程外则不能访问。</li></ul><p><strong>ThreadLocal常用方法:</strong></p><ul><li><code>public void set(T value)</code> 设置当前线程的线程局部变量的值</li><li><code>public T get()</code> 返回当前线程所对应的线程局部变量的值</li></ul><p>如何用ThreadLocal来解决上述的问题呢？</p><ul><li>我们可以在<code>LoginCheckFilter</code>的<code>doFilter</code>方法中获取当前登录用户id，并调用<code>ThreadLocal</code>的<code>set</code>方法来设置当前线程的线程局部变量的值(用户id)，然后在<code>MyMetaObjectHandler</code>的<code>updateFill</code>方法中调用<code>ThreadLocal</code>的<code>get</code>方法来获得当前线程所对应的线程局部变量的值(用户id)。</li></ul><p>具体实现</p><ul><li>在com.blog.common包下新建BaseContext类</li><li><p>作用：基于ThreadLocal的封装工具类，用于保护和获取当前用户id</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="keyword">public</span> <span class="keyword">class</span> <span class="title class_">BaseContext</span> &#123;</span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">static</span> ThreadLocal&lt;Long&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">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title function_">setCurrentId</span><span class="params">(Long id)</span> &#123;</span><br><span class="line">        threadLocal.set(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">static</span> Long <span class="title function_">getCurrentId</span><span class="params">()</span> &#123;</span><br><span class="line">        <span class="keyword">return</span> threadLocal.get();</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></li><li><p>随后在LoginCheckFilter类中添加代码,获取到id后保存到ThreadLocal中</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></pre></td><td class="code"><pre><span class="line"><span class="comment">//4.判断登录状态，如果已登录，则直接放行</span></span><br><span class="line"><span class="keyword">if</span> (request.getSession().getAttribute(<span class="string">&quot;employee&quot;</span>) != <span class="literal">null</span>) &#123;</span><br><span class="line">    log.info(<span class="string">&quot;用户已登录，id为&#123;&#125;&quot;</span>, request.getSession().getAttribute(<span class="string">&quot;employee&quot;</span>));</span><br><span class="line">    <span class="comment">//在这里获取一下线程id</span></span><br><span class="line">    <span class="type">long</span> <span class="variable">id</span> <span class="operator">=</span> Thread.currentThread().getId();</span><br><span class="line">    log.info(<span class="string">&quot;doFilter的线程id为：&#123;&#125;&quot;</span>, id);</span><br><span class="line">    <span class="comment">//根据session来获取之前我们存的id值</span></span><br><span class="line">    <span class="type">Long</span> <span class="variable">empId</span> <span class="operator">=</span> (Long) request.getSession().getAttribute(<span class="string">&quot;employee&quot;</span>);</span><br><span class="line">    <span class="comment">//使用BaseContext封装id</span></span><br><span class="line">    BaseContext.setCurrentId(empId);</span><br><span class="line">    filterChain.doFilter(request, response);</span><br><span class="line">    <span class="keyword">return</span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></li><li><p>在MyMetaObjectHandler类中，调整设置id的代码</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="meta">@Component</span></span><br><span class="line"><span class="meta">@Slf4j</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">MyMetaObjectHandler</span> <span class="keyword">implements</span> <span class="title class_">MetaObjectHandler</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_">insertFill</span><span class="params">(MetaObject metaObject)</span> &#123;</span><br><span class="line">        log.info(<span class="string">&quot;公共字段填充（create）...&quot;</span>);</span><br><span class="line">        metaObject.setValue(<span class="string">&quot;createTime&quot;</span>, LocalDateTime.now());</span><br><span class="line">        metaObject.setValue(<span class="string">&quot;updateTime&quot;</span>, LocalDateTime.now());</span><br><span class="line">        <span class="comment">//设置创建人id</span></span><br><span class="line">        metaObject.setValue(<span class="string">&quot;createUser&quot;</span>, BaseContext.getCurrentId());</span><br><span class="line">        metaObject.setValue(<span class="string">&quot;updateUser&quot;</span>, BaseContext.getCurrentId());</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_">updateFill</span><span class="params">(MetaObject metaObject)</span> &#123;</span><br><span class="line">        log.info(<span class="string">&quot;公共字段填充（insert）...&quot;</span>);</span><br><span class="line">        metaObject.setValue(<span class="string">&quot;updateTime&quot;</span>, LocalDateTime.now());</span><br><span class="line">        <span class="comment">//设置更新人id</span></span><br><span class="line">        metaObject.setValue(<span class="string">&quot;updateUser&quot;</span>, BaseContext.getCurrentId());</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></li></ul><h2 id="新增菜品分类"><a href="#新增菜品分类" class="headerlink" title="新增菜品分类"></a>新增菜品分类</h2><h3 id="需求分析-1"><a href="#需求分析-1" class="headerlink" title="需求分析"></a>需求分析</h3><ul><li>后台系统中可以管理分类信息，分类包括两种类型，分别是<code>菜品分类</code>和<code>套餐分类</code></li><li>当我们在后台系统中添加菜品时，需要选择一个菜品分类</li><li>当我们在后台系统中添加一个套餐时，需要选择一个套餐分类</li><li>在移动端也会按照菜品分类和套餐分类来展示对应的菜品和套餐</li></ul><p>可以在后台系统的分类管理页面分别添加菜品分类和套餐分类，如下</p><div class="tabs" id="新增菜品分析"><ul class="nav-tabs"><li class="tab active"><button type="button" data-href="#新增菜品分析-1">新增菜品分类</button></li><li class="tab"><button type="button" data-href="#新增菜品分析-2">新增套餐分类</button></li></ul><div class="tab-contents"><div class="tab-item-content active" id="新增菜品分析-1"><div class="img-wrap"><div class="img-bg"><img class="img" src="https://raw.githubusercontent.com/silvan2077/markdown_pic/main/Picgo/20251101161108.png"/></div></div><button type="button" class="tab-to-top" aria-label="scroll to top"><i class="fas fa-arrow-up"></i></button></div><div class="tab-item-content" id="新增菜品分析-2"><div class="img-wrap"><div class="img-bg"><img class="img" src="https://raw.githubusercontent.com/silvan2077/markdown_pic/main/Picgo/20251101161117.png"/></div></div><button type="button" class="tab-to-top" aria-label="scroll to top"><i class="fas fa-arrow-up"></i></button></div></div></div><h3 id="数据模型"><a href="#数据模型" class="headerlink" title="数据模型"></a>数据模型</h3><p>简单了解category表中的数据<br><div class="img-wrap"><div class="img-bg"><img class="img" src="https://raw.githubusercontent.com/silvan2077/markdown_pic/main/Picgo/20251101161421.png"/></div></div></p><div class="note info no-icon flat"><p>id是主键，name分类名称Unique，type为1表示菜品分类，type为2表示套餐分类</p></div><h3 id="准备工作-1"><a href="#准备工作-1" class="headerlink" title="准备工作"></a>准备工作</h3><p>开发业务之前，先将需要用到的类和接口的基本结构先创建好</p><ol><li><p>根据数据库信息来创建实体类Category</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></pre></td><td class="code"><pre><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_">Category</span> <span class="keyword">implements</span> <span class="title class_">Serializable</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> <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> Long id;</span><br><span class="line"></span><br><span class="line"></span><br><span class="line">    <span class="comment">//类型 1 菜品分类 2 套餐分类</span></span><br><span class="line">    <span class="keyword">private</span> Integer type;</span><br><span class="line"></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><br><span class="line"></span><br><span class="line"></span><br><span class="line">    <span class="comment">//顺序</span></span><br><span class="line">    <span class="keyword">private</span> Integer sort;</span><br><span class="line"></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 createTime;</span><br><span class="line"></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_UPDATE)</span></span><br><span class="line">    <span class="keyword">private</span> LocalDateTime updateTime;</span><br><span class="line"></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> Long createUser;</span><br><span class="line"></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_UPDATE)</span></span><br><span class="line">    <span class="keyword">private</span> Long updateUser;</span><br><span class="line"></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></li></ol><ul><li>创建接口和实现类<div class="tabs" id="新增菜品创建接口"><ul class="nav-tabs"><li class="tab active"><button type="button" data-href="#新增菜品创建接口-1">CategoryMapper</button></li><li class="tab"><button type="button" data-href="#新增菜品创建接口-2">CategoryMapperImpl</button></li><li class="tab"><button type="button" data-href="#新增菜品创建接口-3">CategoryService</button></li><li class="tab"><button type="button" data-href="#新增菜品创建接口-4">CategoryServiceImpl</button></li></ul><div class="tab-contents"><div class="tab-item-content active" id="新增菜品创建接口-1"><figure class="highlight java"><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="meta">@Mapper</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">interface</span> <span class="title class_">CategoryMapper</span> <span class="keyword">extends</span> <span class="title class_">BaseMapper</span>&lt;Category&gt; &#123;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><button type="button" class="tab-to-top" aria-label="scroll to top"><i class="fas fa-arrow-up"></i></button></div><div class="tab-item-content" id="新增菜品创建接口-2"><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="keyword">public</span> <span class="keyword">class</span> <span class="title class_">CategoryMapperImpl</span> &#123;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><button type="button" class="tab-to-top" aria-label="scroll to top"><i class="fas fa-arrow-up"></i></button></div><div class="tab-item-content" id="新增菜品创建接口-3"><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="keyword">public</span> <span class="keyword">interface</span> <span class="title class_">CategoryService</span> <span class="keyword">extends</span> <span class="title class_">IService</span>&lt;Category&gt; &#123;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><button type="button" class="tab-to-top" aria-label="scroll to top"><i class="fas fa-arrow-up"></i></button></div><div class="tab-item-content" id="新增菜品创建接口-4"><figure class="highlight java"><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="meta">@Service</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">CategoryServiceImpl</span> <span class="keyword">extends</span> <span class="title class_">ServiceImpl</span>&lt;CategoryMapper, Category&gt; <span class="keyword">implements</span> <span class="title class_">CategoryService</span> &#123;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><button type="button" class="tab-to-top" aria-label="scroll to top"><i class="fas fa-arrow-up"></i></button></div></div></div></li><li>创建控制层<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><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">@Slf4j</span></span><br><span class="line"><span class="meta">@RestController</span></span><br><span class="line"><span class="meta">@RequestMapping(&quot;/category&quot;)</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">CategoryController</span> &#123;</span><br><span class="line">    <span class="meta">@Autowired</span></span><br><span class="line">    <span class="keyword">private</span> CategoryService categoryService;</span><br><span class="line"></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></li></ul><h3 id="流程分析-2"><a href="#流程分析-2" class="headerlink" title="流程分析"></a>流程分析</h3><div class="note info no-icon flat"><p>分析整个流程</p><ol><li>页面发送ajax请求，将新增分类窗口输入的数据以json形式提交给服务端</li><li>服务端Controller接收页面提交的数据并调用Service将数据存储到数据库</li><li>Service调用Mapper操作数据库，保存数据</li></ol></div><p>我们先尝试监测一下前端给我们提供的是什么请求，以及会提交什么数据，打开开发者工具，监测NetWork，点击新增<code>菜品分类</code>表单的确定按钮</p><ul><li><p>请求方式</p><blockquote><p>请求网址: <a href="http://localhost/category">http://localhost/category</a><br>请求方法: POST</p></blockquote></li><li><p>json数据</p><blockquote><p>{name: “川菜”, type: “1”, sort: “10”}</p></blockquote></li></ul><p>点击新增<code>套餐分类</code>表单的确定按钮</p><ul><li><p>请求方式</p><blockquote><p>请求网址: <a href="http://localhost/category">http://localhost/category</a><br>请求方法: POST</p></blockquote></li><li><p>json数据</p><blockquote><p>{name: “好吃的套餐”, type: “2”, sort: “10”}</p></blockquote></li></ul><p>新增菜品分类和新增套餐分类请求的<code>服务端地址</code>和提交的<code>json数据</code>结构<code>相同</code>，所以服务端只需要提供一个方法就可以统一处理新增菜品和新增套餐</p><h3 id="代码实现-1"><a href="#代码实现-1" class="headerlink" title="代码实现"></a>代码实现</h3><p>接受数据后调用MybatisPlus提供的save方法保存数据即可，并返回成功添加的提示信息<br><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><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="meta">@PostMapping</span></span><br><span class="line"><span class="keyword">public</span> Result&lt;String&gt; <span class="title function_">save</span><span class="params">(<span class="meta">@RequestBody</span> Category category)</span> &#123;</span><br><span class="line">    log.info(<span class="string">&quot;category:&#123;&#125;&quot;</span>, category);</span><br><span class="line">    categoryService.save(category);</span><br><span class="line">    <span class="keyword">return</span> Result.success(<span class="string">&quot;新增分类成功&quot;</span>);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></p><ul><li><p>但通过查看前端代码，发现显示的信息在前端写死了，只要最后的状态码是成功状态码，则均显示<code>分类添加成功！</code></p><figure class="highlight js"><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="keyword">if</span> (res.<span class="property">code</span> === <span class="number">1</span>) &#123;</span><br><span class="line">    <span class="variable language_">this</span>.<span class="property">$message</span>.<span class="title function_">success</span>(<span class="string">&#x27;分类添加成功！&#x27;</span>)</span><br></pre></td></tr></table></figure></li><li><p>如果想要添加菜品和添加套餐显示不同的响应结果，可以按照如下方式修改代码</p></li></ul><div class="tabs" id="新增菜品修改响应结果"><ul class="nav-tabs"><li class="tab active"><button type="button" data-href="#新增菜品修改响应结果-1">前端代码</button></li><li class="tab"><button type="button" data-href="#新增菜品修改响应结果-2">后端代码</button></li></ul><div class="tab-contents"><div class="tab-item-content active" id="新增菜品修改响应结果-1"><p>将<br><figure class="highlight js"><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="keyword">if</span> (res.<span class="property">code</span> === <span class="number">1</span>) &#123;</span><br><span class="line">    <span class="variable language_">this</span>.<span class="property">$message</span>.<span class="title function_">success</span>(res.<span class="property">data</span>)</span><br></pre></td></tr></table></figure></p><button type="button" class="tab-to-top" aria-label="scroll to top"><i class="fas fa-arrow-up"></i></button></div><div class="tab-item-content" id="新增菜品修改响应结果-2"><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">return</span> Result.success(category.getType() == <span class="number">1</span> ? <span class="string">&quot;添加菜品分类成功！&quot;</span> : <span class="string">&quot;添加套餐分类成功！&quot;</span>);</span><br></pre></td></tr></table></figure><button type="button" class="tab-to-top" aria-label="scroll to top"><i class="fas fa-arrow-up"></i></button></div></div></div><div class="note info no-icon flat"><p>建表时设置的<code>name</code>字段为<code>unique</code>唯一，如果尝试添加重复的<code>name</code>，则会报错,但在前面的全局异常处理器中已经处理了，所以这里我们不需要处理</p></div><h2 id="分类信息分页查询"><a href="#分类信息分页查询" class="headerlink" title="分类信息分页查询"></a>分类信息分页查询</h2><p>与员工信息分页查询类似，只是查询的表不同</p><h3 id="流程分析-3"><a href="#流程分析-3" class="headerlink" title="流程分析"></a>流程分析</h3><p>分析流程：</p><ol><li>页面发送Ajax请求，将分页查询的参数（page、pageSize）提交到服务端</li><li>服务端Controller接受到页面提交的数据之后，调用Service进行查询</li><li>Service调用Mapper操作数据库，查询分页数据</li><li>Controller将查询到的分页数据响应给页面</li><li>页面接收分页数据，并通过ElementUI的Table组件展示到页面上</li></ol><h3 id="前端代码分析-1"><a href="#前端代码分析-1" class="headerlink" title="前端代码分析"></a>前端代码分析</h3><p>跟前面基本一致，简单回顾</p><div class="tabs" id="菜品分页查询"><ul class="nav-tabs"><li class="tab active"><button type="button" data-href="#菜品分页查询-1">created()</button></li><li class="tab"><button type="button" data-href="#菜品分页查询-2">init()</button></li><li class="tab"><button type="button" data-href="#菜品分页查询-3">getCategoryPage()</button></li></ul><div class="tab-contents"><div class="tab-item-content active" id="菜品分页查询-1"><p>页面加载完毕之后调用created钩子函数<br>钩子函数内又调用的是init进行初始化<br><figure class="highlight js"><table><tr><td class="gutter"><pre><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><br><span class="line"><span class="title function_">created</span>(<span class="params"></span>) &#123;</span><br><span class="line">    <span class="variable language_">this</span>.<span class="title function_">init</span>()</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></p><button type="button" class="tab-to-top" aria-label="scroll to top"><i class="fas fa-arrow-up"></i></button></div><div class="tab-item-content" id="菜品分页查询-2"><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><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">async</span> <span class="title function_">init</span> () &#123;</span><br><span class="line">    <span class="keyword">await</span> <span class="title function_">getCategoryPage</span>(&#123;<span class="string">&#x27;page&#x27;</span>: <span class="variable language_">this</span>.<span class="property">page</span>, <span class="string">&#x27;pageSize&#x27;</span>: <span class="variable language_">this</span>.<span class="property">pageSize</span>&#125;).<span class="title function_">then</span>(<span class="function"><span class="params">res</span> =&gt;</span> &#123;</span><br><span class="line">        <span class="keyword">if</span> (<span class="title class_">String</span>(res.<span class="property">code</span>) === <span class="string">&#x27;1&#x27;</span>) &#123;</span><br><span class="line">        <span class="comment">//将服务端查询到的数据赋给tableData，然后就能看到了</span></span><br><span class="line">        <span class="variable language_">this</span>.<span class="property">tableData</span> = res.<span class="property">data</span>.<span class="property">records</span></span><br><span class="line">        <span class="variable language_">this</span>.<span class="property">counts</span> = <span class="title class_">Number</span>(res.<span class="property">data</span>.<span class="property">total</span>)</span><br><span class="line">        &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">        <span class="variable language_">this</span>.<span class="property">$message</span>.<span class="title function_">error</span>(res.<span class="property">msg</span> || <span class="string">&#x27;操作失败&#x27;</span>)</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;).<span class="title function_">catch</span>(<span class="function"><span class="params">err</span> =&gt;</span> &#123;</span><br><span class="line">        <span class="variable language_">this</span>.<span class="property">$message</span>.<span class="title function_">error</span>(<span class="string">&#x27;请求出错了：&#x27;</span> + err)</span><br><span class="line">    &#125;)</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><button type="button" class="tab-to-top" aria-label="scroll to top"><i class="fas fa-arrow-up"></i></button></div><div class="tab-item-content" id="菜品分页查询-3"><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><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="keyword">const</span> <span class="title function_">getCategoryPage</span> = (<span class="params">params</span>) =&gt; &#123;</span><br><span class="line">  <span class="keyword">return</span> $axios(&#123;</span><br><span class="line">    <span class="attr">url</span>: <span class="string">&#x27;/category/page&#x27;</span>,</span><br><span class="line">    <span class="attr">method</span>: <span class="string">&#x27;get&#x27;</span>,</span><br><span class="line">    params</span><br><span class="line">  &#125;)</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><button type="button" class="tab-to-top" aria-label="scroll to top"><i class="fas fa-arrow-up"></i></button></div></div></div><h3 id="代码实现-2"><a href="#代码实现-2" class="headerlink" title="代码实现"></a>代码实现</h3><p>因为前面写员工分页查询，相关配置已经完成，所以直接在CategoryController类中编写page方法即可</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">@GetMapping(&quot;/page&quot;)</span></span><br><span class="line"><span class="keyword">public</span> Result&lt;Page&gt; <span class="title function_">page</span><span class="params">(<span class="type">int</span> page, <span class="type">int</span> pageSize)</span> &#123;</span><br><span class="line">    <span class="comment">//分页构造器</span></span><br><span class="line">    Page&lt;Category&gt; pageInfo = <span class="keyword">new</span> <span class="title class_">Page</span>&lt;&gt;(page, pageSize);</span><br><span class="line">    <span class="comment">//条件查询器</span></span><br><span class="line">    LambdaQueryWrapper&lt;Category&gt; queryWrapper = <span class="keyword">new</span> <span class="title class_">LambdaQueryWrapper</span>&lt;&gt;();</span><br><span class="line">    <span class="comment">//添加排序条件</span></span><br><span class="line">    queryWrapper.orderByDesc(Category::getSort);</span><br><span class="line">    <span class="comment">//分页查询</span></span><br><span class="line">    categoryService.page(pageInfo, queryWrapper);</span><br><span class="line">    <span class="keyword">return</span> Result.success(pageInfo);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>查看效果<br><div class="img-wrap"><div class="img-bg"><img class="img" src="https://raw.githubusercontent.com/silvan2077/markdown_pic/main/Picgo/20251101170834.png"/></div></div></p><h2 id="删除分类"><a href="#删除分类" class="headerlink" title="删除分类"></a>删除分类</h2><h3 id="需求分析-2"><a href="#需求分析-2" class="headerlink" title="需求分析"></a>需求分析</h3><ul><li>在分类管理列表页面，可以对某个分类进行删除操作</li><li>需要注意的是：当某一分类还有关联的菜品或者套餐时，该分类将不允许被删除</li></ul><div class="note info no-icon flat"><p>流程分析</p><ol><li>页面发送ajax请求，将参数(id)提交给服务端</li><li>服务端Controller接收页面提交的数据，并调用Service删除数据</li><li>Service调用Mapper操作数据库</li></ol></div><h3 id="后端代码实现"><a href="#后端代码实现" class="headerlink" title="后端代码实现"></a>后端代码实现</h3><p>在CategoryController类上添加delete方法</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></pre></td><td class="code"><pre><span class="line"><span class="meta">@DeleteMapping</span></span><br><span class="line"><span class="keyword">private</span> Result&lt;String&gt; <span class="title function_">delete</span><span class="params">(Long id)</span> &#123;</span><br><span class="line">    log.info(<span class="string">&quot;将被删除的id：&#123;&#125;&quot;</span>, id);</span><br><span class="line">    categoryService.removeById(id);</span><br><span class="line">    <span class="keyword">return</span> Result.success(<span class="string">&quot;分类信息删除成功&quot;</span>);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h3 id="前端代码分析-2"><a href="#前端代码分析-2" class="headerlink" title="前端代码分析"></a>前端代码分析</h3><div class="tabs" id="删除分类前端分析"><ul class="nav-tabs"><li class="tab active"><button type="button" data-href="#删除分类前端分析-1">删除按钮</button></li><li class="tab"><button type="button" data-href="#删除分类前端分析-2">deleteHandler()</button></li><li class="tab"><button type="button" data-href="#删除分类前端分析-3">deleteCategory()</button></li></ul><div class="tab-contents"><div class="tab-item-content active" id="删除分类前端分析-1"><p>点击删除按钮后会直接调用deleteHandler()方法<br><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><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">&lt;el-button</span><br><span class="line">    type=<span class="string">&quot;text&quot;</span></span><br><span class="line">    size=<span class="string">&quot;small&quot;</span></span><br><span class="line">    <span class="keyword">class</span>=<span class="string">&quot;delBut non&quot;</span></span><br><span class="line">    @click=<span class="string">&quot;deleteHandle(scope.row.id)&quot;</span></span><br><span class="line">&gt;</span><br><span class="line">    删除</span><br><span class="line">&lt;/el-button&gt;</span><br></pre></td></tr></table></figure></p><button type="button" class="tab-to-top" aria-label="scroll to top"><i class="fas fa-arrow-up"></i></button></div><div class="tab-item-content" id="删除分类前端分析-2"><p>返回提示信息，确认删除后调用deleteCategory()方法去发送删除命令<br><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><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="title function_">deleteHandle</span>(<span class="params">id</span>) &#123;</span><br><span class="line">    <span class="variable language_">this</span>.$confirm(<span class="string">&#x27;此操作将永久删除该文件, 是否继续?&#x27;</span>, <span class="string">&#x27;提示&#x27;</span>, &#123;</span><br><span class="line">        <span class="string">&#x27;confirmButtonText&#x27;</span>: <span class="string">&#x27;确定&#x27;</span>,</span><br><span class="line">        <span class="string">&#x27;cancelButtonText&#x27;</span>: <span class="string">&#x27;取消&#x27;</span>,</span><br><span class="line">        <span class="string">&#x27;type&#x27;</span>: <span class="string">&#x27;warning&#x27;</span></span><br><span class="line">    &#125;).<span class="title function_">then</span>(<span class="function">() =&gt;</span> &#123;</span><br><span class="line">        <span class="title function_">deleCategory</span>(id).<span class="title function_">then</span>(<span class="function"><span class="params">res</span> =&gt;</span> &#123;</span><br><span class="line">        <span class="keyword">if</span> (res.<span class="property">code</span> === <span class="number">1</span>) &#123;</span><br><span class="line">            <span class="variable language_">this</span>.<span class="property">$message</span>.<span class="title function_">success</span>(<span class="string">&#x27;删除成功！&#x27;</span>)</span><br><span class="line">            <span class="variable language_">this</span>.<span class="title function_">handleQuery</span>()</span><br><span class="line">        &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">            <span class="variable language_">this</span>.<span class="property">$message</span>.<span class="title function_">error</span>(res.<span class="property">msg</span> || <span class="string">&#x27;操作失败&#x27;</span>)</span><br><span class="line">        &#125;</span><br><span class="line">        &#125;).<span class="title function_">catch</span>(<span class="function"><span class="params">err</span> =&gt;</span> &#123;</span><br><span class="line">        <span class="variable language_">this</span>.<span class="property">$message</span>.<span class="title function_">error</span>(<span class="string">&#x27;请求出错了：&#x27;</span> + err)</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><button type="button" class="tab-to-top" aria-label="scroll to top"><i class="fas fa-arrow-up"></i></button></div><div class="tab-item-content" id="删除分类前端分析-3"><p>这里需要注意没有使用Restful风格，因为请求中为?id=xxx，还需要注意修改黑马提供的前端资料，路径为<code>backend/api/category.js</code>，将此处的<code>ids</code>改为<code>id</code>(两处都要修改)，然后清除缓存<br><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><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="keyword">const</span> <span class="title function_">deleCategory</span> = (<span class="params">id</span>) =&gt; &#123;</span><br><span class="line">  <span class="keyword">return</span> $axios(&#123;</span><br><span class="line">    <span class="attr">url</span>: <span class="string">&#x27;/category&#x27;</span>,</span><br><span class="line">    <span class="attr">method</span>: <span class="string">&#x27;delete&#x27;</span>,</span><br><span class="line">    <span class="attr">params</span>: &#123;id&#125;</span><br><span class="line">  &#125;)</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></p><button type="button" class="tab-to-top" aria-label="scroll to top"><i class="fas fa-arrow-up"></i></button></div></div></div><p>此时再重启服务器再次测试即可</p><div class="img-wrap"><div class="img-bg"><img class="img" src="https://raw.githubusercontent.com/silvan2077/markdown_pic/main/Picgo/20251101174023.png"/></div></div><h3 id="功能完善-1"><a href="#功能完善-1" class="headerlink" title="功能完善"></a>功能完善</h3><p>完成了单纯的删除操作，现在来完善功能，当菜品分类或套餐分类关联了其他菜品或套餐时，该分类将不允许被删除<br><div class="note info no-icon flat"><p>思路：删除时拿着当前分类的id值，去对应的菜品/套餐表中进行查询，如果能查询到数据，则说明该分类关联了菜品</p></div></p><p>代码完善：</p><ul><li>首先根据数据表创建菜品和套餐对应的模型类</li></ul><div class="tabs" id="dish"><ul class="nav-tabs"><li class="tab active"><button type="button" data-href="#dish-1">Dish</button></li><li class="tab"><button type="button" data-href="#dish-2">Setmeal</button></li></ul><div class="tab-contents"><div class="tab-item-content active" id="dish-1"><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><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="meta">@Data</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">Dish</span> <span class="keyword">implements</span> <span class="title class_">Serializable</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">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> Long id;</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><br><span class="line"></span><br><span class="line">    <span class="comment">//菜品分类id</span></span><br><span class="line">    <span class="keyword">private</span> Long categoryId;</span><br><span class="line"></span><br><span class="line">    <span class="comment">//菜品价格</span></span><br><span class="line">    <span class="keyword">private</span> BigDecimal price;</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 code;</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 image;</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 description;</span><br><span class="line"></span><br><span class="line">    <span class="comment">//0 停售 1 起售</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="comment">//顺序</span></span><br><span class="line">    <span class="keyword">private</span> Integer sort;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@TableField(fill = FieldFill.INSERT)</span></span><br><span class="line">    <span class="keyword">private</span> LocalDateTime createTime;</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 updateTime;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@TableField(fill = FieldFill.INSERT)</span></span><br><span class="line">    <span class="keyword">private</span> Long createUser;</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> Long updateUser;</span><br><span class="line"></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><button type="button" class="tab-to-top" aria-label="scroll to top"><i class="fas fa-arrow-up"></i></button></div><div class="tab-item-content" id="dish-2"><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><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">@Data</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">Setmeal</span> <span class="keyword">implements</span> <span class="title class_">Serializable</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> <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> Long id;</span><br><span class="line"></span><br><span class="line">    <span class="comment">//分类id</span></span><br><span class="line">    <span class="keyword">private</span> Long categoryId;</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><br><span class="line"></span><br><span class="line">    <span class="comment">//套餐价格</span></span><br><span class="line">    <span class="keyword">private</span> BigDecimal price;</span><br><span class="line"></span><br><span class="line">    <span class="comment">//状态 0:停用 1:启用</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="comment">//编码</span></span><br><span class="line">    <span class="keyword">private</span> String code;</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 description;</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 image;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@TableField(fill = FieldFill.INSERT)</span></span><br><span class="line">    <span class="keyword">private</span> LocalDateTime createTime;</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 updateTime;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@TableField(fill = FieldFill.INSERT)</span></span><br><span class="line">    <span class="keyword">private</span> Long createUser;</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> Long updateUser;</span><br><span class="line"></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><button type="button" class="tab-to-top" aria-label="scroll to top"><i class="fas fa-arrow-up"></i></button></div></div></div><ul><li>编写对应的Mapper接口</li></ul><div class="tabs" id="dishmapper"><ul class="nav-tabs"><li class="tab active"><button type="button" data-href="#dishmapper-1">DishMapper</button></li><li class="tab"><button type="button" data-href="#dishmapper-2">SetmealMapper</button></li></ul><div class="tab-contents"><div class="tab-item-content active" id="dishmapper-1"><figure class="highlight java"><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="meta">@Mapper</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">interface</span> <span class="title class_">DishMapper</span> <span class="keyword">extends</span> <span class="title class_">BaseMapper</span>&lt;Dish&gt; &#123;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><button type="button" class="tab-to-top" aria-label="scroll to top"><i class="fas fa-arrow-up"></i></button></div><div class="tab-item-content" id="dishmapper-2"><figure class="highlight java"><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="meta">@Mapper</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">interface</span> <span class="title class_">SetmealMapper</span> <span class="keyword">extends</span> <span class="title class_">BaseMapper</span>&lt;Setmeal&gt; &#123;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><button type="button" class="tab-to-top" aria-label="scroll to top"><i class="fas fa-arrow-up"></i></button></div></div></div><ul><li>编写对应的Service接口及Impl实现类</li></ul><div class="tabs" id="dishservice"><ul class="nav-tabs"><li class="tab active"><button type="button" data-href="#dishservice-1">DishService</button></li><li class="tab"><button type="button" data-href="#dishservice-2">DishServiceImpl</button></li><li class="tab"><button type="button" data-href="#dishservice-3">SetmealService</button></li><li class="tab"><button type="button" data-href="#dishservice-4">SetmealServiceImpl</button></li></ul><div class="tab-contents"><div class="tab-item-content active" id="dishservice-1"><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="keyword">public</span> <span class="keyword">interface</span> <span class="title class_">DishService</span> <span class="keyword">extends</span> <span class="title class_">IService</span>&lt;Dish&gt; &#123;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><button type="button" class="tab-to-top" aria-label="scroll to top"><i class="fas fa-arrow-up"></i></button></div><div class="tab-item-content" id="dishservice-2"><figure class="highlight java"><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="meta">@Service</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">DishServiceImpl</span> <span class="keyword">extends</span> <span class="title class_">ServiceImpl</span>&lt;DishMapper, Dish&gt; <span class="keyword">implements</span> <span class="title class_">DishService</span> &#123;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><button type="button" class="tab-to-top" aria-label="scroll to top"><i class="fas fa-arrow-up"></i></button></div><div class="tab-item-content" id="dishservice-3"><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="keyword">public</span> <span class="keyword">interface</span> <span class="title class_">SetmealService</span> <span class="keyword">extends</span> <span class="title class_">IService</span>&lt;Setmeal&gt; &#123;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><button type="button" class="tab-to-top" aria-label="scroll to top"><i class="fas fa-arrow-up"></i></button></div><div class="tab-item-content" id="dishservice-4"><figure class="highlight java"><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="meta">@Service</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">SetmealServiceImpl</span> <span class="keyword">extends</span> <span class="title class_">ServiceImpl</span>&lt;SetmealMapper, Setmeal&gt; <span class="keyword">implements</span> <span class="title class_">SetmealService</span> &#123;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><button type="button" class="tab-to-top" aria-label="scroll to top"><i class="fas fa-arrow-up"></i></button></div></div></div><ul><li>在common包下新增<code>CustomException</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></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">CustomException</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_">CustomException</span><span class="params">(String msg)</span>&#123;</span><br><span class="line">        <span class="built_in">super</span>(msg);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><ul><li>在全局异常处理器类中，添加上<code>CustomerException</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></pre></td><td class="code"><pre><span class="line"><span class="meta">@ExceptionHandler(CustomException.class)</span></span><br><span class="line"><span class="keyword">public</span> Result&lt;String&gt; <span class="title function_">exceptionHandler</span><span class="params">(CustomException exception)</span> &#123;</span><br><span class="line">    log.error(exception.getMessage());</span><br><span class="line">    <span class="keyword">return</span> Result.error(exception.getMessage());</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><ul><li>在CategoryService接口中自己写一个<code>remove</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></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">interface</span> <span class="title class_">CategoryService</span> <span class="keyword">extends</span> <span class="title class_">IService</span>&lt;Category&gt; &#123;</span><br><span class="line">    <span class="keyword">void</span> <span class="title function_">remove</span><span class="params">(Long id)</span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><ul><li><p>在CategoryServiceImpl中来写具体业务逻辑<br>我们需要在删除数据之前，根据<code>id</code>值，去<code>Dish</code>表和<code>Setmeal</code>表中查询是否管理数据<br>如果未查询到数据则代码不关联数据，则可以删除，查询到数据则代表不能删除并抛出异常</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">@Service</span></span><br><span class="line"><span class="meta">@Slf4j</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">CategoryServiceImpl</span> <span class="keyword">extends</span> <span class="title class_">ServiceImpl</span>&lt;CategoryMapper, Category&gt; <span class="keyword">implements</span> <span class="title class_">CategoryService</span> &#123;</span><br><span class="line">    <span class="meta">@Autowired</span></span><br><span class="line">    DishService dishService;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Autowired</span></span><br><span class="line">    SetmealService setmealService;</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 class="doctag">@param</span> id</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> <span class="keyword">void</span> <span class="title function_">remove</span><span class="params">(Long id)</span> &#123;</span><br><span class="line">        LambdaQueryWrapper&lt;Dish&gt; dishLambdaQueryWrapper = <span class="keyword">new</span> <span class="title class_">LambdaQueryWrapper</span>&lt;&gt;();</span><br><span class="line">        <span class="comment">//添加dish查询条件，根据分类id进行查询</span></span><br><span class="line">        dishLambdaQueryWrapper.eq(Dish::getCategoryId, id);</span><br><span class="line">        <span class="comment">//方便Debug用的</span></span><br><span class="line">        <span class="type">int</span> <span class="variable">count1</span> <span class="operator">=</span> dishService.count(dishLambdaQueryWrapper);</span><br><span class="line">        log.info(<span class="string">&quot;dish查询条件，查询到的条目数为：&#123;&#125;&quot;</span>,count1);</span><br><span class="line">        <span class="comment">//查看当前分类是否关联了菜品，如果已经关联，则抛出异常</span></span><br><span class="line">        <span class="keyword">if</span> (count1 &gt; <span class="number">0</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_">CustomException</span>(<span class="string">&quot;当前分类下关联了菜品，不能删除&quot;</span>);</span><br><span class="line">        &#125;</span><br><span class="line"></span><br><span class="line">        LambdaQueryWrapper&lt;Setmeal&gt; setmealLambdaQueryWrapper = <span class="keyword">new</span> <span class="title class_">LambdaQueryWrapper</span>&lt;&gt;();</span><br><span class="line">        <span class="comment">//添加dish查询条件，根据分类id进行查询</span></span><br><span class="line">        setmealLambdaQueryWrapper.eq(Setmeal::getCategoryId,id);</span><br><span class="line">        <span class="type">int</span> <span class="variable">count2</span> <span class="operator">=</span> setmealService.count(setmealLambdaQueryWrapper);</span><br><span class="line">        <span class="comment">//方便Debug用的</span></span><br><span class="line">        log.info(<span class="string">&quot;setmeal查询条件，查询到的条目数为：&#123;&#125;&quot;</span>,count2);</span><br><span class="line">        <span class="comment">//查看当前分类是否关联了套餐，如果已经关联，则抛出异常</span></span><br><span class="line">        <span class="keyword">if</span> (count2 &gt; <span class="number">0</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_">CustomException</span>(<span class="string">&quot;当前分类下关联了套餐，不能删除&quot;</span>);</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="comment">//正常删除</span></span><br><span class="line">        <span class="built_in">super</span>.removeById(id);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></li><li><p>最后在controller的删除方法中调用自己写的remove方法</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></pre></td><td class="code"><pre><span class="line"><span class="meta">@DeleteMapping</span></span><br><span class="line"><span class="keyword">public</span> Result&lt;String&gt; <span class="title function_">delete</span><span class="params">(Long id)</span>&#123;</span><br><span class="line">    log.info(<span class="string">&quot;将要删除的分类id:&#123;&#125;&quot;</span>,id);</span><br><span class="line">    categoryService.remove(id);</span><br><span class="line">    <span class="keyword">return</span> Result.success(<span class="string">&quot;分类信息删除成功&quot;</span>);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></li></ul><p>最终效果：<br><div class="img-wrap"><div class="img-bg"><img class="img" src="https://raw.githubusercontent.com/silvan2077/markdown_pic/main/Picgo/20251101180030.png"/></div></div></p><h2 id="修改分类"><a href="#修改分类" class="headerlink" title="修改分类"></a>修改分类</h2><h3 id="需求分析-3"><a href="#需求分析-3" class="headerlink" title="需求分析"></a>需求分析</h3><p>与修改员工信息类似，点击修改按钮后，在修改窗口回显信息，最后点击确认完成修改操作<br><div class="img-wrap"><div class="img-bg"><img class="img" src="https://raw.githubusercontent.com/silvan2077/markdown_pic/main/Picgo/20251101180949.png"/></div></div></p><h3 id="代码开发"><a href="#代码开发" class="headerlink" title="代码开发"></a>代码开发</h3><p>数据的回显效果由前端来实现，与员工信息修改大体相同，简略说一些即可</p><ul><li>修改按钮绑定了<code>editHandle</code>函数，一旦点击就会调用该函数，并以该行的数据作为参数</li><li>editHandle函数负责初始化表格中数据的值，也就是数据回显</li><li>表单通过v-model实现双向绑定<br>此处修改功能也和添加功能共用一个方法<div class="tabs" id="修改代码开发"><ul class="nav-tabs"><li class="tab active"><button type="button" data-href="#修改代码开发-1">submitForm</button></li><li class="tab"><button type="button" data-href="#修改代码开发-2">editCategory</button></li></ul><div class="tab-contents"><div class="tab-item-content active" id="修改代码开发-1"><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><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="title function_">submitForm</span>(<span class="params">st</span>) &#123;</span><br><span class="line">    <span class="keyword">const</span> classData = <span class="variable language_">this</span>.<span class="property">classData</span></span><br><span class="line">    <span class="keyword">const</span> valid = (classData.<span class="property">name</span> === <span class="number">0</span> ||classData.<span class="property">name</span>)  &amp;&amp; (classData.<span class="property">sort</span> === <span class="number">0</span> || classData.<span class="property">sort</span>)</span><br><span class="line">    <span class="keyword">if</span> (<span class="variable language_">this</span>.<span class="property">action</span> === <span class="string">&#x27;add&#x27;</span>) &#123;</span><br><span class="line">    <span class="keyword">if</span> (valid) &#123;</span><br><span class="line">        <span class="keyword">const</span> reg = <span class="regexp">/^\d+$/</span></span><br><span class="line">        <span class="keyword">if</span> (reg.<span class="title function_">test</span>(classData.<span class="property">sort</span>)) &#123;</span><br><span class="line">        <span class="title function_">addCategory</span>(&#123;<span class="string">&#x27;name&#x27;</span>: classData.<span class="property">name</span>,<span class="string">&#x27;type&#x27;</span>:<span class="variable language_">this</span>.<span class="property">type</span>, <span class="attr">sort</span>: classData.<span class="property">sort</span>&#125;).<span class="title function_">then</span>(<span class="function"><span class="params">res</span> =&gt;</span> &#123;</span><br><span class="line">            <span class="variable language_">console</span>.<span class="title function_">log</span>(res)</span><br><span class="line">            <span class="keyword">if</span> (res.<span class="property">code</span> === <span class="number">1</span>) &#123;</span><br><span class="line">            <span class="variable language_">this</span>.<span class="property">$message</span>.<span class="title function_">success</span>(<span class="string">&#x27;分类添加成功！&#x27;</span>)</span><br><span class="line">            <span class="keyword">if</span> (!st) &#123;</span><br><span class="line">                <span class="variable language_">this</span>.<span class="property">classData</span>.<span class="property">dialogVisible</span> = <span class="literal">false</span></span><br><span class="line">            &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">                <span class="variable language_">this</span>.<span class="property">classData</span>.<span class="property">name</span> = <span class="string">&#x27;&#x27;</span></span><br><span class="line">                <span class="variable language_">this</span>.<span class="property">classData</span>.<span class="property">sort</span> = <span class="string">&#x27;&#x27;</span></span><br><span class="line">            &#125;</span><br><span class="line">            <span class="variable language_">this</span>.<span class="title function_">handleQuery</span>()</span><br><span class="line">            &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">            <span class="variable language_">this</span>.<span class="property">$message</span>.<span class="title function_">error</span>(res.<span class="property">msg</span> || <span class="string">&#x27;操作失败&#x27;</span>)</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;).<span class="title function_">catch</span>(<span class="function"><span class="params">err</span> =&gt;</span> &#123;</span><br><span class="line">            <span class="variable language_">this</span>.<span class="property">$message</span>.<span class="title function_">error</span>(<span class="string">&#x27;请求出错了：&#x27;</span> + err)</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="variable language_">this</span>.<span class="property">$message</span>.<span class="title function_">error</span>(<span class="string">&#x27;排序只能输入数字类型&#x27;</span>)</span><br><span class="line">        &#125;</span><br><span class="line">        </span><br><span class="line">    &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">        <span class="variable language_">this</span>.<span class="property">$message</span>.<span class="title function_">error</span>(<span class="string">&#x27;请输入分类名称或排序&#x27;</span>)</span><br><span class="line">    &#125;</span><br><span class="line">&#125; <span class="keyword">else</span> <span class="keyword">if</span> (valid) &#123;</span><br><span class="line">    <span class="keyword">const</span> reg = <span class="regexp">/^\d+$/</span></span><br><span class="line">    <span class="keyword">if</span> (reg.<span class="title function_">test</span>(<span class="variable language_">this</span>.<span class="property">classData</span>.<span class="property">sort</span>)) &#123;</span><br><span class="line">    <span class="title function_">editCategory</span>(&#123;<span class="string">&#x27;id&#x27;</span>:<span class="variable language_">this</span>.<span class="property">classData</span>.<span class="property">id</span>,<span class="string">&#x27;name&#x27;</span>: <span class="variable language_">this</span>.<span class="property">classData</span>.<span class="property">name</span>, <span class="attr">sort</span>: <span class="variable language_">this</span>.<span class="property">classData</span>.<span class="property">sort</span>&#125;).<span class="title function_">then</span>(<span class="function"><span class="params">res</span> =&gt;</span> &#123;</span><br><span class="line">        <span class="keyword">if</span> (res.<span class="property">code</span> === <span class="number">1</span>) &#123;</span><br><span class="line">        <span class="variable language_">this</span>.<span class="property">$message</span>.<span class="title function_">success</span>(<span class="string">&#x27;分类修改成功！&#x27;</span>)</span><br><span class="line">        <span class="variable language_">this</span>.<span class="property">classData</span>.<span class="property">dialogVisible</span> = <span class="literal">false</span></span><br><span class="line">        <span class="variable language_">this</span>.<span class="title function_">handleQuery</span>()</span><br><span class="line">        &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">        <span class="variable language_">this</span>.<span class="property">$message</span>.<span class="title function_">error</span>(res.<span class="property">msg</span> || <span class="string">&#x27;操作失败&#x27;</span>)</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;).<span class="title function_">catch</span>(<span class="function"><span class="params">err</span> =&gt;</span> &#123;</span><br><span class="line">        <span class="variable language_">this</span>.<span class="property">$message</span>.<span class="title function_">error</span>(<span class="string">&#x27;请求出错了：&#x27;</span> + err)</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="variable language_">this</span>.<span class="property">$message</span>.<span class="title function_">error</span>(<span class="string">&#x27;排序只能输入数字类型&#x27;</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="variable language_">this</span>.<span class="property">$message</span>.<span class="title function_">error</span>(<span class="string">&#x27;请输入分类名称或排序&#x27;</span>)</span><br><span class="line">&#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><button type="button" class="tab-to-top" aria-label="scroll to top"><i class="fas fa-arrow-up"></i></button></div><div class="tab-item-content" id="修改代码开发-2"><p>添加操作是post请求，修改是发送PUT请求<br><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><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="keyword">const</span> <span class="title function_">editCategory</span> = (<span class="params">params</span>) =&gt; &#123;</span><br><span class="line">  <span class="keyword">return</span> $axios(&#123;</span><br><span class="line">    <span class="attr">url</span>: <span class="string">&#x27;/category&#x27;</span>,</span><br><span class="line">    <span class="attr">method</span>: <span class="string">&#x27;put&#x27;</span>,</span><br><span class="line">    <span class="attr">data</span>: &#123; ...params &#125;</span><br><span class="line">  &#125;)</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></p><button type="button" class="tab-to-top" aria-label="scroll to top"><i class="fas fa-arrow-up"></i></button></div></div></div></li></ul><ul><li>后端代码开发<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><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="meta">@PutMapping</span></span><br><span class="line"><span class="keyword">public</span> Result&lt;String&gt; <span class="title function_">update</span><span class="params">(<span class="meta">@RequestBody</span> Category category)</span> &#123;</span><br><span class="line">    log.info(<span class="string">&quot;修改分类信息为：&#123;&#125;&quot;</span>, category);</span><br><span class="line">    categoryService.updateById(category);</span><br><span class="line">    <span class="keyword">return</span> Result.success(<span class="string">&quot;修改分类信息成功&quot;</span>);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></li></ul><h2 id="文件上传与下载"><a href="#文件上传与下载" class="headerlink" title="文件上传与下载"></a>文件上传与下载</h2><h3 id="文件上传简介"><a href="#文件上传简介" class="headerlink" title="文件上传简介"></a>文件上传简介</h3><div class="note info no-icon flat"><p>文件上传，也叫<code>upload</code>，是指将本地图片、视频、音频等文件上传到服务器中，可以供其他用户浏览或下载的过程</p></div><ul><li><p>文件上传时，对页面的form表单有如下要求：</p><ol><li><code>method=&quot;post&quot;</code>，采用post方式提交数据</li><li><code>enctype=&quot;multipart/form-data&quot;</code>，采用multipart格式上传文件</li><li><code>type=&quot;file&quot;</code>，使用input的file控件上传</li></ol></li><li><p>举例</p><figure class="highlight html"><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><br><span class="line"><span class="tag">&lt;<span class="name">input</span> <span class="attr">type</span>=<span class="string">&quot;file&quot;</span>&gt;</span><span class="tag">&lt;<span class="name">br</span>&gt;</span></span><br></pre></td></tr></table></figure><p>头像：<br><input type="file"><br></p></li></ul><ul><li><p>目前一些前端组件库也提供了相应的上传组件，但是底层原理还是基于form表单的文件上传，这里直接使用提供好的组件就行了<br>把这段代码放在<code>backend/demo</code>目录下，命名为<code>upload.html</code></p><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><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="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;en&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">http-equiv</span>=<span class="string">&quot;X-UA-Compatible&quot;</span> <span class="attr">content</span>=<span class="string">&quot;IE=edge&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="comment">&lt;!-- 引入样式 --&gt;</span></span><br><span class="line">  <span class="tag">&lt;<span class="name">link</span> <span class="attr">rel</span>=<span class="string">&quot;stylesheet&quot;</span> <span class="attr">href</span>=<span class="string">&quot;../../plugins/element-ui/index.css&quot;</span> /&gt;</span></span><br><span class="line">  <span class="tag">&lt;<span class="name">link</span> <span class="attr">rel</span>=<span class="string">&quot;stylesheet&quot;</span> <span class="attr">href</span>=<span class="string">&quot;../../styles/common.css&quot;</span> /&gt;</span></span><br><span class="line">  <span class="tag">&lt;<span class="name">link</span> <span class="attr">rel</span>=<span class="string">&quot;stylesheet&quot;</span> <span class="attr">href</span>=<span class="string">&quot;../../styles/page.css&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">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;addBrand-container&quot;</span> <span class="attr">id</span>=<span class="string">&quot;food-add-app&quot;</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">el-upload</span> <span class="attr">class</span>=<span class="string">&quot;avatar-uploader&quot;</span></span></span><br><span class="line"><span class="tag">                <span class="attr">action</span>=<span class="string">&quot;/common/upload&quot;</span></span></span><br><span class="line"><span class="tag">                <span class="attr">:show-file-list</span>=<span class="string">&quot;false&quot;</span></span></span><br><span class="line"><span class="tag">                <span class="attr">:on-success</span>=<span class="string">&quot;handleAvatarSuccess&quot;</span></span></span><br><span class="line"><span class="tag">                <span class="attr">:before-upload</span>=<span class="string">&quot;beforeUpload&quot;</span></span></span><br><span class="line"><span class="tag">                <span class="attr">ref</span>=<span class="string">&quot;upload&quot;</span>&gt;</span></span><br><span class="line">            <span class="tag">&lt;<span class="name">img</span> <span class="attr">v-if</span>=<span class="string">&quot;imageUrl&quot;</span> <span class="attr">:src</span>=<span class="string">&quot;imageUrl&quot;</span> <span class="attr">class</span>=<span class="string">&quot;avatar&quot;</span>&gt;</span><span class="tag">&lt;/<span class="name">img</span>&gt;</span></span><br><span class="line">            <span class="tag">&lt;<span class="name">i</span> <span class="attr">v-else</span> <span class="attr">class</span>=<span class="string">&quot;el-icon-plus avatar-uploader-icon&quot;</span>&gt;</span><span class="tag">&lt;/<span class="name">i</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;/<span class="name">el-upload</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 class="tag">&lt;/<span class="name">div</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">script</span> <span class="attr">src</span>=<span class="string">&quot;../../plugins/vue/vue.js&quot;</span>&gt;</span><span class="tag">&lt;/<span class="name">script</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">script</span> <span class="attr">src</span>=<span class="string">&quot;../../plugins/element-ui/index.js&quot;</span>&gt;</span><span class="tag">&lt;/<span class="name">script</span>&gt;</span></span><br><span class="line">    <span class="comment">&lt;!-- 引入axios --&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">script</span> <span class="attr">src</span>=<span class="string">&quot;../../plugins/axios/axios.min.js&quot;</span>&gt;</span><span class="tag">&lt;/<span class="name">script</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">script</span> <span class="attr">src</span>=<span class="string">&quot;../../js/index.js&quot;</span>&gt;</span><span class="tag">&lt;/<span class="name">script</span>&gt;</span></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">new</span> <span class="title class_">Vue</span>(&#123;</span></span><br><span class="line"><span class="language-javascript">        <span class="attr">el</span>: <span class="string">&#x27;#food-add-app&#x27;</span>,</span></span><br><span class="line"><span class="language-javascript">        <span class="title function_">data</span>(<span class="params"></span>) &#123;</span></span><br><span class="line"><span class="language-javascript">          <span class="keyword">return</span> &#123;</span></span><br><span class="line"><span class="language-javascript">            <span class="attr">imageUrl</span>: <span class="string">&#x27;&#x27;</span></span></span><br><span class="line"><span class="language-javascript">          &#125;</span></span><br><span class="line"><span class="language-javascript">        &#125;,</span></span><br><span class="line"><span class="language-javascript">        <span class="attr">methods</span>: &#123;</span></span><br><span class="line"><span class="language-javascript">          <span class="title function_">handleAvatarSuccess</span> (response, file, fileList) &#123;</span></span><br><span class="line"><span class="language-javascript">              <span class="variable language_">this</span>.<span class="property">imageUrl</span> = <span class="string">`/common/download?name=<span class="subst">$&#123;response.data&#125;</span>`</span></span></span><br><span class="line"><span class="language-javascript">          &#125;,</span></span><br><span class="line"><span class="language-javascript">          <span class="title function_">beforeUpload</span> (file) &#123;</span></span><br><span class="line"><span class="language-javascript">            <span class="keyword">if</span>(file)&#123;</span></span><br><span class="line"><span class="language-javascript">              <span class="keyword">const</span> suffix = file.<span class="property">name</span>.<span class="title function_">split</span>(<span class="string">&#x27;.&#x27;</span>)[<span class="number">1</span>]</span></span><br><span class="line"><span class="language-javascript">              <span class="keyword">const</span> size = file.<span class="property">size</span> / <span class="number">1024</span> / <span class="number">1024</span> &lt; <span class="number">2</span></span></span><br><span class="line"><span class="language-javascript">              <span class="keyword">if</span>([<span class="string">&#x27;png&#x27;</span>,<span class="string">&#x27;jpeg&#x27;</span>,<span class="string">&#x27;jpg&#x27;</span>].<span class="title function_">indexOf</span>(suffix) &lt; <span class="number">0</span>)&#123;</span></span><br><span class="line"><span class="language-javascript">                <span class="variable language_">this</span>.<span class="property">$message</span>.<span class="title function_">error</span>(<span class="string">&#x27;上传图片只支持 png、jpeg、jpg 格式！&#x27;</span>)</span></span><br><span class="line"><span class="language-javascript">                <span class="variable language_">this</span>.<span class="property">$refs</span>.<span class="property">upload</span>.<span class="title function_">clearFiles</span>()</span></span><br><span class="line"><span class="language-javascript">                <span class="keyword">return</span> <span class="literal">false</span></span></span><br><span class="line"><span class="language-javascript">              &#125;</span></span><br><span class="line"><span class="language-javascript">              <span class="keyword">if</span>(!size)&#123;</span></span><br><span class="line"><span class="language-javascript">                <span class="variable language_">this</span>.<span class="property">$message</span>.<span class="title function_">error</span>(<span class="string">&#x27;上传文件大小不能超过 2MB!&#x27;</span>)</span></span><br><span class="line"><span class="language-javascript">                <span class="keyword">return</span> <span class="literal">false</span></span></span><br><span class="line"><span class="language-javascript">              &#125;</span></span><br><span class="line"><span class="language-javascript">              <span class="keyword">return</span> file</span></span><br><span class="line"><span class="language-javascript">            &#125;</span></span><br><span class="line"><span class="language-javascript">          &#125;</span></span><br><span class="line"><span class="language-javascript">        &#125;</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></li><li><p>服务端接收客户端页面上传的文件，通常都会使用Apache的两个组件:</p><ul><li><code>commons-fileupload</code></li><li><code>commons-io</code></li></ul></li><li><p>Spring框架在<code>spring-web</code>包中对文件上传进行了封装，大大简化了服务端代码，只需要在Controller的方法中声明一个MultipartFile类型的参数即可接收上传的文件，例如</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">@RestController</span></span><br><span class="line"><span class="meta">@RequestMapping(&quot;/common&quot;)</span></span><br><span class="line"><span class="meta">@Slf4j</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">CommonController</span> &#123;</span><br><span class="line">    <span class="meta">@PostMapping(&quot;/upload&quot;)</span></span><br><span class="line">    <span class="keyword">public</span> Result&lt;String&gt; <span class="title function_">upload</span><span class="params">(MultipartFile file)</span> &#123;</span><br><span class="line">        log.info(<span class="string">&quot;获取文件：&#123;&#125;&quot;</span>, file.toString());</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></li><li><p>启动服务器，登陆之后访问<a href="http://localhost/backend/page/demo/upload.html">http://localhost/backend/page/demo/upload.html</a>,传入一张图片，便能在日志上看到相关信息(需要先有过登录行为)</p><div class="img-wrap"><div class="img-bg"><img class="img" src="https://raw.githubusercontent.com/silvan2077/markdown_pic/main/Picgo/20251102144839.png"/></div></div></li></ul><h3 id="文件下载简介"><a href="#文件下载简介" class="headerlink" title="文件下载简介"></a>文件下载简介</h3><div class="note info no-icon flat"><ul><li>文件下载，也称download，是指将文件从服务器传输到本地计算机的过程</li></ul></div><ul><li>通过浏览器进行文件下载，通常有两种表现形式<ol><li>以附件形式下载，弹出保存对话框，将文件保存到指定磁盘目录</li><li>直接在浏览器中打开</li></ol></li><li>通过浏览器进行文件下载，本质上就是服务端将文件以流的形式写回浏览器的过程</li></ul><h3 id="文件上传代码实现"><a href="#文件上传代码实现" class="headerlink" title="文件上传代码实现"></a>文件上传代码实现</h3><ul><li><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></pre></td><td class="code"><pre><span class="line"><span class="comment">//定义不需要处理的请求</span></span><br><span class="line">String[] urls = <span class="keyword">new</span> <span class="title class_">String</span>[]&#123;</span><br><span class="line">        <span class="string">&quot;/employee/login&quot;</span>,</span><br><span class="line">        <span class="string">&quot;/employee/logout&quot;</span>,</span><br><span class="line">        <span class="string">&quot;/backend/**&quot;</span>,</span><br><span class="line">        <span class="string">&quot;/front/**&quot;</span>,</span><br><span class="line">        <span class="string">&quot;/common/**&quot;</span></span><br><span class="line">&#125;;</span><br></pre></td></tr></table></figure></li><li><p>随后使用<code>transferTo</code>方法将上传的临时文件转存到指定位置</p></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></pre></td><td class="code"><pre><span class="line"><span class="meta">@RestController</span></span><br><span class="line"><span class="meta">@RequestMapping(&quot;/common&quot;)</span></span><br><span class="line"><span class="meta">@Slf4j</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">CommonController</span> &#123;</span><br><span class="line">    <span class="meta">@PostMapping(&quot;/upload&quot;)</span></span><br><span class="line">    <span class="comment">//file是个临时文件，我们在断点调试的时候可以看到，但是执行完整个方法之后就消失了</span></span><br><span class="line">    <span class="keyword">public</span> Result&lt;String&gt; <span class="title function_">upload</span><span class="params">(MultipartFile file)</span> &#123;</span><br><span class="line">        log.info(<span class="string">&quot;获取文件：&#123;&#125;&quot;</span>, file.toString());</span><br><span class="line">        <span class="comment">//方法会抛异常，我们这里用try/catch处理一下</span></span><br><span class="line">        <span class="keyword">try</span> &#123;</span><br><span class="line">            <span class="comment">//我们将其转存为E盘下的test.jpg</span></span><br><span class="line">            file.transferTo(<span class="keyword">new</span> <span class="title class_">File</span>(<span class="string">&quot;D:\\Test\\test.jpg&quot;</span>));</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>(e);</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><ul><li><p>此时可以通过在<a href="http://localhost:8080/backend/page/demo/upload.html">http://localhost:8080/backend/page/demo/upload.html</a>该网址内继续上传图片来测试功能是否能正常执行</p><div class="img-wrap"><div class="img-bg"><img class="img" src="https://raw.githubusercontent.com/silvan2077/markdown_pic/main/Picgo/20251102150817.png"/></div></div></li><li><p>文件转存的位置可以通过修改配置文件来动态改变：在application.yml文件中加入以下内容</p><figure class="highlight yml"><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="attr">reggie:</span></span><br><span class="line">  <span class="attr">path:</span> <span class="string">D:\\Test\\</span></span><br></pre></td></tr></table></figure></li><li><p>使用<code>@Value(&quot;$&#123;reggie.path&#125;&quot;)</code>读取到配置文件中的动态转存位置</p></li><li><p>使用<code>uuid方式</code>重新生成文件名，避免文件名重复造成文件覆盖</p></li><li><p>通过获取原文件名来截取文件后缀，并拼接成新的文件名</p></li></ul><div class="note warning no-icon flat"><p>注意事项：我们需要先判断一下文件目录是否存在，如果不存在则先创建</p></div><ul><li><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></pre></td><td class="code"><pre><span class="line"><span class="meta">@RestController</span></span><br><span class="line"><span class="meta">@RequestMapping(&quot;/common&quot;)</span></span><br><span class="line"><span class="meta">@Slf4j</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">CommonController</span> &#123;</span><br><span class="line"><span class="meta">@Value(&quot;$&#123;reggie.path&#125;&quot;)</span></span><br><span class="line">  <span class="keyword">private</span> String basePath;</span><br><span class="line"></span><br><span class="line">  <span class="meta">@PostMapping(&quot;/upload&quot;)</span></span><br><span class="line">  <span class="comment">//file是个临时文件，我们在断点调试的时候可以看到，但是执行完整个方法之后就消失了</span></span><br><span class="line">  <span class="keyword">public</span> Result&lt;String&gt; <span class="title function_">upload</span><span class="params">(MultipartFile file)</span> &#123;</span><br><span class="line">      log.info(<span class="string">&quot;获取文件：&#123;&#125;&quot;</span>, file.toString());</span><br><span class="line">      <span class="comment">//判断一下当前目录是否存在，不存在则创建</span></span><br><span class="line">      <span class="type">File</span> <span class="variable">dir</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">File</span>(basePath);</span><br><span class="line">      <span class="keyword">if</span> (!dir.exists()) &#123;</span><br><span class="line">          dir.mkdirs();</span><br><span class="line">      &#125;</span><br><span class="line"></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">originalFilename</span> <span class="operator">=</span> file.getOriginalFilename();</span><br><span class="line">      <span class="comment">//我们只需要获取一下格式后缀，取子串，起始点为最后一个.</span></span><br><span class="line">      <span class="type">String</span> <span class="variable">suffix</span> <span class="operator">=</span> originalFilename.substring(originalFilename.lastIndexOf(<span class="string">&quot;.&quot;</span>));</span><br><span class="line">      <span class="comment">//为了防止出现重复的文件名，我们需要使用UUID</span></span><br><span class="line">      <span class="type">String</span> <span class="variable">fileName</span> <span class="operator">=</span> UUID.randomUUID() + suffix;</span><br><span class="line">      log.info(<span class="string">&quot;文件保存路径：&#123;&#125;&quot;</span>, basePath + fileName);</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">          file.transferTo(<span class="keyword">new</span> <span class="title class_">File</span>(basePath + fileName));</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>(e);</span><br><span class="line">      &#125;</span><br><span class="line">      <span class="comment">//将文件名返回给前端，便于后期的开发</span></span><br><span class="line">      <span class="keyword">return</span> Result.success(fileName);</span><br><span class="line">  &#125;</span><br><span class="line">    </span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></li><li><p>重启服务器，随便上传一张图片，然后去对应的目录下看看是否有上传的图片</p></li><li><p>如果一切顺利的话，目录不存在则会自动创建，而且上传的图片也在文件夹下</p></li></ul><h3 id="文件下载代码实现"><a href="#文件下载代码实现" class="headerlink" title="文件下载代码实现"></a>文件下载代码实现</h3><h4 id="前端处理"><a href="#前端处理" class="headerlink" title="前端处理"></a>前端处理</h4><ul><li><p>前端页面的ElementUI的upload组件会在上传完图片后，触发img组件发送请求，服务端以流的方式（输出流）将文件写回给浏览器，在浏览器中展示图片</p><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><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">&lt;el-upload <span class="keyword">class</span>=<span class="string">&quot;avatar-uploader&quot;</span></span><br><span class="line">        action=<span class="string">&quot;/common/upload&quot;</span></span><br><span class="line">        :show-file-list=<span class="string">&quot;false&quot;</span></span><br><span class="line">        :on-success=<span class="string">&quot;handleAvatarSuccess&quot;</span></span><br><span class="line">        :before-upload=<span class="string">&quot;beforeUpload&quot;</span></span><br><span class="line">        ref=<span class="string">&quot;upload&quot;</span>&gt;</span><br><span class="line">    <span class="language-xml"><span class="tag">&lt;<span class="name">img</span> <span class="attr">v-if</span>=<span class="string">&quot;imageUrl&quot;</span> <span class="attr">:src</span>=<span class="string">&quot;imageUrl&quot;</span> <span class="attr">class</span>=<span class="string">&quot;avatar&quot;</span>&gt;</span><span class="tag">&lt;/<span class="name">img</span>&gt;</span></span></span><br><span class="line">    <span class="language-xml"><span class="tag">&lt;<span class="name">i</span> <span class="attr">v-else</span> <span class="attr">class</span>=<span class="string">&quot;el-icon-plus avatar-uploader-icon&quot;</span>&gt;</span><span class="tag">&lt;/<span class="name">i</span>&gt;</span></span></span><br><span class="line">&lt;/el-upload&gt;</span><br></pre></td></tr></table></figure></li><li><p>定义前端发送回显图片请求的地址<br>通过这个url我们可以看出，请求路径为<code>/common/download</code>，且发送的是GET请求</p><figure class="highlight js"><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="title function_">handleAvatarSuccess</span> (response, file, fileList) &#123;</span><br><span class="line">    <span class="variable language_">this</span>.<span class="property">imageUrl</span> = <span class="string">`/common/download?name=<span class="subst">$&#123;response.data&#125;</span>`</span></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></li></ul><h4 id="后端处理"><a href="#后端处理" class="headerlink" title="后端处理"></a>后端处理</h4><ul><li>在<code>CommonController</code>类中添加<code>download</code>方法<ol><li>通过输入流读取文件内容</li><li>通过输出流将文件写回浏览器，在浏览器展示图片</li><li>关闭输入输出流，释放资源</li></ol></li></ul><div class="tabs" id="下载文件后端处理"><ul class="nav-tabs"><li class="tab active"><button type="button" data-href="#下载文件后端处理-1">核心代码</button></li><li class="tab"><button type="button" data-href="#下载文件后端处理-2">完整代码</button></li></ul><div class="tab-contents"><div class="tab-item-content active" id="下载文件后端处理-1"><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><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="meta">@GetMapping(&quot;/download&quot;)</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">download</span><span class="params">(String name, HttpServletResponse response)</span> &#123;</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>(basePath + name);</span><br><span class="line">    <span class="type">ServletOutputStream</span> <span class="variable">os</span> <span class="operator">=</span> response.getOutputStream();</span><br><span class="line">    <span class="type">int</span> len;</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="keyword">while</span> ((len = fis.read(buffer)) != -<span class="number">1</span>) </span><br><span class="line">        os.write(buffer, <span class="number">0</span>, len);</span><br><span class="line">    fis.close();</span><br><span class="line">    os.close();</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><button type="button" class="tab-to-top" aria-label="scroll to top"><i class="fas fa-arrow-up"></i></button></div><div class="tab-item-content" id="下载文件后端处理-2"><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><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="meta">@GetMapping(&quot;/download&quot;)</span></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">download</span><span class="params">(String name, HttpServletResponse response)</span> &#123;</span><br><span class="line">        <span class="type">FileInputStream</span> <span class="variable">fis</span> <span class="operator">=</span> <span class="literal">null</span>;</span><br><span class="line">        <span class="type">ServletOutputStream</span> <span class="variable">os</span> <span class="operator">=</span> <span class="literal">null</span>;</span><br><span class="line">        <span class="keyword">try</span> &#123;</span><br><span class="line">            fis = <span class="keyword">new</span> <span class="title class_">FileInputStream</span>(basePath + name);</span><br><span class="line">            os = response.getOutputStream();</span><br><span class="line">            response.setContentType(<span class="string">&quot;image/jpeg&quot;</span>);</span><br><span class="line">            <span class="type">int</span> len;</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="keyword">while</span> ((len = fis.read(buffer)) != -<span class="number">1</span>)&#123;</span><br><span class="line">                os.write(buffer, <span class="number">0</span>, len);</span><br><span class="line">            &#125;</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>(e);</span><br><span class="line">        &#125; <span class="keyword">finally</span> &#123;</span><br><span class="line">            <span class="keyword">if</span> (fis != <span class="literal">null</span>) &#123;</span><br><span class="line"></span><br><span class="line">                <span class="keyword">try</span> &#123;</span><br><span class="line">                    fis.close();</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>(e);</span><br><span class="line">                &#125;</span><br><span class="line">            &#125;</span><br><span class="line">            <span class="keyword">if</span> (os != <span class="literal">null</span>) &#123;</span><br><span class="line">                <span class="keyword">try</span> &#123;</span><br><span class="line">                    os.close();</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>(e);</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><button type="button" class="tab-to-top" aria-label="scroll to top"><i class="fas fa-arrow-up"></i></button></div></div></div><ul><li>然后启动服务器，上传一张图片,就会发现图片直接展示在页面上了<div class="img-wrap"><div class="img-bg"><img class="img" src="https://raw.githubusercontent.com/silvan2077/markdown_pic/main/Picgo/20251102160841.png"/></div></div></li></ul><h2 id="新增菜品"><a href="#新增菜品" class="headerlink" title="新增菜品"></a>新增菜品</h2><h3 id="需求分析-4"><a href="#需求分析-4" class="headerlink" title="需求分析"></a>需求分析</h3><ol><li>后台系统中可以管理菜品信息，通过新增功能来添加一个新的菜品</li><li>在添加菜品时需要选择当前菜品所属的菜品分类，并且上传菜品图片</li><li>在移动端会按照菜品分类来展示对应的菜品信息（前端的活儿，跟后端没啥太大关系）</li></ol><div class="img-wrap"><div class="img-bg"><img class="img" src="https://raw.githubusercontent.com/silvan2077/markdown_pic/main/Picgo/20251101182818.png"/></div></div><h3 id="数据模型-1"><a href="#数据模型-1" class="headerlink" title="数据模型"></a>数据模型</h3><p>dish表，最后一条字段is_deleted是逻辑删除</p><div class="tabs" id="新增菜品数据模型"><ul class="nav-tabs"><li class="tab active"><button type="button" data-href="#新增菜品数据模型-1">Dish</button></li><li class="tab"><button type="button" data-href="#新增菜品数据模型-2">DishFlavor</button></li></ul><div class="tab-contents"><div class="tab-item-content active" id="新增菜品数据模型-1"><div class="img-wrap"><div class="img-bg"><img class="img" src="https://raw.githubusercontent.com/silvan2077/markdown_pic/main/Picgo/20251101183154.png"/></div></div><button type="button" class="tab-to-top" aria-label="scroll to top"><i class="fas fa-arrow-up"></i></button></div><div class="tab-item-content" id="新增菜品数据模型-2"><div class="img-wrap"><div class="img-bg"><img class="img" src="https://raw.githubusercontent.com/silvan2077/markdown_pic/main/Picgo/20251101183030.png"/></div></div><button type="button" class="tab-to-top" aria-label="scroll to top"><i class="fas fa-arrow-up"></i></button></div></div></div><h3 id="代码开发-1"><a href="#代码开发-1" class="headerlink" title="代码开发"></a>代码开发</h3><h4 id="准备工作-2"><a href="#准备工作-2" class="headerlink" title="准备工作"></a>准备工作</h4><ul><li>前面跟Dish有关的已经创建好了，接下来创建DishFlavor对应的实体类，Mapper接口，Service接口及其对应的实现类</li></ul><div class="tabs" id="新增菜品准备工作"><ul class="nav-tabs"><li class="tab active"><button type="button" data-href="#新增菜品准备工作-1">DishFlavor实体类</button></li><li class="tab"><button type="button" data-href="#新增菜品准备工作-2">DishFlavorMapper</button></li><li class="tab"><button type="button" data-href="#新增菜品准备工作-3">DishFlavorService</button></li><li class="tab"><button type="button" data-href="#新增菜品准备工作-4">DishFlavorServiceImpl</button></li></ul><div class="tab-contents"><div class="tab-item-content active" id="新增菜品准备工作-1"><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><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">@Data</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">DishFlavor</span> <span class="keyword">implements</span> <span class="title class_">Serializable</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> <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> Long id;</span><br><span class="line"></span><br><span class="line"></span><br><span class="line">    <span class="comment">//菜品id</span></span><br><span class="line">    <span class="keyword">private</span> Long dishId;</span><br><span class="line"></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><br><span class="line"></span><br><span class="line"></span><br><span class="line">    <span class="comment">//口味数据list</span></span><br><span class="line">    <span class="keyword">private</span> String value;</span><br><span class="line"></span><br><span class="line"></span><br><span class="line">    <span class="meta">@TableField(fill = FieldFill.INSERT)</span></span><br><span class="line">    <span class="keyword">private</span> LocalDateTime createTime;</span><br><span class="line"></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 updateTime;</span><br><span class="line"></span><br><span class="line"></span><br><span class="line">    <span class="meta">@TableField(fill = FieldFill.INSERT)</span></span><br><span class="line">    <span class="keyword">private</span> Long createUser;</span><br><span class="line"></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> Long updateUser;</span><br><span class="line"></span><br><span class="line"></span><br><span class="line">    <span class="comment">//是否删除</span></span><br><span class="line">    <span class="keyword">private</span> Integer isDeleted;</span><br><span class="line"></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><button type="button" class="tab-to-top" aria-label="scroll to top"><i class="fas fa-arrow-up"></i></button></div><div class="tab-item-content" id="新增菜品准备工作-2"><figure class="highlight java"><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="meta">@Mapper</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">interface</span> <span class="title class_">DishFlavorMapper</span> <span class="keyword">extends</span> <span class="title class_">BaseMapper</span>&lt;DishFlavor&gt; &#123;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><button type="button" class="tab-to-top" aria-label="scroll to top"><i class="fas fa-arrow-up"></i></button></div><div class="tab-item-content" id="新增菜品准备工作-3"><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="keyword">public</span> <span class="keyword">interface</span> <span class="title class_">DishFlavorService</span> <span class="keyword">extends</span> <span class="title class_">IService</span>&lt;DishFlavor&gt; &#123;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><button type="button" class="tab-to-top" aria-label="scroll to top"><i class="fas fa-arrow-up"></i></button></div><div class="tab-item-content" id="新增菜品准备工作-4"><figure class="highlight java"><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="meta">@Service</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">DishFlavorServiceImpl</span> <span class="keyword">extends</span> <span class="title class_">ServiceImpl</span>&lt;DishFlavorMapper, DishFlavor&gt; <span class="keyword">implements</span> <span class="title class_">DishFlavorService</span> &#123;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><button type="button" class="tab-to-top" aria-label="scroll to top"><i class="fas fa-arrow-up"></i></button></div></div></div><p>编写Controller层代码<br><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><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="meta">@RestController</span></span><br><span class="line"><span class="meta">@RequestMapping(&quot;/dish&quot;)</span></span><br><span class="line"><span class="meta">@Slf4j</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">DishController</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> DishService dishService;</span><br><span class="line">    <span class="meta">@Autowired</span></span><br><span class="line">    <span class="keyword">private</span> DishFlavorService dishFlavorService;</span><br><span class="line">    </span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></p><h4 id="查询分类数据"><a href="#查询分类数据" class="headerlink" title="查询分类数据"></a>查询分类数据</h4><div class="note info no-icon flat"><p>流程分析：</p><ol><li>页面（backend/page/food/add.html）发送ajax请求，请求服务端获取菜品分类数据并展示到下拉框中</li><li>页面发送请求进行图片上传，请求服务端将图片保存到服务器</li><li>页面发送请求进行图片下载，并回显上传的图片</li><li>点击保存按钮，发送ajax请求，将菜品相关数据以json形式提交到服务端</li></ol></div><p>接下来逐步完成这四个请求即可<br><div class="img-wrap"><div class="img-bg"><img class="img" src="https://raw.githubusercontent.com/silvan2077/markdown_pic/main/Picgo/20251103100456.png"/></div></div></p><div class="tabs" id="查询分类数据"><ul class="nav-tabs"><li class="tab active"><button type="button" data-href="#查询分类数据-1">钩子函数</button></li><li class="tab"><button type="button" data-href="#查询分类数据-2">getDishList</button></li><li class="tab"><button type="button" data-href="#查询分类数据-3">getCategoryList</button></li><li class="tab"><button type="button" data-href="#查询分类数据-4">下拉框</button></li></ul><div class="tab-contents"><div class="tab-item-content active" id="查询分类数据-1"><ul><li>调用getDishList方法来初始化表格，并通过传入的id来判断是添加菜品还是编辑菜品<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><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="title function_">created</span>(<span class="params"></span>) &#123;</span><br><span class="line">    <span class="variable language_">this</span>.<span class="title function_">getDishList</span>()</span><br><span class="line">    <span class="comment">// 口味临时数据</span></span><br><span class="line">    <span class="variable language_">this</span>.<span class="title function_">getFlavorListHand</span>()</span><br><span class="line">    <span class="variable language_">this</span>.<span class="property">id</span> = <span class="title function_">requestUrlParam</span>(<span class="string">&#x27;id&#x27;</span>)</span><br><span class="line">    <span class="variable language_">this</span>.<span class="property">actionType</span> = <span class="variable language_">this</span>.<span class="property">id</span> ? <span class="string">&#x27;edit&#x27;</span> : <span class="string">&#x27;add&#x27;</span></span><br><span class="line">    <span class="keyword">if</span> (<span class="variable language_">this</span>.<span class="property">id</span>) &#123;</span><br><span class="line">    <span class="variable language_">this</span>.<span class="title function_">init</span>()</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></li></ul><button type="button" class="tab-to-top" aria-label="scroll to top"><i class="fas fa-arrow-up"></i></button></div><div class="tab-item-content" id="查询分类数据-2"><p>根据响应的Code值来判断操作是否成功<br><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><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="title function_">getDishList</span> () &#123;</span><br><span class="line">    <span class="title function_">getCategoryList</span>(&#123; <span class="string">&#x27;type&#x27;</span>: <span class="number">1</span> &#125;).<span class="title function_">then</span>(<span class="function"><span class="params">res</span> =&gt;</span> &#123;</span><br><span class="line">        <span class="keyword">if</span> (res.<span class="property">code</span> === <span class="number">1</span>) &#123;</span><br><span class="line">        <span class="variable language_">this</span>.<span class="property">dishList</span> = res.<span class="property">data</span></span><br><span class="line">        &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">        <span class="variable language_">this</span>.<span class="property">$message</span>.<span class="title function_">error</span>(res.<span class="property">msg</span> || <span class="string">&#x27;操作失败&#x27;</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><button type="button" class="tab-to-top" aria-label="scroll to top"><i class="fas fa-arrow-up"></i></button></div><div class="tab-item-content" id="查询分类数据-3"><p>发送get请求来获取菜品分类列表<br><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><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="keyword">const</span> <span class="title function_">getCategoryList</span> = (<span class="params">params</span>) =&gt; &#123;</span><br><span class="line">  <span class="keyword">return</span> $axios(&#123;</span><br><span class="line">    <span class="attr">url</span>: <span class="string">&#x27;/category/list&#x27;</span>,</span><br><span class="line">    <span class="attr">method</span>: <span class="string">&#x27;get&#x27;</span>,</span><br><span class="line">    params</span><br><span class="line">  &#125;)</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></p><button type="button" class="tab-to-top" aria-label="scroll to top"><i class="fas fa-arrow-up"></i></button></div><div class="tab-item-content" id="查询分类数据-4"><p>使用<code>v-for</code>遍历菜品信息<br><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><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">&lt;el-select</span><br><span class="line">    v-model=<span class="string">&quot;ruleForm.categoryId&quot;</span></span><br><span class="line">    placeholder=<span class="string">&quot;请选择菜品分类&quot;</span></span><br><span class="line">&gt;</span><br><span class="line">    <span class="language-xml"><span class="tag">&lt;<span class="name">el-option</span> <span class="attr">v-for</span>=<span class="string">&quot;(item,index) in dishList&quot;</span> <span class="attr">:key</span>=<span class="string">&quot;index&quot;</span> <span class="attr">:label</span>=<span class="string">&quot;item.name&quot;</span> <span class="attr">:value</span>=<span class="string">&quot;item.id&quot;</span> /&gt;</span></span></span><br><span class="line">&lt;/el-select&gt;</span><br></pre></td></tr></table></figure></p><button type="button" class="tab-to-top" aria-label="scroll to top"><i class="fas fa-arrow-up"></i></button></div></div></div><ul><li><p>在<code>CategoryController</code>类中，添加list方法,查询菜品信息即可</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></pre></td><td class="code"><pre><span class="line"><span class="meta">@GetMapping(&quot;/list&quot;)</span></span><br><span class="line"><span class="keyword">public</span> Result&lt;List&lt;Category&gt;&gt; <span class="title function_">list</span><span class="params">(Category category)</span> &#123;</span><br><span class="line">    <span class="comment">//条件构造器</span></span><br><span class="line">    LambdaQueryWrapper&lt;Category&gt; queryWrapper = <span class="keyword">new</span> <span class="title class_">LambdaQueryWrapper</span>&lt;&gt;();</span><br><span class="line">    <span class="comment">//添加条件，这里只需要判断是否为菜品（type为1是菜品，type为2是套餐）</span></span><br><span class="line">    queryWrapper.eq(category.getType() != <span class="literal">null</span>,Category::getType,category.getType());</span><br><span class="line">    <span class="comment">//添加排序条件</span></span><br><span class="line">    queryWrapper.orderByAsc(Category::getSort).orderByDesc(Category::getUpdateTime);</span><br><span class="line">    <span class="comment">//查询数据</span></span><br><span class="line">    List&lt;Category&gt; list = categoryService.list(queryWrapper);</span><br><span class="line">    <span class="comment">//返回数据</span></span><br><span class="line">    <span class="keyword">return</span> Result.success(list);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></li></ul><h4 id="接收与回显图片"><a href="#接收与回显图片" class="headerlink" title="接收与回显图片"></a>接收与回显图片</h4><p>该功能前面已经在<code>CommonController</code>中添加了对应的<code>upload</code>和<code>download</code>方法，因此可以直接使用</p><h4 id="提交数据到服务端"><a href="#提交数据到服务端" class="headerlink" title="提交数据到服务端"></a>提交数据到服务端</h4><ul><li>先随便提交点数据测试一下<div class="img-wrap"><div class="img-bg"><img class="img" src="https://raw.githubusercontent.com/silvan2077/markdown_pic/main/Picgo/20251103104649.png"/></div></div></li><li>以下是浏览器发送的Json数据<div class="img-wrap"><div class="img-bg"><img class="img" src="https://raw.githubusercontent.com/silvan2077/markdown_pic/main/Picgo/20251103104821.png"/></div></div></li></ul><ul><li>此处可以发现价格变成了输入价格的100倍，这是前端在<code>submitForm</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><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><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">submitForm(formName, st) &#123;</span><br><span class="line">    <span class="built_in">this</span>.$refs[formName].validate((valid) =&gt; &#123;</span><br><span class="line">        <span class="keyword">if</span> (valid) &#123;</span><br><span class="line">        <span class="type">let</span> <span class="variable">params</span> <span class="operator">=</span> &#123;...<span class="built_in">this</span>.ruleForm&#125;</span><br><span class="line">        <span class="comment">// params.flavors = this.dishFlavors</span></span><br><span class="line">        params.status = <span class="built_in">this</span>.ruleForm ? <span class="number">1</span> : <span class="number">0</span></span><br><span class="line">        params.price *= <span class="number">100</span></span><br><span class="line">        params.categoryId = <span class="built_in">this</span>.ruleForm.categoryId</span><br><span class="line">        params.flavors = <span class="built_in">this</span>.dishFlavors.map(obj =&gt; (&#123; ...obj, value: JSON.stringify(obj.value) &#125;))</span><br><span class="line">        delete params.dishFlavors</span><br><span class="line">        <span class="title function_">if</span><span class="params">(!<span class="built_in">this</span>.imageUrl)</span>&#123;</span><br><span class="line">            <span class="built_in">this</span>.$message.error(<span class="string">&#x27;请上传菜品图片&#x27;</span>)</span><br><span class="line">            <span class="keyword">return</span> </span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">if</span> (<span class="built_in">this</span>.actionType == <span class="string">&#x27;add&#x27;</span>) &#123;</span><br><span class="line">            delete params.id</span><br><span class="line">            <span class="title function_">addDish</span><span class="params">(params)</span>.then(res =&gt; &#123;</span><br><span class="line">            <span class="keyword">if</span> (res.code === <span class="number">1</span>) &#123;</span><br><span class="line">                <span class="built_in">this</span>.$message.success(<span class="string">&#x27;菜品添加成功！&#x27;</span>)</span><br><span class="line">                <span class="keyword">if</span> (!st) &#123;</span><br><span class="line">                <span class="built_in">this</span>.goBack()</span><br><span class="line">                &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">                <span class="built_in">this</span>.dishFlavors = []</span><br><span class="line">                <span class="comment">// this.dishFlavorsData = []</span></span><br><span class="line">                <span class="built_in">this</span>.imageUrl = <span class="string">&#x27;&#x27;</span></span><br><span class="line">                <span class="built_in">this</span>.ruleForm = &#123;</span><br><span class="line">                    <span class="string">&#x27;name&#x27;</span>: <span class="string">&#x27;&#x27;</span>,</span><br><span class="line">                    <span class="string">&#x27;id&#x27;</span>: <span class="string">&#x27;&#x27;</span>,</span><br><span class="line">                    <span class="string">&#x27;price&#x27;</span>: <span class="string">&#x27;&#x27;</span>,</span><br><span class="line">                    <span class="string">&#x27;code&#x27;</span>: <span class="string">&#x27;&#x27;</span>,</span><br><span class="line">                    <span class="string">&#x27;image&#x27;</span>: <span class="string">&#x27;&#x27;</span>,</span><br><span class="line">                    <span class="string">&#x27;description&#x27;</span>: <span class="string">&#x27;&#x27;</span>,</span><br><span class="line">                    <span class="string">&#x27;dishFlavors&#x27;</span>: [],</span><br><span class="line">                    <span class="string">&#x27;status&#x27;</span>: <span class="literal">true</span>,</span><br><span class="line">                    categoryId: <span class="string">&#x27;&#x27;</span></span><br><span class="line">                &#125;</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="built_in">this</span>.$message.error(res.msg || <span class="string">&#x27;操作失败&#x27;</span>)</span><br><span class="line">            &#125;</span><br><span class="line">            &#125;).<span class="keyword">catch</span>(err =&gt; &#123;</span><br><span class="line">            <span class="built_in">this</span>.$message.error(<span class="string">&#x27;请求出错了：&#x27;</span> + err)</span><br><span class="line">            &#125;)</span><br><span class="line">        &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">            delete params.updateTime</span><br><span class="line">            <span class="title function_">editDish</span><span class="params">(params)</span>.then(res =&gt; &#123;</span><br><span class="line">            <span class="keyword">if</span> (res.code === <span class="number">1</span>) &#123;</span><br><span class="line">                <span class="built_in">this</span>.$message.success(<span class="string">&#x27;菜品修改成功！&#x27;</span>)</span><br><span class="line">                <span class="built_in">this</span>.goBack()</span><br><span class="line">            &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">                <span class="built_in">this</span>.$message.error(res.msg || <span class="string">&#x27;操作失败&#x27;</span>)</span><br><span class="line">            &#125;</span><br><span class="line">            &#125;).<span class="keyword">catch</span>(err =&gt; &#123;</span><br><span class="line">            <span class="built_in">this</span>.$message.error(<span class="string">&#x27;请求出错了：&#x27;</span> + err)</span><br><span class="line">            &#125;)</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">return</span> <span class="literal">false</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><ul><li><p>因为Dish实体类不满足接收flavor参数，即需要导入DishDto，用于封装页面提交的数据</p></li><li><p>DTO，全称为</p><figure class="highlight fortran"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">Data</span> <span class="built_in">Transfer</span> Object</span><br></pre></td></tr></table></figure><p>，即数据传输对象，一般用于展示层与服务层之间的数据传输。</p><figure class="highlight scala"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><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">@Data</span></span><br><span class="line">public <span class="class"><span class="keyword">class</span> <span class="title">DishDto</span> <span class="keyword">extends</span> <span class="title">Dish</span> </span>&#123;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">private</span> <span class="type">List</span>&lt;<span class="type">DishFlavor</span>&gt; flavors = <span class="keyword">new</span> <span class="type">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">private</span> <span class="type">String</span> categoryName;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">private</span> <span class="type">Integer</span> copies;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></li><li><p>在</p><figure class="highlight ebnf"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="attribute">DishController</span></span><br></pre></td></tr></table></figure><p>类中添加</p><figure class="highlight ebnf"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="attribute">save</span></span><br></pre></td></tr></table></figure><p>方法，重启服务器，断点调试一下看看是否封装好了数据</p><figure class="highlight less"><table><tr><td class="gutter"><pre><span class="line">1</span><br><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="variable">@PostMapping</span></span><br><span class="line">public Result&lt;String&gt; <span class="built_in">save</span>(<span class="variable">@RequestBody</span> DishDto dishDto) &#123;</span><br><span class="line">    <span class="selector-tag">log</span><span class="selector-class">.info</span>(<span class="string">&quot;接收到的数据为：&#123;&#125;&quot;</span>,dishDto);</span><br><span class="line">    <span class="selector-tag">return</span> <span class="selector-tag">null</span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></li></ul><p><a href="https://pic1.imgdb.cn/item/6342224816f2c2beb1de4001.jpg"><img src="data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7" alt="img"></a><br>从图中我们可以看出，DishFlavor中的dishId为null<br>但是我们需要对DishFlavor中的dishId进行赋值<br>所以我们要取出dishDto的dishId，然后对每一组flavor的dishId赋值</p><ul><li><p>这里进行一下小结，我们需要做的有以下几点</p><ul><li><p>将菜品数据保存到<code>dish</code>表</p></li><li><p>将菜品口味数据保存到<code>dish_flavor</code>表</p><ul><li>但是<code>dish_flavor</code>表中需要一个<code>dishId</code>字段值，这个字段值需要我们从<code>dishDto</code>中获取</li><li>获取方式为：取出<code>dishDto</code>的<code>dishId</code>，对每一组<code>flavor</code>的<code>dishId</code>赋值</li></ul></li></ul></li><li><p>梳理完毕之后，那么我们就在<code>DishFlavorService</code>中编写一个<code>saveWithFlavor</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></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">interface</span> <span class="title class_">DishService</span> <span class="keyword">extends</span> <span class="title class_">IService</span>&lt;Dish&gt; &#123;</span><br><span class="line">    <span class="keyword">void</span> <span class="title function_">saveWithFlavor</span><span class="params">(DishDto dishDto)</span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></li><li><p>同时在<code>DishFlavorServiceImpl</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></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_">DishServiceImpl</span> <span class="keyword">extends</span> <span class="title class_">ServiceImpl</span>&lt;DishMapper, Dish&gt; <span class="keyword">implements</span> <span class="title class_">DishService</span> &#123;</span><br><span class="line">    <span class="meta">@Autowired</span></span><br><span class="line">    <span class="keyword">private</span> DishFlavorService dishFlavorService;</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_">saveWithFlavor</span><span class="params">(DishDto dishDto)</span> &#123;</span><br><span class="line">        <span class="comment">//将菜品数据保存到dish表</span></span><br><span class="line">        <span class="built_in">this</span>.save(dishDto);</span><br><span class="line">        <span class="comment">//获取dishId</span></span><br><span class="line">        <span class="type">Long</span> <span class="variable">dishId</span> <span class="operator">=</span> dishDto.getId();</span><br><span class="line">        <span class="comment">//将获取到的dishId赋值给dishFlavor的dishId属性</span></span><br><span class="line">        List&lt;DishFlavor&gt; flavors = dishDto.getFlavors();</span><br><span class="line">        <span class="keyword">for</span> (DishFlavor dishFlavor : flavors) &#123;</span><br><span class="line">            dishFlavor.setDishId(dishId);</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="comment">//同时将菜品口味数据保存到dish_flavor表</span></span><br><span class="line">        dishFlavorService.saveBatch(flavors);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>最后别忘了改Controller层的方法</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></pre></td><td class="code"><pre><span class="line"><span class="meta">@PostMapping</span></span><br><span class="line">  <span class="keyword">public</span> Result&lt;String&gt; <span class="title function_">save</span><span class="params">(<span class="meta">@RequestBody</span> DishDto dishDto)</span> &#123;</span><br><span class="line">      log.info(<span class="string">&quot;接收到的数据为：&#123;&#125;&quot;</span>,dishDto);</span><br><span class="line">      dishService.saveWithFlavor(dishDto);</span><br><span class="line">      <span class="keyword">return</span> Result.success(<span class="string">&quot;菜品添加成功&quot;</span>);</span><br><span class="line">  &#125;</span><br></pre></td></tr></table></figure></li></ul><h2 id="菜品信息分页查询"><a href="#菜品信息分页查询" class="headerlink" title="菜品信息分页查询"></a>菜品信息分页查询</h2><h3 id="需求分析-5"><a href="#需求分析-5" class="headerlink" title="需求分析"></a>需求分析</h3><ul><li>与之前一样，如果将所有数据直接在土匪页面展示出来会显得非常乱，不易于查看，所以需要以分页的方式展示列表数据，其中图片列和菜品分类列比较特殊<ul><li>图片列：会用到文件的下载功能</li><li>菜品分类列：由于菜品表只保存了category_id，所以需要查询category_id对应的菜品分类名称，从而回显数据</li></ul></li></ul><div class="img-wrap"><div class="img-bg"><img class="img" src="https://raw.githubusercontent.com/silvan2077/markdown_pic/main/Picgo/20251103112815.png"/></div></div><div class="note info no-icon flat"><p>梳理流程：</p><ol><li>页面(backend/page/food/list.html)发送ajax请求，将分页查询参数(<code>page</code>、<code>pageSize</code>、<code>name</code>)，提交到服务端，获取分页数据</li><li>页面发送请求，请求服务端进行图片下载，用于页面图片展示</li></ol></div><h3 id="代码开发-2"><a href="#代码开发-2" class="headerlink" title="代码开发"></a>代码开发</h3><p>开发分页功能只需要编写代码实现前端发送的两次请求即可</p><ul><li>在<code>DishController</code>下添加<code>page</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><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="meta">@GetMapping(&quot;/page&quot;)</span></span><br><span class="line"><span class="keyword">public</span> Result&lt;Page&gt; <span class="title function_">page</span><span class="params">(<span class="type">int</span> page, <span class="type">int</span> pageSize, String name)</span> &#123;</span><br><span class="line">    <span class="comment">//构造分页构造器对象</span></span><br><span class="line">    Page&lt;Dish&gt; pageInfo = <span class="keyword">new</span> <span class="title class_">Page</span>&lt;&gt;(page, pageSize);</span><br><span class="line">    <span class="comment">//条件构造器</span></span><br><span class="line">    LambdaQueryWrapper&lt;Dish&gt; queryWrapper = <span class="keyword">new</span> <span class="title class_">LambdaQueryWrapper</span>&lt;&gt;();</span><br><span class="line">    <span class="comment">//添加条件</span></span><br><span class="line">    queryWrapper.like(name != <span class="literal">null</span>, Dish::getName, name);</span><br><span class="line">    queryWrapper.orderByDesc(Dish::getUpdateTime);</span><br><span class="line">    <span class="comment">//执行分页查询</span></span><br><span class="line">    dishService.page(pageInfo, queryWrapper);</span><br><span class="line">    <span class="keyword">return</span> Result.success(pageInfo);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><ul><li>重启服务器可以发现，此时数据能够分页展示，但是图片和菜品分类的数据都没有展示<ul><li>图片只需要将黑马提供的图片资源放到存放图片的目录下即可<div class="img-wrap"><div class="img-bg"><img class="img" src="https://raw.githubusercontent.com/silvan2077/markdown_pic/main/Picgo/20251103113344.png"/></div></div></li></ul></li></ul><ul><li><p>为什么没有菜品分类数据？</p><ul><li>我们传递的是一个Dish对象，dish对象只有菜品分类id，没有菜品分类名称属性，前端是根据分类名称来填写，所以暂时没有分类数据</li><li>可以根据这个菜品分类id，去菜品分类表中查询对应的菜品分类名称</li></ul></li><li><p>此时可以使用DishDto类中的另外一个属性，返回一个DishDto对象就有菜品分类名称数据了</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">@Data</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">DishDto</span> <span class="keyword">extends</span> <span class="title class_">Dish</span> &#123;</span><br><span class="line">    <span class="comment">//菜品口味</span></span><br><span class="line">    <span class="keyword">private</span> List&lt;DishFlavor&gt; flavors = <span class="keyword">new</span> <span class="title class_">ArrayList</span>&lt;&gt;();</span><br><span class="line">    <span class="comment">//菜品分类名称</span></span><br><span class="line">    <span class="keyword">private</span> String categoryName;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">private</span> Integer copies;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></li><li><p>DishDto直接继承了Dish，所以可以看作是Dish类额外添加了一个categoryName属性。</p><div class="note info no-icon flat"><p>实现思路：将查询的dish数据传递给dishDto，再根据dish中的分类id去菜品分类表查询对应的category_name，将其赋值给dishDto的categoryName属性。</p></div></li></ul><ul><li>修改Controller层中的page方法<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><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">@GetMapping(&quot;/page&quot;)</span></span><br><span class="line"><span class="keyword">public</span> Result&lt;Page&gt; <span class="title function_">page</span><span class="params">(<span class="type">int</span> page, <span class="type">int</span> pageSize, String name)</span> &#123;</span><br><span class="line">    <span class="comment">//构造分页构造器对象</span></span><br><span class="line">    Page&lt;Dish&gt; pageInfo = <span class="keyword">new</span> <span class="title class_">Page</span>&lt;&gt;(page, pageSize);</span><br><span class="line">    <span class="comment">//这个就是我们到时候返回的结果</span></span><br><span class="line">    Page&lt;DishDto&gt; dishDtoPage = <span class="keyword">new</span> <span class="title class_">Page</span>&lt;&gt;(page, pageSize);</span><br><span class="line">    <span class="comment">//条件构造器</span></span><br><span class="line">    LambdaQueryWrapper&lt;Dish&gt; queryWrapper = <span class="keyword">new</span> <span class="title class_">LambdaQueryWrapper</span>&lt;&gt;();</span><br><span class="line">    <span class="comment">//添加条件</span></span><br><span class="line">    queryWrapper.like(name != <span class="literal">null</span>, Dish::getName, name);</span><br><span class="line">    queryWrapper.orderByDesc(Dish::getUpdateTime);</span><br><span class="line">    <span class="comment">//执行分页查询</span></span><br><span class="line">    dishService.page(pageInfo, queryWrapper);</span><br><span class="line"></span><br><span class="line">    <span class="comment">//对象拷贝，这里只需要拷贝一下查询到的条目数</span></span><br><span class="line">    BeanUtils.copyProperties(pageInfo, dishDtoPage, <span class="string">&quot;records&quot;</span>);</span><br><span class="line"></span><br><span class="line">    <span class="comment">//获取原records数据</span></span><br><span class="line">    List&lt;Dish&gt; records = pageInfo.getRecords();</span><br><span class="line"></span><br><span class="line">    <span class="comment">//遍历每一条records数据</span></span><br><span class="line">    List&lt;DishDto&gt; list = records.stream().map((item) -&gt; &#123;</span><br><span class="line">        <span class="type">DishDto</span> <span class="variable">dishDto</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">DishDto</span>();</span><br><span class="line">        <span class="comment">//将数据赋给dishDto对象</span></span><br><span class="line">        BeanUtils.copyProperties(item, dishDto);</span><br><span class="line">        <span class="comment">//然后获取一下dish对象的category_id属性</span></span><br><span class="line">        <span class="type">Long</span> <span class="variable">categoryId</span> <span class="operator">=</span> item.getCategoryId();  <span class="comment">//分类id</span></span><br><span class="line">        <span class="comment">//根据这个属性，获取到Category对象（这里需要用@Autowired注入一个CategoryService对象）</span></span><br><span class="line">        <span class="type">Category</span> <span class="variable">category</span> <span class="operator">=</span> categoryService.getById(categoryId);</span><br><span class="line">        <span class="comment">//随后获取Category对象的name属性，也就是菜品分类名称</span></span><br><span class="line">        <span class="type">String</span> <span class="variable">categoryName</span> <span class="operator">=</span> category.getName();</span><br><span class="line">        <span class="comment">//最后将菜品分类名称赋给dishDto对象就好了</span></span><br><span class="line">        dishDto.setCategoryName(categoryName);</span><br><span class="line">        <span class="comment">//结果返回一个dishDto对象</span></span><br><span class="line">        <span class="keyword">return</span> dishDto;</span><br><span class="line">        <span class="comment">//并将dishDto对象封装成一个集合，作为我们的最终结果</span></span><br><span class="line">    &#125;).collect(Collectors.toList());</span><br><span class="line"></span><br><span class="line">    dishDtoPage.setRecords(list);</span><br><span class="line">    <span class="keyword">return</span> Result.success(dishDtoPage);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></li></ul><h2 id="修改菜品"><a href="#修改菜品" class="headerlink" title="修改菜品"></a>修改菜品</h2><h3 id="梳理交互过程"><a href="#梳理交互过程" class="headerlink" title="梳理交互过程"></a>梳理交互过程</h3><p>梳理流程:</p><ol><li>页面发送ajax请求，请求服务器获取分类数据，用于菜品分类下拉框的数据回显（之前我们已经实现过了）</li><li>页面发送ajax请求，请求服务端，根据id查询当前菜品信息，用于菜品信息回显</li><li>页面发送请求，请求服务端进行图片下载，用于页面图片回显（之前我们已经实现过了）</li><li>点击保存按钮，页面发送ajax请求，将修改后的菜品相关数据以json形式提交到服务端</li></ol><div class="img-wrap"><div class="img-bg"><img class="img" src="https://raw.githubusercontent.com/silvan2077/markdown_pic/main/Picgo/20251103155937.png"/></div></div><p>开发修改菜品功能，其实就是在服务端写代码去处理以上四次请求</p><h3 id="查询菜品信息"><a href="#查询菜品信息" class="headerlink" title="查询菜品信息"></a>查询菜品信息</h3><ul><li><p>先获取修改行的数据，根据id来查询到对应菜品信息进行回显</p></li><li><p>含有菜品口味属性，所以还是要用到DishDto</p></li><li><p>先在service层编写一个<code>getByIdWithFlavor</code>方法，根据<code>dish_id</code>去<code>dish_flavor</code>表中查询，将查询到的菜品口味数据赋给我们的<code>DishDto</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></pre></td><td class="code"><pre><span class="line"><span class="meta">@Override</span></span><br><span class="line"><span class="keyword">public</span> DishDto <span class="title function_">getByIdWithFlavor</span><span class="params">(Long id)</span> &#123;</span><br><span class="line">    <span class="comment">//先根据id查询到对应的dish对象</span></span><br><span class="line">    <span class="type">Dish</span> <span class="variable">dish</span> <span class="operator">=</span> <span class="built_in">this</span>.getById(id);</span><br><span class="line">    <span class="comment">//创建一个dishDao对象</span></span><br><span class="line">    <span class="type">DishDto</span> <span class="variable">dishDto</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">DishDto</span>();</span><br><span class="line">    <span class="comment">//拷贝对象</span></span><br><span class="line">    BeanUtils.copyProperties(dish, dishDto);</span><br><span class="line">    <span class="comment">//条件构造器，对DishFlavor表查询</span></span><br><span class="line">    LambdaQueryWrapper&lt;DishFlavor&gt; queryWrapper = <span class="keyword">new</span> <span class="title class_">LambdaQueryWrapper</span>&lt;&gt;();</span><br><span class="line">    <span class="comment">//根据dish_id来查询对应的菜品口味数据</span></span><br><span class="line">    queryWrapper.eq(DishFlavor::getDishId, id);</span><br><span class="line">    <span class="comment">//获取查询的结果</span></span><br><span class="line">    List&lt;DishFlavor&gt; flavors = dishFlavorService.list(queryWrapper);</span><br><span class="line">    <span class="comment">//并将其赋给dishDto</span></span><br><span class="line">    dishDto.setFlavors(flavors);</span><br><span class="line">    <span class="comment">//作为结果返回给前端</span></span><br><span class="line">    <span class="keyword">return</span> dishDto;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></li><li><p>在<code>DishController</code>中添加get方法，实现添加在<code>DishServicelmpl</code></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></pre></td><td class="code"><pre><span class="line"><span class="meta">@GetMapping(&quot;/&#123;id&#125;&quot;)</span></span><br><span class="line"><span class="keyword">public</span> Result&lt;DishDto&gt; <span class="title function_">getByIdWithFlavor</span><span class="params">(<span class="meta">@PathVariable</span> Long id)</span> &#123;</span><br><span class="line">    <span class="type">DishDto</span> <span class="variable">dishDto</span> <span class="operator">=</span> dishService.getByIdWithFlavor(id);</span><br><span class="line">    log.info(<span class="string">&quot;查询到的数据为：&#123;&#125;&quot;</span>, dishDto);</span><br><span class="line">    <span class="keyword">return</span> Result.success(dishDto);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></li><li><p>此时重启服务器，就看到数据成功回显在表格中了</p><div class="img-wrap"><div class="img-bg"><img class="img" src="https://raw.githubusercontent.com/silvan2077/markdown_pic/main/Picgo/20251103163509.png"/></div></div><h3 id="修改菜品信息"><a href="#修改菜品信息" class="headerlink" title="修改菜品信息"></a>修改菜品信息</h3></li></ul><p>由于Dish表中没有Flavor这个属性，所以修改的时候依旧要通过<code>DishDto</code></p><div class="tabs" id="修改菜品信息前端"><ul class="nav-tabs"><li class="tab active"><button type="button" data-href="#修改菜品信息前端-1">修改按钮</button></li><li class="tab"><button type="button" data-href="#修改菜品信息前端-2">addFoodtype</button></li><li class="tab"><button type="button" data-href="#修改菜品信息前端-3">editDish</button></li></ul><div class="tab-contents"><div class="tab-item-content active" id="修改菜品信息前端-1"><p>修改菜品和添加菜品共用一个界面，根据参数来区分两种操作<br><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><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">&lt;el-button</span><br><span class="line">    type=<span class="string">&quot;text&quot;</span></span><br><span class="line">    size=<span class="string">&quot;small&quot;</span></span><br><span class="line">    <span class="keyword">class</span>=<span class="string">&quot;blueBug&quot;</span></span><br><span class="line">    @click=<span class="string">&quot;addFoodtype(scope.row.id)&quot;</span></span><br><span class="line">&gt;</span><br><span class="line">    修改</span><br><span class="line">&lt;/el-button&gt;</span><br></pre></td></tr></table></figure></p><button type="button" class="tab-to-top" aria-label="scroll to top"><i class="fas fa-arrow-up"></i></button></div><div class="tab-item-content" id="修改菜品信息前端-2"><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><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="title function_">addFoodtype</span> (st) &#123;</span><br><span class="line"><span class="keyword">if</span> (st === <span class="string">&#x27;add&#x27;</span>)&#123;</span><br><span class="line">    <span class="variable language_">window</span>.<span class="property">parent</span>.<span class="title function_">menuHandle</span>(&#123;</span><br><span class="line">    <span class="attr">id</span>: <span class="string">&#x27;4&#x27;</span>,</span><br><span class="line">    <span class="attr">url</span>: <span class="string">&#x27;/backend/page/food/add.html&#x27;</span>,</span><br><span class="line">    <span class="attr">name</span>: <span class="string">&#x27;添加菜品&#x27;</span></span><br><span class="line">    &#125;,<span class="literal">true</span>)</span><br><span class="line">&#125; <span class="keyword">else</span> &#123;</span><br><span class="line">    <span class="variable language_">window</span>.<span class="property">parent</span>.<span class="title function_">menuHandle</span>(&#123;</span><br><span class="line">    <span class="attr">id</span>: <span class="string">&#x27;4&#x27;</span>,</span><br><span class="line">    <span class="attr">url</span>: <span class="string">&#x27;/backend/page/food/add.html?id=&#x27;</span>+st,</span><br><span class="line">    <span class="attr">name</span>: <span class="string">&#x27;修改菜品&#x27;</span></span><br><span class="line">    &#125;,<span class="literal">true</span>)</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><button type="button" class="tab-to-top" aria-label="scroll to top"><i class="fas fa-arrow-up"></i></button></div><div class="tab-item-content" id="修改菜品信息前端-3"><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><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="keyword">const</span> <span class="title function_">editDish</span> = (<span class="params">params</span>) =&gt; &#123;</span><br><span class="line">  <span class="keyword">return</span> $axios(&#123;</span><br><span class="line">    <span class="attr">url</span>: <span class="string">&#x27;/dish&#x27;</span>,</span><br><span class="line">    <span class="attr">method</span>: <span class="string">&#x27;put&#x27;</span>,</span><br><span class="line">    <span class="attr">data</span>: &#123; ...params &#125;</span><br><span class="line">  &#125;)</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><button type="button" class="tab-to-top" aria-label="scroll to top"><i class="fas fa-arrow-up"></i></button></div></div></div><ul><li>编写后端逻辑</li></ul><div class="tabs" id="修改菜品信息后端"><ul class="nav-tabs"><li class="tab active"><button type="button" data-href="#修改菜品信息后端-1">添加菜品信息后端代码</button></li><li class="tab"><button type="button" data-href="#修改菜品信息后端-2">updateWithFlavor</button></li></ul><div class="tab-contents"><div class="tab-item-content active" id="修改菜品信息后端-1"><p>和之前类似，重点是编写<code>updateWithFlavor</code>方法<br><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><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="meta">@PutMapping</span></span><br><span class="line"><span class="keyword">public</span> Result&lt;String&gt; <span class="title function_">update</span><span class="params">(<span class="meta">@RequestBody</span> DishDto dishDto)</span> &#123;</span><br><span class="line">    log.info(<span class="string">&quot;接收到的数据为：&#123;&#125;&quot;</span>, dishDto);</span><br><span class="line">    dishService.updateWithFlavor(dishDto);</span><br><span class="line">    <span class="keyword">return</span> Result.success(<span class="string">&quot;修改菜品成功&quot;</span>);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></p><button type="button" class="tab-to-top" aria-label="scroll to top"><i class="fas fa-arrow-up"></i></button></div><div class="tab-item-content" id="修改菜品信息后端-2"><p>通过id来修改基本信息，通过dish_id来删除口味信息，然后重新传入新的口味信息<br><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><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="meta">@Override</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">updateWithFlavor</span><span class="params">(DishDto dishDto)</span> &#123;</span><br><span class="line">    <span class="comment">//更新当前菜品数据（dish表）</span></span><br><span class="line">    <span class="built_in">this</span>.updateById(dishDto);</span><br><span class="line">    <span class="comment">//下面是更新当前菜品的口味数据</span></span><br><span class="line">    <span class="comment">//条件构造器</span></span><br><span class="line">    LambdaQueryWrapper&lt;DishFlavor&gt; queryWrapper = <span class="keyword">new</span> <span class="title class_">LambdaQueryWrapper</span>&lt;&gt;();</span><br><span class="line">    <span class="comment">//条件是当前菜品id</span></span><br><span class="line">    queryWrapper.eq(DishFlavor::getDishId, dishDto.getId());</span><br><span class="line">    <span class="comment">//将其删除掉</span></span><br><span class="line">    dishFlavorService.remove(queryWrapper);</span><br><span class="line">    <span class="comment">//获取传入的新的口味数据</span></span><br><span class="line">    List&lt;DishFlavor&gt; flavors = dishDto.getFlavors();</span><br><span class="line">    <span class="comment">//这些口味数据还是没有dish_id，所以需要赋予其dishId</span></span><br><span class="line">    flavors = flavors.stream().map((item) -&gt; &#123;</span><br><span class="line">        item.setDishId(dishDto.getId());</span><br><span class="line">        <span class="keyword">return</span> item;</span><br><span class="line">    &#125;).collect(Collectors.toList());</span><br><span class="line">    <span class="comment">//再重新加入到表中</span></span><br><span class="line">    dishFlavorService.saveBatch(flavors);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></p><button type="button" class="tab-to-top" aria-label="scroll to top"><i class="fas fa-arrow-up"></i></button></div></div></div><div class="note warning no-icon flat"><p>注意要在<code>DishServiceImpl</code>上添加<code>@Transactional</code>注解，同时也要在主启动类上加上<code>@EnableTransactionManagement</code>注解</p></div><h2 id="新增套餐"><a href="#新增套餐" class="headerlink" title="新增套餐"></a>新增套餐</h2><h3 id="需求分析-6"><a href="#需求分析-6" class="headerlink" title="需求分析"></a>需求分析</h3><ul><li>套餐就是多个菜品的集合</li><li>后台系统中可以管理套餐信息，通过新增套餐来添加一个新的套餐</li><li>在添加套餐时需要选择当前套餐所属的套餐分类和包含的菜品，并且需要上传套餐对应的图片</li></ul><div class="note info no-icon flat"><p>梳理新增套餐时前端页面与服务端的交互过程:</p><ol><li>页面发送ajax请求，请求服务端，获取套餐分类数据并展示到下拉框中（这个之前做过）</li><li>页面发送ajax请求，请求服务端，获取菜品分类数据并展示到添加菜品窗口中</li><li>页面发送ajax请求，请求服务端，根据菜品分类查询对应的菜品数据并展示到添加菜品窗口中</li><li>页面发送请求进行图片上传，请求服务端将图片保存到服务器（已完成）</li><li>页面发送请求进行图片下载，将上传的图片进行回显（已完成）</li><li>点击保存按钮，发送ajax请求，将套餐相关数据以json形式提交到服务端</li></ol><p>开发新增套餐功能，其实就是在服务端编写代码去处理前端页面发送的这6次请求</p></div><h3 id="数据模型-2"><a href="#数据模型-2" class="headerlink" title="数据模型"></a>数据模型</h3><ul><li>新增套餐，其实就是将新增页面录入的套餐信息插入到setmeal表中，而且还要向setmeal_dish表中插入套餐和菜品关联数据</li><li><p>所以在新增套餐时，需要对两张表进行操作</p><ol><li>setmeal表 —&gt; 套餐表</li><li>setmeal_dish表 —&gt; 套餐菜品关系表</li></ol><div class="tabs" id="新增套餐数据模型"><ul class="nav-tabs"><li class="tab active"><button type="button" data-href="#新增套餐数据模型-1">setmeal表</button></li><li class="tab"><button type="button" data-href="#新增套餐数据模型-2">setmeal_dish表</button></li></ul><div class="tab-contents"><div class="tab-item-content active" id="新增套餐数据模型-1"><div class="img-wrap"><div class="img-bg"><img class="img" src="https://raw.githubusercontent.com/silvan2077/markdown_pic/main/Picgo/20251103170418.png"/></div></div><button type="button" class="tab-to-top" aria-label="scroll to top"><i class="fas fa-arrow-up"></i></button></div><div class="tab-item-content" id="新增套餐数据模型-2"><div class="img-wrap"><div class="img-bg"><img class="img" src="https://raw.githubusercontent.com/silvan2077/markdown_pic/main/Picgo/20251103170226.png"/></div></div><button type="button" class="tab-to-top" aria-label="scroll to top"><i class="fas fa-arrow-up"></i></button></div></div></div></li></ul><h3 id="准备工作-3"><a href="#准备工作-3" class="headerlink" title="准备工作"></a>准备工作</h3><p>在开发业务功能前，先将需要用到的类和接口基本结构创建好:</p><ol><li><p>实体类SetmealDish</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="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_">SetmealDish</span> <span class="keyword">implements</span> <span class="title class_">Serializable</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> <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> Long id;</span><br><span class="line"></span><br><span class="line"></span><br><span class="line">    <span class="comment">//套餐id</span></span><br><span class="line">    <span class="keyword">private</span> Long setmealId;</span><br><span class="line"></span><br><span class="line"></span><br><span class="line">    <span class="comment">//菜品id</span></span><br><span class="line">    <span class="keyword">private</span> Long dishId;</span><br><span class="line"></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><br><span class="line"></span><br><span class="line">    <span class="comment">//菜品原价</span></span><br><span class="line">    <span class="keyword">private</span> BigDecimal price;</span><br><span class="line"></span><br><span class="line">    <span class="comment">//份数</span></span><br><span class="line">    <span class="keyword">private</span> Integer copies;</span><br><span class="line"></span><br><span class="line"></span><br><span class="line">    <span class="comment">//排序</span></span><br><span class="line">    <span class="keyword">private</span> Integer sort;</span><br><span class="line"></span><br><span class="line"></span><br><span class="line">    <span class="meta">@TableField(fill = FieldFill.INSERT)</span></span><br><span class="line">    <span class="keyword">private</span> LocalDateTime createTime;</span><br><span class="line"></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 updateTime;</span><br><span class="line"></span><br><span class="line"></span><br><span class="line">    <span class="meta">@TableField(fill = FieldFill.INSERT)</span></span><br><span class="line">    <span class="keyword">private</span> Long createUser;</span><br><span class="line"></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> Long updateUser;</span><br><span class="line"></span><br><span class="line"></span><br><span class="line">    <span class="comment">//是否删除</span></span><br><span class="line">    <span class="keyword">private</span> Integer isDeleted;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></li><li><p>DTO SetmealDto</p><p>普通的SetmealDish类肯定是不够我们用的，这里还需要加上套餐内的具体菜品和套餐分类名称</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="meta">@Data</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">SetmealDto</span> <span class="keyword">extends</span> <span class="title class_">Setmeal</span> &#123;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">private</span> List&lt;SetmealDish&gt; setmealDishes;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">private</span> String categoryName;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></li><li><p>Mapper接口SetmealDishMapper</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></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_">SetmealDishMapper</span> <span class="keyword">extends</span> <span class="title class_">BaseMapper</span>&lt;SetmealDish&gt; &#123;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></li><li><p>业务层接口SetmealDishService</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="keyword">public</span> <span class="keyword">interface</span> <span class="title class_">SetmealDishService</span> <span class="keyword">extends</span> <span class="title class_">IService</span>&lt;SetmealDish&gt; &#123;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></li><li><p>业务层实现类SetmealDishservicelmpl</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></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_">SetmealDishServiceImpl</span> <span class="keyword">extends</span> <span class="title class_">ServiceImpl</span>&lt;SetmealDishMapper, SetmealDish&gt; <span class="keyword">implements</span> <span class="title class_">SetmealDishService</span> &#123;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></li><li><p>控制层SetmealController</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">@RestController</span></span><br><span class="line"><span class="meta">@RequestMapping(&quot;/setmeal&quot;)</span></span><br><span class="line"><span class="meta">@Slf4j</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">SetmealController</span> &#123;</span><br><span class="line">    <span class="meta">@Autowired</span></span><br><span class="line">    <span class="keyword">private</span> SetmealService setmealService;</span><br><span class="line">    <span class="meta">@Autowired</span></span><br><span class="line">    <span class="keyword">private</span> SetmealDishService setmealDishService;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h3 id="代码开发-3"><a href="#代码开发-3" class="headerlink" title="代码开发"></a>代码开发</h3></li></ol><p>新增套餐页面中套餐分类的下拉框能够直接显示套餐分类数据，该功能在之前已经实现<br><div class="img-wrap"><div class="img-bg"><img class="img" src="https://raw.githubusercontent.com/silvan2077/markdown_pic/main/Picgo/20251103172351.png"/></div></div></p><ul><li><p>点击添加菜品后可以看到发送了一个<code>dish/list?categoryId=xxx</code>的get请求，请求参数中包含categoryId，根据这个参数查询对应的菜品数据</p><div class="img-wrap"><div class="img-bg"><img class="img" src="https://raw.githubusercontent.com/silvan2077/markdown_pic/main/Picgo/20251105113755.png"/></div></div></li><li><p>先去DishController中编写对应的get方法来正确显示菜品数据</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></pre></td><td class="code"><pre><span class="line"><span class="meta">@GetMapping(&quot;/list&quot;)</span></span><br><span class="line"><span class="keyword">public</span> Result&lt;List&lt;Dish&gt;&gt; <span class="title function_">get</span><span class="params">(Dish dish)</span> &#123;</span><br><span class="line">    <span class="comment">//条件查询器</span></span><br><span class="line">    LambdaQueryWrapper&lt;Dish&gt; queryWrapper = <span class="keyword">new</span> <span class="title class_">LambdaQueryWrapper</span>&lt;&gt;();</span><br><span class="line">    <span class="comment">//根据传进来的categoryId查询</span></span><br><span class="line">    queryWrapper.eq(dish.getCategoryId() != <span class="literal">null</span>, Dish::getCategoryId, dish.getCategoryId());</span><br><span class="line">    <span class="comment">//只查询状态为1的菜品（启售菜品）</span></span><br><span class="line">    queryWrapper.eq(Dish::getStatus, <span class="number">1</span>);</span><br><span class="line">    <span class="comment">//简单排下序，其实也没啥太大作用</span></span><br><span class="line">    queryWrapper.orderByAsc(Dish::getSort).orderByDesc(Dish::getUpdateTime);</span><br><span class="line">    <span class="comment">//获取查询到的结果作为返回值</span></span><br><span class="line">    List&lt;Dish&gt; list = dishService.list(queryWrapper);</span><br><span class="line">    <span class="keyword">return</span> Result.success(list);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></li><li><p>前端代码分析</p></li></ul><div class="tabs" id="新增套餐前端代码分析"><ul class="nav-tabs"><li class="tab active"><button type="button" data-href="#新增套餐前端代码分析-1">保存按钮</button></li><li class="tab"><button type="button" data-href="#新增套餐前端代码分析-2">submitForm</button></li><li class="tab"><button type="button" data-href="#新增套餐前端代码分析-3">addSetmeal</button></li></ul><div class="tab-contents"><div class="tab-item-content active" id="新增套餐前端代码分析-1"><figure class="highlight html"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="tag">&lt;<span class="name">el-button</span> <span class="attr">type</span>=<span class="string">&quot;primary&quot;</span> @<span class="attr">click</span>=<span class="string">&quot;submitForm(&#x27;ruleForm&#x27;, false)&quot;</span>&gt;</span> 保存 <span class="tag">&lt;/<span class="name">el-button</span>&gt;</span></span><br></pre></td></tr></table></figure><button type="button" class="tab-to-top" aria-label="scroll to top"><i class="fas fa-arrow-up"></i></button></div><div class="tab-item-content" id="新增套餐前端代码分析-2"><p>表单和前面一样，是新增和修改共用一个表单。<br><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><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="title function_">submitForm</span>(<span class="params">formName, st</span>) &#123;</span><br><span class="line">    <span class="variable language_">this</span>.<span class="property">$refs</span>[formName].<span class="title function_">validate</span>(<span class="function">(<span class="params">valid</span>) =&gt;</span> &#123;</span><br><span class="line">        <span class="keyword">if</span> (valid) &#123;</span><br><span class="line">        <span class="keyword">let</span> prams = &#123; ...<span class="variable language_">this</span>.<span class="property">ruleForm</span> &#125;</span><br><span class="line">        prams.<span class="property">price</span> *= <span class="number">100</span></span><br><span class="line">        prams.<span class="property">setmealDishes</span> = <span class="variable language_">this</span>.<span class="property">dishTable</span>.<span class="title function_">map</span>(<span class="function">(<span class="params">obj</span>) =&gt;</span> (&#123;</span><br><span class="line">            <span class="attr">copies</span>: obj.<span class="property">copies</span>,</span><br><span class="line">            <span class="attr">dishId</span>: obj.<span class="property">dishId</span>,</span><br><span class="line">            <span class="attr">name</span>: obj.<span class="property">name</span>,</span><br><span class="line">            <span class="attr">price</span>: obj.<span class="property">price</span>,</span><br><span class="line">        &#125;))</span><br><span class="line">        prams.<span class="property">status</span> = <span class="variable language_">this</span>.<span class="property">ruleForm</span> ? <span class="number">1</span> : <span class="number">0</span></span><br><span class="line">        prams.<span class="property">categoryId</span> = <span class="variable language_">this</span>.<span class="property">ruleForm</span>.<span class="property">idType</span></span><br><span class="line">        <span class="keyword">if</span>(prams.<span class="property">setmealDishes</span>.<span class="property">length</span> &lt; <span class="number">1</span>)&#123;</span><br><span class="line">            <span class="variable language_">this</span>.<span class="property">$message</span>.<span class="title function_">error</span>(<span class="string">&#x27;请选择菜品！&#x27;</span>)</span><br><span class="line">            <span class="keyword">return</span> </span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">if</span>(!<span class="variable language_">this</span>.<span class="property">imageUrl</span>)&#123;</span><br><span class="line">            <span class="variable language_">this</span>.<span class="property">$message</span>.<span class="title function_">error</span>(<span class="string">&#x27;请上传套餐图片&#x27;</span>)</span><br><span class="line">            <span class="keyword">return</span> </span><br><span class="line">        &#125;</span><br><span class="line">        <span class="comment">// delete prams.dishList</span></span><br><span class="line">        <span class="keyword">if</span> (<span class="variable language_">this</span>.<span class="property">actionType</span> == <span class="string">&#x27;add&#x27;</span>) &#123;</span><br><span class="line">            <span class="keyword">delete</span> prams.<span class="property">id</span></span><br><span class="line">            <span class="title function_">addSetmeal</span>(prams)</span><br><span class="line">            .<span class="title function_">then</span>(<span class="function">(<span class="params">res</span>) =&gt;</span> &#123;</span><br><span class="line">                <span class="keyword">if</span> (res.<span class="property">code</span> === <span class="number">1</span>) &#123;</span><br><span class="line">                <span class="variable language_">this</span>.<span class="property">$message</span>.<span class="title function_">success</span>(<span class="string">&#x27;套餐添加成功！&#x27;</span>)</span><br><span class="line">                <span class="keyword">if</span> (!st) &#123;</span><br><span class="line">                    <span class="variable language_">this</span>.<span class="title function_">goBack</span>()</span><br><span class="line">                &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">                    <span class="variable language_">this</span>.<span class="property">$refs</span>.<span class="property">ruleForm</span>.<span class="title function_">resetFields</span>()</span><br><span class="line">                    <span class="variable language_">this</span>.<span class="property">dishList</span> = []</span><br><span class="line">                    <span class="variable language_">this</span>.<span class="property">dishTable</span> = []</span><br><span class="line">                    <span class="variable language_">this</span>.<span class="property">ruleForm</span> = &#123;</span><br><span class="line">                    <span class="attr">name</span>: <span class="string">&#x27;&#x27;</span>,</span><br><span class="line">                    <span class="attr">categoryId</span>: <span class="string">&#x27;&#x27;</span>,</span><br><span class="line">                    <span class="attr">price</span>: <span class="string">&#x27;&#x27;</span>,</span><br><span class="line">                    <span class="attr">code</span>: <span class="string">&#x27;&#x27;</span>,</span><br><span class="line">                    <span class="attr">image</span>: <span class="string">&#x27;&#x27;</span>,</span><br><span class="line">                    <span class="attr">description</span>: <span class="string">&#x27;&#x27;</span>,</span><br><span class="line">                    <span class="attr">dishList</span>: [],</span><br><span class="line">                    <span class="attr">status</span>: <span class="literal">true</span>,</span><br><span class="line">                    <span class="attr">id</span>: <span class="string">&#x27;&#x27;</span>,</span><br><span class="line">                    <span class="attr">idType</span>: <span class="string">&#x27;&#x27;</span>,</span><br><span class="line">                    &#125;</span><br><span class="line">                    <span class="variable language_">this</span>.<span class="property">imageUrl</span> = <span class="string">&#x27;&#x27;</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="variable language_">this</span>.<span class="property">$message</span>.<span class="title function_">error</span>(res.<span class="property">msg</span> || <span class="string">&#x27;操作失败&#x27;</span>)</span><br><span class="line">                &#125;</span><br><span class="line">            &#125;)</span><br><span class="line">            .<span class="title function_">catch</span>(<span class="function">(<span class="params">err</span>) =&gt;</span> &#123;</span><br><span class="line">                <span class="variable language_">this</span>.<span class="property">$message</span>.<span class="title function_">error</span>(<span class="string">&#x27;请求出错了：&#x27;</span> + err)</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">delete</span> prams.<span class="property">updateTime</span></span><br><span class="line">            <span class="title function_">editSetmeal</span>(prams)</span><br><span class="line">            .<span class="title function_">then</span>(<span class="function">(<span class="params">res</span>) =&gt;</span> &#123;</span><br><span class="line">                <span class="keyword">if</span> (res.<span class="property">code</span> === <span class="number">1</span>) &#123;</span><br><span class="line">                <span class="variable language_">this</span>.<span class="property">$message</span>.<span class="title function_">success</span>(<span class="string">&#x27;套餐修改成功！&#x27;</span>)</span><br><span class="line">                <span class="variable language_">this</span>.<span class="title function_">goBack</span>()</span><br><span class="line">                &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">                <span class="variable language_">this</span>.<span class="property">$message</span>.<span class="title function_">error</span>(res.<span class="property">msg</span> || <span class="string">&#x27;操作失败&#x27;</span>)</span><br><span class="line">                &#125;</span><br><span class="line">            &#125;)</span><br><span class="line">            .<span class="title function_">catch</span>(<span class="function">(<span class="params">err</span>) =&gt;</span> &#123;</span><br><span class="line">                <span class="variable language_">this</span>.<span class="property">$message</span>.<span class="title function_">error</span>(<span class="string">&#x27;请求出错了：&#x27;</span> + err)</span><br><span class="line">            &#125;)</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">return</span> <span class="literal">false</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><button type="button" class="tab-to-top" aria-label="scroll to top"><i class="fas fa-arrow-up"></i></button></div><div class="tab-item-content" id="新增套餐前端代码分析-3"><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><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="keyword">const</span> <span class="title function_">addSetmeal</span> = (<span class="params">params</span>) =&gt; &#123;</span><br><span class="line">  <span class="keyword">return</span> $axios(&#123;</span><br><span class="line">    <span class="attr">url</span>: <span class="string">&#x27;/setmeal&#x27;</span>,</span><br><span class="line">    <span class="attr">method</span>: <span class="string">&#x27;post&#x27;</span>,</span><br><span class="line">    <span class="attr">data</span>: &#123; ...params &#125;</span><br><span class="line">  &#125;)</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><button type="button" class="tab-to-top" aria-label="scroll to top"><i class="fas fa-arrow-up"></i></button></div></div></div><ul><li><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></pre></td><td class="code"><pre><span class="line"><span class="meta">@PostMapping</span></span><br><span class="line"><span class="keyword">public</span> Result&lt;String&gt; <span class="title function_">save</span><span class="params">(<span class="meta">@RequestBody</span> SetmealDto setmealDto)</span> &#123;</span><br><span class="line">    log.info(<span class="string">&quot;套餐信息：&#123;&#125;&quot;</span>, setmealDto);</span><br><span class="line">    <span class="keyword">return</span> Result.success(<span class="string">&quot;套餐添加成功&quot;</span>);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></li></ul><blockquote><p>套餐信息：SetmealDto(setmealDishes=[SetmealDish(id=null, setmealId=null, dishId=1397851370462687234, name=邵阳猪血丸子, price=13800, copies=1, sort=null, createTime=null, updateTime=null, createUser=null, updateUser=null, isDeleted=null)], categoryName=null)</p></blockquote><p>此处setmealId为null，再具体的代码中，要从setmealDao中获取并赋值</p><ul><li>具体业务逻辑如下</li></ul><div class="tabs" id="新增套餐controller层"><ul class="nav-tabs"><li class="tab active"><button type="button" data-href="#新增套餐controller层-1">controller层</button></li><li class="tab"><button type="button" data-href="#新增套餐controller层-2">setmealService</button></li><li class="tab"><button type="button" data-href="#新增套餐controller层-3">setmealServiceImpl</button></li></ul><div class="tab-contents"><div class="tab-item-content active" id="新增套餐controller层-1"><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><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="meta">@PostMapping</span></span><br><span class="line"><span class="keyword">public</span> Result&lt;String&gt; <span class="title function_">save</span><span class="params">(<span class="meta">@RequestBody</span> SetmealDto setmealDto)</span> &#123;</span><br><span class="line">    log.info(<span class="string">&quot;套餐信息：&#123;&#125;&quot;</span>, setmealDto);</span><br><span class="line">    setmealService.saveWithDish(setmealDto);</span><br><span class="line">    <span class="keyword">return</span> Result.success(<span class="string">&quot;套餐添加成功&quot;</span>);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><button type="button" class="tab-to-top" aria-label="scroll to top"><i class="fas fa-arrow-up"></i></button></div><div class="tab-item-content" id="新增套餐controller层-2"><figure class="highlight java"><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="keyword">public</span> <span class="keyword">interface</span> <span class="title class_">SetmealService</span> <span class="keyword">extends</span> <span class="title class_">IService</span>&lt;Setmeal&gt; &#123;</span><br><span class="line">    <span class="keyword">void</span> <span class="title function_">saveWithDish</span><span class="params">(SetmealDto setmealDto)</span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><button type="button" class="tab-to-top" aria-label="scroll to top"><i class="fas fa-arrow-up"></i></button></div><div class="tab-item-content" id="新增套餐controller层-3"><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><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="meta">@Service</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">SetmealServiceImpl</span> <span class="keyword">extends</span> <span class="title class_">ServiceImpl</span>&lt;SetmealMapper, Setmeal&gt; <span class="keyword">implements</span> <span class="title class_">SetmealService</span> &#123;</span><br><span class="line">    <span class="meta">@Autowired</span></span><br><span class="line">    <span class="keyword">protected</span> SetmealDishService setmealDishService;</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_">saveWithDish</span><span class="params">(SetmealDto setmealDto)</span> &#123;</span><br><span class="line">        <span class="built_in">this</span>.save(setmealDto);</span><br><span class="line">        List&lt;SetmealDish&gt; setmealDishes = setmealDto.getSetmealDishes();</span><br><span class="line">        setmealDishes = setmealDishes.stream().map((item) -&gt; &#123;</span><br><span class="line">            item.setSetmealId(setmealDto.getId());</span><br><span class="line">            <span class="keyword">return</span> item;</span><br><span class="line">        &#125;).collect(Collectors.toList());</span><br><span class="line">        setmealDishService.saveBatch(setmealDishes);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><button type="button" class="tab-to-top" aria-label="scroll to top"><i class="fas fa-arrow-up"></i></button></div></div></div><ul><li>至此，新增套餐的功能就实现了，重启服务器测试一下，查看数据库，套餐信息和套餐菜品信息都能成功插入</li></ul><h2 id="套餐信息分页查询"><a href="#套餐信息分页查询" class="headerlink" title="套餐信息分页查询"></a>套餐信息分页查询</h2><h3 id="梳理交互过程-1"><a href="#梳理交互过程-1" class="headerlink" title="梳理交互过程"></a>梳理交互过程</h3><ol><li>页面发送ajax请求，将分页查询参数（page，pageSize，name）提交到服务端，获取分页数据</li><li>页面发送请求，请求服务端进行图片下载，用于页面图片展示（已完成）</li></ol><h3 id="前端分析"><a href="#前端分析" class="headerlink" title="前端分析"></a>前端分析</h3><p>点击套餐管理，在搜索框输入1，获取请求url与请求方式<br><div class="img-wrap"><div class="img-bg"><img class="img" src="https://raw.githubusercontent.com/silvan2077/markdown_pic/main/Picgo/20251105124250.png"/></div></div></p><ul><li>请求网址: <a href="http://localhost/setmeal/page?page=1&amp;pageSize=10&amp;name=1">http://localhost/setmeal/page?page=1&amp;pageSize=10&amp;name=1</a></li><li>请求方法: GET</li></ul><h3 id="代码开发-4"><a href="#代码开发-4" class="headerlink" title="代码开发"></a>代码开发</h3><ul><li>在SetmealController类中，添加list方法，这里和前面的查询方法几乎一致，对比学习</li></ul><div class="tabs" id="套餐分类查询代码开发"><ul class="nav-tabs"><li class="tab active"><button type="button" data-href="#套餐分类查询代码开发-1">套餐信息分页查询</button></li><li class="tab"><button type="button" data-href="#套餐分类查询代码开发-2">菜品信息分页查询</button></li></ul><div class="tab-contents"><div class="tab-item-content active" id="套餐分类查询代码开发-1"><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><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">@GetMapping(&quot;/page&quot;)</span></span><br><span class="line"><span class="keyword">public</span> Result&lt;Page&gt; <span class="title function_">page</span><span class="params">(<span class="type">int</span> page, <span class="type">int</span> pageSize, String name)</span> &#123;</span><br><span class="line">    Page&lt;Setmeal&gt; pageInfo = <span class="keyword">new</span> <span class="title class_">Page</span>&lt;&gt;(page, pageSize);</span><br><span class="line">    Page&lt;SetmealDto&gt; dtoPage = <span class="keyword">new</span> <span class="title class_">Page</span>&lt;&gt;(page, pageSize);</span><br><span class="line">    LambdaQueryWrapper&lt;Setmeal&gt; queryWrapper = <span class="keyword">new</span> <span class="title class_">LambdaQueryWrapper</span>&lt;&gt;();</span><br><span class="line">    queryWrapper.like(name != <span class="literal">null</span>, Setmeal::getName, name);</span><br><span class="line">    queryWrapper.orderByDesc(Setmeal::getUpdateTime);</span><br><span class="line">    setmealService.page(pageInfo, queryWrapper);</span><br><span class="line">    BeanUtils.copyProperties(pageInfo, dtoPage, <span class="string">&quot;records&quot;</span>);</span><br><span class="line">    List&lt;Setmeal&gt; records = pageInfo.getRecords();</span><br><span class="line">    List&lt;SetmealDto&gt; list = records.stream().map((item) -&gt; &#123;</span><br><span class="line">        <span class="type">SetmealDto</span> <span class="variable">setmealDto</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">SetmealDto</span>();</span><br><span class="line">        BeanUtils.copyProperties(item, setmealDto);</span><br><span class="line">        <span class="type">Long</span> <span class="variable">categoryId</span> <span class="operator">=</span> item.getCategoryId();</span><br><span class="line">        <span class="type">Category</span> <span class="variable">category</span> <span class="operator">=</span> categoryService.getById(categoryId);</span><br><span class="line">        <span class="keyword">if</span> (category != <span class="literal">null</span>) &#123;</span><br><span class="line">            setmealDto.setCategoryName(category.getName());</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">return</span> setmealDto;</span><br><span class="line">    &#125;).collect(Collectors.toList());</span><br><span class="line">    dtoPage.setRecords(list);</span><br><span class="line">    <span class="keyword">return</span> Result.success(dtoPage);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><button type="button" class="tab-to-top" aria-label="scroll to top"><i class="fas fa-arrow-up"></i></button></div><div class="tab-item-content" id="套餐分类查询代码开发-2"><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><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">@GetMapping(&quot;/page&quot;)</span></span><br><span class="line"><span class="keyword">public</span> Result&lt;Page&gt; <span class="title function_">page</span><span class="params">(<span class="type">int</span> page, <span class="type">int</span> pageSize, String name)</span> &#123;</span><br><span class="line">    <span class="comment">//构造分页构造器对象</span></span><br><span class="line">    Page&lt;Dish&gt; pageInfo = <span class="keyword">new</span> <span class="title class_">Page</span>&lt;&gt;(page, pageSize);</span><br><span class="line">    <span class="comment">//这个就是我们到时候返回的结果</span></span><br><span class="line">    Page&lt;DishDto&gt; dishDtoPage = <span class="keyword">new</span> <span class="title class_">Page</span>&lt;&gt;(page, pageSize);</span><br><span class="line">    <span class="comment">//条件构造器</span></span><br><span class="line">    LambdaQueryWrapper&lt;Dish&gt; queryWrapper = <span class="keyword">new</span> <span class="title class_">LambdaQueryWrapper</span>&lt;&gt;();</span><br><span class="line">    <span class="comment">//添加条件</span></span><br><span class="line">    queryWrapper.like(name != <span class="literal">null</span>, Dish::getName, name);</span><br><span class="line">    queryWrapper.orderByDesc(Dish::getUpdateTime);</span><br><span class="line">    <span class="comment">//执行分页查询</span></span><br><span class="line">    dishService.page(pageInfo, queryWrapper);</span><br><span class="line"></span><br><span class="line">    <span class="comment">//对象拷贝，这里只需要拷贝一下查询到的条目数</span></span><br><span class="line">    BeanUtils.copyProperties(pageInfo, dishDtoPage, <span class="string">&quot;records&quot;</span>);</span><br><span class="line"></span><br><span class="line">    <span class="comment">//获取原records数据</span></span><br><span class="line">    List&lt;Dish&gt; records = pageInfo.getRecords();</span><br><span class="line"></span><br><span class="line">    <span class="comment">//遍历每一条records数据</span></span><br><span class="line">    List&lt;DishDto&gt; list = records.stream().map((item) -&gt; &#123;</span><br><span class="line">        <span class="type">DishDto</span> <span class="variable">dishDto</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">DishDto</span>();</span><br><span class="line">        <span class="comment">//将数据赋给dishDto对象</span></span><br><span class="line">        BeanUtils.copyProperties(item, dishDto);</span><br><span class="line">        <span class="comment">//然后获取一下dish对象的category_id属性</span></span><br><span class="line">        <span class="type">Long</span> <span class="variable">categoryId</span> <span class="operator">=</span> item.getCategoryId();  <span class="comment">//分类id</span></span><br><span class="line">        <span class="comment">//根据这个属性，获取到Category对象（这里需要用@Autowired注入一个CategoryService对象）</span></span><br><span class="line">        <span class="type">Category</span> <span class="variable">category</span> <span class="operator">=</span> categoryService.getById(categoryId);</span><br><span class="line">        <span class="comment">//随后获取Category对象的name属性，也就是菜品分类名称</span></span><br><span class="line">        <span class="type">String</span> <span class="variable">categoryName</span> <span class="operator">=</span> category.getName();</span><br><span class="line">        <span class="comment">//最后将菜品分类名称赋给dishDto对象就好了</span></span><br><span class="line">        dishDto.setCategoryName(categoryName);</span><br><span class="line">        <span class="comment">//结果返回一个dishDto对象</span></span><br><span class="line">        <span class="keyword">return</span> dishDto;</span><br><span class="line">        <span class="comment">//并将dishDto对象封装成一个集合，作为我们的最终结果</span></span><br><span class="line">    &#125;).collect(Collectors.toList());</span><br><span class="line"></span><br><span class="line">    dishDtoPage.setRecords(list);</span><br><span class="line">    <span class="keyword">return</span> Result.success(dishDtoPage);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><button type="button" class="tab-to-top" aria-label="scroll to top"><i class="fas fa-arrow-up"></i></button></div></div></div><h2 id="删除套餐"><a href="#删除套餐" class="headerlink" title="删除套餐"></a>删除套餐</h2><h3 id="需求分析-7"><a href="#需求分析-7" class="headerlink" title="需求分析"></a>需求分析</h3><ul><li>套餐管理列表页面点击删除按钮，可以删除对应的套餐信息</li><li>可以通过复选框选择多个套餐，选择批量删除一次性删除多个套餐</li></ul><div class="note warning no-icon flat"><p><strong>注意：</strong><code>在售</code>中的套餐不能删除，需要先<code>停售</code>，然后才能删除</p></div><h3 id="梳理交互过程-2"><a href="#梳理交互过程-2" class="headerlink" title="梳理交互过程"></a>梳理交互过程</h3><ol><li>删除单个套餐时，页面发送ajax请求，根据套餐id删除对应套餐</li><li>删除多个套餐时，页面发送ajax请求，根据提交的多个套餐id删除对应套餐开发删除套餐功能</li></ol><div class="img-wrap"><div class="img-bg"><img class="img" src="https://raw.githubusercontent.com/silvan2077/markdown_pic/main/Picgo/20251105125444.png"/></div></div><ul><li>请求网址: <a href="http://localhost/setmeal?ids=1579044544635232258,1415580119015145474">http://localhost/setmeal?ids=1579044544635232258,1415580119015145474</a></li><li>请求方法: DELETE</li></ul><div class="note info no-icon flat"><p>删除单个套餐和批量删除这两种请求的地址和请求方式都是相同的,不同的则是传递的id个数，所以在服务端可以提供一个方法来统一处理。</p></div><h3 id="代码开发-5"><a href="#代码开发-5" class="headerlink" title="代码开发"></a>代码开发</h3><ul><li><p>在<code>SetmealController</code>中添加delete方法</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></pre></td><td class="code"><pre><span class="line"><span class="meta">@DeleteMapping</span></span><br><span class="line"><span class="keyword">public</span> Result&lt;String&gt; <span class="title function_">deleteByIds</span><span class="params">(<span class="meta">@RequestParam</span> List&lt;Long&gt; ids)</span> &#123;</span><br><span class="line">    log.info(<span class="string">&quot;要删除的套餐id为：&#123;&#125;&quot;</span>,ids);</span><br><span class="line">    setmealService.removeWithDish(ids);</span><br><span class="line">    <span class="keyword">return</span> Result.success(<span class="string">&quot;删除成功&quot;</span>);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></li><li><p>在<code>SetmealService</code>中创建<code>removeWithDish</code>方法</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">void</span> <span class="title function_">removeWithDish</span><span class="params">(List&lt;Long&gt; ids)</span>;</span><br></pre></td></tr></table></figure></li><li><p>在<code>SetmealServiceImpl</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></pre></td><td class="code"><pre><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_">removeWithDish</span><span class="params">(List&lt;Long&gt; ids)</span> &#123;</span><br><span class="line">    <span class="comment">//先判断一下能不能删，如果status为1，则套餐在售，不能删</span></span><br><span class="line">    <span class="comment">//select * from setmeal where id in (ids) and status = 1</span></span><br><span class="line">    LambdaQueryWrapper&lt;Setmeal&gt; setmealLambdaQueryWrapper = <span class="keyword">new</span> <span class="title class_">LambdaQueryWrapper</span>&lt;&gt;();</span><br><span class="line">    setmealLambdaQueryWrapper.in(Setmeal::getId, ids);</span><br><span class="line">    setmealLambdaQueryWrapper.eq(Setmeal::getStatus, <span class="number">1</span>);</span><br><span class="line">    <span class="type">int</span> <span class="variable">count</span> <span class="operator">=</span> <span class="built_in">this</span>.count(setmealLambdaQueryWrapper);</span><br><span class="line">    <span class="comment">//下面两行是我debug输出的日志，没啥用</span></span><br><span class="line">    List&lt;Setmeal&gt; list = <span class="built_in">this</span>.list(setmealLambdaQueryWrapper);</span><br><span class="line">    log.info(<span class="string">&quot;查询到的数据为：&#123;&#125;&quot;</span>,list);</span><br><span class="line">    <span class="keyword">if</span> (count &gt; <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_">CustomException</span>(<span class="string">&quot;套餐正在售卖中，请先停售再进行删除&quot;</span>);</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="comment">//如果没有在售套餐，则直接删除</span></span><br><span class="line">    <span class="built_in">this</span>.removeByIds(ids);</span><br><span class="line">    <span class="comment">//继续删除</span></span><br><span class="line">    LambdaQueryWrapper&lt;SetmealDish&gt; setmealDishLambdaQueryWrapper = <span class="keyword">new</span> <span class="title class_">LambdaQueryWrapper</span>&lt;&gt;();</span><br><span class="line">    setmealDishLambdaQueryWrapper.in(SetmealDish::getSetmealId, ids);</span><br><span class="line">    setmealDishService.remove(setmealDishLambdaQueryWrapper);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></li><li><p>重启服务器，并测试</p><div class="note warning no-icon flat"><p>注意：</p><ol><li>因为此处还没有设置停售功能，所以删除套餐前需要去数据库将<code>status</code>字段设置为0，即可成功删除</li><li>之前在启动类上已经添加了<code>@EnableTransactionManagement</code>注解，所以在SetmealServiceImpl类上添加<code>@Transactional</code>注解就可以实现事务了</li></ol></div></li></ul><h2 id="邮件发送（替换手机验证）"><a href="#邮件发送（替换手机验证）" class="headerlink" title="邮件发送（替换手机验证）"></a>邮件发送（替换手机验证）</h2><p>该处黑马使用的是阿里云短信验证码，但是使用该功能需要一定的费用，且需要申请签名等流程，非常繁琐，所以这里使用别的大佬给的QQ邮箱验证码的方式来完成<br><div class="img-wrap"><div class="img-bg"><img class="img" src="https://raw.githubusercontent.com/silvan2077/markdown_pic/main/Picgo/20251105201711.png"/></div></div><br><div class="img-wrap"><div class="img-bg"><img class="img" src="https://raw.githubusercontent.com/silvan2077/markdown_pic/main/Picgo/20251105210136.png} ### 需求分析 - 手机（邮箱）验证码登录的优点： - 方便快捷，无需注册，直接登录 - 使用短信验证码作为登录凭证，无需记忆密码 - 安全 - 登录流程: - 输入手机号（邮箱） > 获取验证码 > 输入验证码 > 点击登录 > 登录成功 ### 数据模型 此处直接将邮箱号码存到phone字段中(如果不嫌麻烦也可以另外创建一个字段来存邮箱) {% image https://raw.githubusercontent.com/silvan2077/markdown_pic/main/Picgo/20251105210804.png"/></div></div></p><div class="note warning no-icon flat"><ul><li>手机号（邮箱）是区分不同用户的标识，在用户登录的时候判断所输入的手机号（邮箱）是否存储在表中</li><li>如果不在表中，说明该用户为一个新的用户，将该用户自动保在user表中</li></ul></div><h3 id="准备工作-4"><a href="#准备工作-4" class="headerlink" title="准备工作"></a>准备工作</h3><p>先将要用到的类和接口的基本结构都创建好</p><ul><li>实体类User<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><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">@Data</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">User</span> <span class="keyword">implements</span> <span class="title class_">Serializable</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> <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> Long id;</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><br><span class="line"></span><br><span class="line">    <span class="comment">//手机号</span></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">//性别 0 女 1 男</span></span><br><span class="line">    <span class="keyword">private</span> String sex;</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 idNumber;</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 avatar;</span><br><span class="line"></span><br><span class="line">    <span class="comment">//状态 0:禁用，1:正常</span></span><br><span class="line">    <span class="keyword">private</span> Integer status;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></li></ul><div class="tabs" id="邮件发送准备工作"><ul class="nav-tabs"><li class="tab active"><button type="button" data-href="#邮件发送准备工作-1">UserMapper</button></li><li class="tab"><button type="button" data-href="#邮件发送准备工作-2">UserService</button></li><li class="tab"><button type="button" data-href="#邮件发送准备工作-3">UserServiceImpl</button></li></ul><div class="tab-contents"><div class="tab-item-content active" id="邮件发送准备工作-1"><figure class="highlight java"><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="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> &#123;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><button type="button" class="tab-to-top" aria-label="scroll to top"><i class="fas fa-arrow-up"></i></button></div><div class="tab-item-content" id="邮件发送准备工作-2"><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="keyword">public</span> <span class="keyword">interface</span> <span class="title class_">UserService</span> <span class="keyword">extends</span> <span class="title class_">IService</span>&lt;User&gt; &#123;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><button type="button" class="tab-to-top" aria-label="scroll to top"><i class="fas fa-arrow-up"></i></button></div><div class="tab-item-content" id="邮件发送准备工作-3"><figure class="highlight java"><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="meta">@Service</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">extends</span> <span class="title class_">ServiceImpl</span>&lt;UserMapper,User&gt; <span class="keyword">implements</span> <span class="title class_">UserService</span> &#123;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><button type="button" class="tab-to-top" aria-label="scroll to top"><i class="fas fa-arrow-up"></i></button></div></div></div><ul><li><p>控制层UserController</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">@RestController</span></span><br><span class="line"><span class="meta">@Slf4j</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 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">&#125;</span><br></pre></td></tr></table></figure></li><li><p>工具类（我们自己造自己的邮箱工具类）</p><ul><li>首先导入坐标<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.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-mail<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></li><li>添加配置文件<figure class="highlight yml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><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="attr">spring:</span></span><br><span class="line">  <span class="attr">mail:</span></span><br><span class="line">    <span class="attr">password:</span> <span class="string">tzunlrdblmhvijce</span></span><br><span class="line">    <span class="attr">username:</span> <span class="number">1330132229</span><span class="string">@qq.com</span></span><br><span class="line">    <span class="attr">host:</span> <span class="string">smtp.qq.com</span></span><br><span class="line">    <span class="attr">port:</span> <span class="number">465</span> <span class="comment"># 必须是 465 或 587</span></span><br><span class="line">    <span class="attr">properties:</span></span><br><span class="line">      <span class="attr">mail:</span></span><br><span class="line">        <span class="attr">smtp:</span></span><br><span class="line">          <span class="attr">auth:</span> <span class="literal">true</span></span><br><span class="line">          <span class="attr">ssl:</span></span><br><span class="line">            <span class="attr">enable:</span> <span class="literal">true</span></span><br></pre></td></tr></table></figure></li></ul></li><li><p>然后编写一个工具类，用于发送邮件验证码和随机生成验证码</p></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="meta">@Component</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">MailUtils</span> &#123;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">static</span> JavaMailSender sender;</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_">setSender</span><span class="params">(JavaMailSender sender)</span> &#123;</span><br><span class="line">        MailUtils.sender = sender;</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_">sendTestMail</span><span class="params">(String email, String code)</span> <span class="keyword">throws</span> MessagingException &#123;</span><br><span class="line">        <span class="type">SimpleMailMessage</span> <span class="variable">mail</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">SimpleMailMessage</span>();</span><br><span class="line">        mail.setSubject(<span class="string">&quot;瑞吉外卖验证码&quot;</span>);</span><br><span class="line">        mail.setTo(email);</span><br><span class="line">        mail.setFrom(<span class="string">&quot;1330132229@qq.com&quot;</span>);</span><br><span class="line">        mail.setText(<span class="string">&quot;您的验证码为：&quot;</span> + code);</span><br><span class="line">        sender.send(mail);</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">static</span> String <span class="title function_">achieveCode</span><span class="params">()</span> &#123;  <span class="comment">//由于数字 1 、 0 和字母 O 、l 有时分不清楚，所以，没有数字 1 、 0</span></span><br><span class="line">        String[] beforeShuffle = <span class="keyword">new</span> <span class="title class_">String</span>[]&#123;<span class="string">&quot;2&quot;</span>, <span class="string">&quot;3&quot;</span>, <span class="string">&quot;4&quot;</span>, <span class="string">&quot;5&quot;</span>, <span class="string">&quot;6&quot;</span>, <span class="string">&quot;7&quot;</span>, <span class="string">&quot;8&quot;</span>, <span class="string">&quot;9&quot;</span>, <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 class="string">&quot;E&quot;</span>, <span class="string">&quot;F&quot;</span>,</span><br><span class="line">                <span class="string">&quot;G&quot;</span>, <span class="string">&quot;H&quot;</span>, <span class="string">&quot;I&quot;</span>, <span class="string">&quot;J&quot;</span>, <span class="string">&quot;K&quot;</span>, <span class="string">&quot;L&quot;</span>, <span class="string">&quot;M&quot;</span>, <span class="string">&quot;N&quot;</span>, <span class="string">&quot;O&quot;</span>, <span class="string">&quot;P&quot;</span>, <span class="string">&quot;Q&quot;</span>, <span class="string">&quot;R&quot;</span>, <span class="string">&quot;S&quot;</span>, <span class="string">&quot;T&quot;</span>, <span class="string">&quot;U&quot;</span>, <span class="string">&quot;V&quot;</span>, <span class="string">&quot;W&quot;</span>, <span class="string">&quot;X&quot;</span>, <span class="string">&quot;Y&quot;</span>, <span class="string">&quot;Z&quot;</span>, <span class="string">&quot;a&quot;</span>,</span><br><span class="line">                <span class="string">&quot;b&quot;</span>, <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 class="string">&quot;g&quot;</span>, <span class="string">&quot;h&quot;</span>, <span class="string">&quot;i&quot;</span>, <span class="string">&quot;j&quot;</span>, <span class="string">&quot;k&quot;</span>, <span class="string">&quot;l&quot;</span>, <span class="string">&quot;m&quot;</span>, <span class="string">&quot;n&quot;</span>, <span class="string">&quot;o&quot;</span>, <span class="string">&quot;p&quot;</span>, <span class="string">&quot;q&quot;</span>, <span class="string">&quot;r&quot;</span>, <span class="string">&quot;s&quot;</span>, <span class="string">&quot;t&quot;</span>, <span class="string">&quot;u&quot;</span>, <span class="string">&quot;v&quot;</span>,</span><br><span class="line">                <span class="string">&quot;w&quot;</span>, <span class="string">&quot;x&quot;</span>, <span class="string">&quot;y&quot;</span>, <span class="string">&quot;z&quot;</span>&#125;;</span><br><span class="line">        List&lt;String&gt; list = Arrays.asList(beforeShuffle);<span class="comment">//将数组转换为集合</span></span><br><span class="line">        Collections.shuffle(list);  <span class="comment">//打乱集合顺序</span></span><br><span class="line">        <span class="type">StringBuilder</span> <span class="variable">sb</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">StringBuilder</span>();</span><br><span class="line">        <span class="keyword">for</span> (String s : list) &#123;</span><br><span class="line">            sb.append(s); <span class="comment">//将集合转化为字符串</span></span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">return</span> sb.substring(<span class="number">3</span>, <span class="number">8</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><ul><li><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></pre></td><td class="code"><pre><span class="line"><span class="comment">//定义不需要处理的请求</span></span><br><span class="line">String[] urls = <span class="keyword">new</span> <span class="title class_">String</span>[]&#123;</span><br><span class="line">        <span class="string">&quot;/employee/login&quot;</span>,</span><br><span class="line">        <span class="string">&quot;/employee/logout&quot;</span>,</span><br><span class="line">        <span class="string">&quot;/backend/**&quot;</span>,</span><br><span class="line">        <span class="string">&quot;/front/**&quot;</span>,</span><br><span class="line">        <span class="string">&quot;/common/**&quot;</span>,</span><br><span class="line">        <span class="comment">//对用户登陆操作放行</span></span><br><span class="line">        <span class="string">&quot;/user/login&quot;</span>,</span><br><span class="line">        <span class="string">&quot;/user/sendMsg&quot;</span></span><br><span class="line">&#125;;</span><br></pre></td></tr></table></figure></li><li><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></pre></td><td class="code"><pre><span class="line"><span class="comment">//判断用户是否登录</span></span><br><span class="line"><span class="keyword">if</span>(request.getSession().getAttribute(<span class="string">&quot;user&quot;</span>) != <span class="literal">null</span>)&#123;</span><br><span class="line">    log.info(<span class="string">&quot;用户已登录，用户id为：&#123;&#125;&quot;</span>,request.getSession().getAttribute(<span class="string">&quot;user&quot;</span>));</span><br><span class="line">    <span class="type">Long</span> <span class="variable">userId</span> <span class="operator">=</span> (Long)request.getSession().getAttribute(<span class="string">&quot;user&quot;</span>);</span><br><span class="line">    BaseContext.setCurrentId(userId);</span><br><span class="line">    filterChain.doFilter(request,response);</span><br><span class="line">    <span class="keyword">return</span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></li></ul><h3 id="发送验证码"><a href="#发送验证码" class="headerlink" title="发送验证码"></a>发送验证码</h3><div class="note warning no-icon flat"><p>此处需要重新导入前端资料，将<code>day06</code>中的<code>front</code>资料重新导入一遍<br>以下是通过网站对比文件区别的结果，之前的login.html文件是直接将生成的验证码赋值给code并显示，现在是调用后端结果来实现功能</p><div class="img-wrap"><div class="img-bg"><img class="img" src="https://raw.githubusercontent.com/silvan2077/markdown_pic/main/Picgo/20251105215236.png"/></div></div><p>将login.html中判断手机号的正则表达式换成判断邮箱的正则表达式:<code>const regex = /^\w+([-+.]\w+)*@\w+([-.]\w+)*\.\w+([-.]\w+)*$/;</code></p></div><ul><li>重新导入完资源之后，<strong>清除浏览器缓存(或使用无痕浏览)</strong>，并重启服务器，访问登录页面，获取验证码，这下应该是能收到请求的<div class="img-wrap"><div class="img-bg"><img class="img" src="https://raw.githubusercontent.com/silvan2077/markdown_pic/main/Picgo/20251105220250.png"/></div></div></li><li><p>从上图中可以看到，发送验证码的请求方式是POST，路径为<code>/user/sendMsg</code></p><ul><li>请求方式：<code>POST</code></li><li>请求路径：<code>/user/sendMsg</code></li></ul></li><li><p>那么我们在UserController控制层中，添加sendMsg方法,用来接收指令发送邮件</p></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></pre></td><td class="code"><pre><span class="line"><span class="meta">@PostMapping(&quot;/sendMsg&quot;)</span></span><br><span class="line">    <span class="keyword">public</span> Result&lt;String&gt; <span class="title function_">sendMsg</span><span class="params">(<span class="meta">@RequestBody</span> User user, HttpSession session)</span> <span class="keyword">throws</span> MessagingException &#123;</span><br><span class="line"><span class="comment">//        黑马提供的前端字段是 phone，懒得改了所以这里依旧是getPhone</span></span><br><span class="line">        <span class="type">String</span> <span class="variable">mail</span> <span class="operator">=</span> user.getPhone();</span><br><span class="line">        <span class="keyword">if</span> (!mail.isEmpty()) &#123;</span><br><span class="line">            <span class="comment">//随机生成一个验证码</span></span><br><span class="line">            <span class="type">String</span> <span class="variable">code</span> <span class="operator">=</span> MailUtils.achieveCode();</span><br><span class="line">            log.info(code);</span><br><span class="line">            <span class="comment">//这里的mail其实就是邮箱，code是我们生成的验证码</span></span><br><span class="line">            MailUtils.sendTestMail(mail, code);</span><br><span class="line">            <span class="comment">//验证码存session，方便后面拿出来比对</span></span><br><span class="line">            session.setAttribute(mail, code);</span><br><span class="line">            log.info(<span class="string">&quot;验证码：&#123;&#125;&quot;</span>, code);</span><br><span class="line">            <span class="keyword">return</span> Result.success(<span class="string">&quot;验证码发送成功&quot;</span>);</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">return</span> Result.error(<span class="string">&quot;验证码发送失败&quot;</span>);</span><br><span class="line">    &#125;</span><br></pre></td></tr></table></figure><ul><li>此时邮箱中就已经能接收到验证码了<div class="img-wrap"><div class="img-bg"><img class="img" src="https://raw.githubusercontent.com/silvan2077/markdown_pic/main/Picgo/20251106184101.png"/></div></div></li></ul><ul><li><p>输入验证码，点击登录</p><ul><li>请求路径为:<code>/user/login</code>，数据以json格式返回给服务端<div class="img-wrap"><div class="img-bg"><img class="img" src="https://raw.githubusercontent.com/silvan2077/markdown_pic/main/Picgo/20251106184842.png"/></div></div></li></ul></li><li><p>在UserController控制层中，添加<code>login</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></pre></td><td class="code"><pre><span class="line"><span class="meta">@PostMapping(&quot;/login&quot;)</span></span><br><span class="line"><span class="keyword">public</span> Result&lt;String&gt; <span class="title function_">login</span><span class="params">(<span class="meta">@RequestBody</span> Map map,HttpSession session)</span>&#123;</span><br><span class="line">    log.info(map.toString());</span><br><span class="line">    <span class="keyword">return</span> <span class="literal">null</span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><blockquote><p>com.blog.controller.UserController : {phone=1586385296@qq.com, code=bxQCK}</p></blockquote></li><li><p>看样子是可以获取到数据的，那么我们继续完善login方法</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="meta">@PostMapping(&quot;/login&quot;)</span></span><br><span class="line"><span class="keyword">public</span> Result&lt;User&gt; <span class="title function_">login</span><span class="params">(<span class="meta">@RequestBody</span> Map map, HttpSession session)</span> &#123;</span><br><span class="line">    log.info(map.toString());</span><br><span class="line">    <span class="comment">//获取邮箱</span></span><br><span class="line">    <span class="type">String</span> <span class="variable">phone</span> <span class="operator">=</span> map.get(<span class="string">&quot;phone&quot;</span>).toString();</span><br><span class="line">    <span class="comment">//获取验证码</span></span><br><span class="line">    <span class="type">String</span> <span class="variable">code</span> <span class="operator">=</span> map.get(<span class="string">&quot;code&quot;</span>).toString();</span><br><span class="line">    <span class="comment">//从session中获取验证码</span></span><br><span class="line">    <span class="type">String</span> <span class="variable">codeInSession</span> <span class="operator">=</span> session.getAttribute(phone).toString();</span><br><span class="line">    <span class="comment">//比较这用户输入的验证码和session中存的验证码是否一致</span></span><br><span class="line">    <span class="keyword">if</span> (code != <span class="literal">null</span> &amp;&amp; code.equals(codeInSession)) &#123;</span><br><span class="line">        <span class="comment">//如果输入正确，判断一下当前用户是否存在</span></span><br><span class="line">        LambdaQueryWrapper&lt;User&gt; queryWrapper = <span class="keyword">new</span> <span class="title class_">LambdaQueryWrapper</span>&lt;&gt;();</span><br><span class="line">        <span class="comment">//判断依据是从数据库中查询是否有其邮箱</span></span><br><span class="line">        queryWrapper.eq(User::getPhone, phone);</span><br><span class="line">        <span class="type">User</span> <span class="variable">user</span> <span class="operator">=</span> userService.getOne(queryWrapper);</span><br><span class="line">        <span class="comment">//如果不存在，则创建一个，存入数据库</span></span><br><span class="line">        <span class="keyword">if</span> (user == <span class="literal">null</span>) &#123;</span><br><span class="line">            user = <span class="keyword">new</span> <span class="title class_">User</span>();</span><br><span class="line">            user.setPhone(phone);</span><br><span class="line">            userService.save(user);</span><br><span class="line">            user.setName(<span class="string">&quot;用户&quot;</span> + codeInSession);</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="comment">//存个session，表示登录状态</span></span><br><span class="line">        session.setAttribute(<span class="string">&quot;user&quot;</span>,user.getId());</span><br><span class="line">        <span class="comment">//并将其作为结果返回</span></span><br><span class="line">        <span class="keyword">return</span> Result.success(user);</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">return</span> Result.error(<span class="string">&quot;登录失败&quot;</span>);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></li><li><p>此时输入验证码就能完成登录了。</p><div class="img-wrap"><div class="img-bg"><img class="img" src="https://raw.githubusercontent.com/silvan2077/markdown_pic/main/Picgo/20251106185101.png"/></div></div><div class="note warning no-icon flat"><p>发送验证码大概会有10秒钟的延迟，一定要等待验证码发送成功再登录，否则此时session为空会报错</p></div></li></ul><h2 id="地址簿"><a href="#地址簿" class="headerlink" title="地址簿"></a>地址簿</h2><h3 id="需求分析-8"><a href="#需求分析-8" class="headerlink" title="需求分析"></a>需求分析</h3><ul><li>地址簿，指的是移动端消费者用户的地址信息（外卖快递的收货地址）</li><li>用户登录成功后可以维护自己的地址信息（自己修改删除新增等）</li><li>同一个用户可以有多个地址信息，但是只能有一个默认地址。</li></ul><h3 id="数据模型-3"><a href="#数据模型-3" class="headerlink" title="数据模型"></a>数据模型</h3><div class="note warning no-icon flat"><p>注意这里黑马提供的phone类型为varchar(11)，显然邮箱长度更长，因此需要自己将该字段改大一点。</p></div><div class="img-wrap"><div class="img-bg"><img class="img" src="https://raw.githubusercontent.com/silvan2077/markdown_pic/main/Picgo/20251106185353.png"/></div></div><h3 id="准备工作-5"><a href="#准备工作-5" class="headerlink" title="准备工作"></a>准备工作</h3><ol><li><p>创建对应的实体类<code>AddressBook</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><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="meta">@Data</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">AddressBook</span> <span class="keyword">implements</span> <span class="title class_">Serializable</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> <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> Long id;</span><br><span class="line"></span><br><span class="line">    <span class="comment">//用户id</span></span><br><span class="line">    <span class="keyword">private</span> Long userId;</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 consignee;</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 phone;</span><br><span class="line"></span><br><span class="line">    <span class="comment">//性别 0 女 1 男</span></span><br><span class="line">    <span class="keyword">private</span> String sex;</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 provinceCode;</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 provinceName;</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 cityCode;</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 cityName;</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 districtCode;</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 districtName;</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 detail;</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 label;</span><br><span class="line"></span><br><span class="line">    <span class="comment">//是否默认 0否 1是</span></span><br><span class="line">    <span class="keyword">private</span> Integer isDefault;</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 createTime;</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_UPDATE)</span></span><br><span class="line">    <span class="keyword">private</span> LocalDateTime updateTime;</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> Long createUser;</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_UPDATE)</span></span><br><span class="line">    <span class="keyword">private</span> Long updateUser;</span><br><span class="line"></span><br><span class="line">    <span class="comment">//是否删除</span></span><br><span class="line">    <span class="keyword">private</span> Integer isDeleted;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></li><li><p><code>Mapper</code>接口<code>AddressBookMapper</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></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_">AddressBookMapper</span> <span class="keyword">extends</span> <span class="title class_">BaseMapper</span>&lt;AddressBook&gt; &#123;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></li><li><p>业务层接口<code>AddressBookService</code></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="keyword">public</span> <span class="keyword">interface</span> <span class="title class_">AddressBookService</span> <span class="keyword">extends</span> <span class="title class_">IService</span>&lt;AddressBook&gt; &#123;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></li><li><p>业务层实现类<code>AddressBookServicelmpl</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></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_">AddressBookServiceImpl</span> <span class="keyword">extends</span> <span class="title class_">ServiceImpl</span>&lt;AddressBookMapper, AddressBook&gt; <span class="keyword">implements</span> <span class="title class_">AddressBookService</span> &#123;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></li><li><p>控制层<code>AddressBookController</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="meta">@RestController</span></span><br><span class="line"><span class="meta">@Slf4j</span></span><br><span class="line"><span class="meta">@RequestMapping(&quot;/addressBook&quot;)</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">AddressBookController</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> AddressBookService addressBookService;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></li></ol><h3 id="完善地址管理页面"><a href="#完善地址管理页面" class="headerlink" title="完善地址管理页面"></a>完善地址管理页面</h3><ul><li>点击头像下的地址管理，查看请求方式与地址<div class="img-wrap"><div class="img-bg"><img class="img" src="https://raw.githubusercontent.com/silvan2077/markdown_pic/main/Picgo/20251106190332.png"/></div></div></li></ul><div class="img-wrap"><div class="img-bg"><img class="img" src="https://raw.githubusercontent.com/silvan2077/markdown_pic/main/Picgo/20251106190447.png"/></div></div><ul><li><p>在<code>AddressBookController</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></pre></td><td class="code"><pre><span class="line"><span class="meta">@GetMapping(&quot;/list&quot;)</span></span><br><span class="line"><span class="keyword">public</span> Result&lt;List&lt;AddressBook&gt;&gt; <span class="title function_">list</span><span class="params">(AddressBook addressBook)</span> &#123;</span><br><span class="line">    addressBook.setUserId(BaseContext.getCurrentId());</span><br><span class="line">    log.info(<span class="string">&quot;addressBook=&#123;&#125;&quot;</span>, addressBook);</span><br><span class="line"></span><br><span class="line">    <span class="comment">//条件构造器</span></span><br><span class="line">    LambdaQueryWrapper&lt;AddressBook&gt; queryWrapper = <span class="keyword">new</span> <span class="title class_">LambdaQueryWrapper</span>&lt;&gt;();</span><br><span class="line">    queryWrapper.eq(addressBook.getUserId() != <span class="literal">null</span>, AddressBook::getUserId, addressBook.getUserId());</span><br><span class="line">    queryWrapper.orderByDesc(AddressBook::getUpdateTime);</span><br><span class="line"></span><br><span class="line">    List&lt;AddressBook&gt; addressBooks = addressBookService.list(queryWrapper);</span><br><span class="line">    <span class="keyword">return</span> Result.success(addressBooks);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></li><li><p>不过写完了暂时还是不能看到效果的，数据库中并没有添加对应账号的数据，所以我们继续来做新增收货地址功能</p></li></ul><h3 id="新增收货地址"><a href="#新增收货地址" class="headerlink" title="新增收货地址"></a>新增收货地址</h3><ul><li><p>修改前端代码<br>这段代码是新增地址的前端代码，将其中的手机号全部替换成邮箱，判断手机号的正则也换成判断邮箱的正则,也可以直接复制这段代码更换掉<code>front/page/address-edit.html</code>文件中的代码</p><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><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></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;en&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">http-equiv</span>=<span class="string">&quot;X-UA-Compatible&quot;</span> <span class="attr">content</span>=<span class="string">&quot;IE=edge&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, maximum-scale=1.0,user-scalable=no,minimal-ui&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">link</span> <span class="attr">rel</span>=<span class="string">&quot;icon&quot;</span> <span class="attr">href</span>=<span class="string">&quot;./../images/favico.ico&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">script</span> <span class="attr">src</span>=<span class="string">&quot;./../js/base.js&quot;</span>&gt;</span><span class="tag">&lt;/<span class="name">script</span>&gt;</span></span><br><span class="line">        <span class="comment">&lt;!--element-ui的样式--&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">link</span> <span class="attr">rel</span>=<span class="string">&quot;stylesheet&quot;</span> <span class="attr">href</span>=<span class="string">&quot;../../backend/plugins/element-ui/index.css&quot;</span> /&gt;</span></span><br><span class="line">        <span class="comment">&lt;!--引入vant样式--&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">link</span> <span class="attr">rel</span>=<span class="string">&quot;stylesheet&quot;</span> <span class="attr">href</span>=<span class="string">&quot;../styles/vant.min.css&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">link</span> <span class="attr">rel</span>=<span class="string">&quot;stylesheet&quot;</span> <span class="attr">href</span>=<span class="string">&quot;../styles/index.css&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">link</span> <span class="attr">rel</span>=<span class="string">&quot;stylesheet&quot;</span> <span class="attr">href</span>=<span class="string">&quot;./../styles/address-edit.css&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">body</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">div</span> <span class="attr">id</span>=<span class="string">&quot;address_edit&quot;</span> <span class="attr">class</span>=<span class="string">&quot;app&quot;</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;divHead&quot;</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;divTitle&quot;</span>&gt;</span></span><br><span class="line">                    <span class="tag">&lt;<span class="name">i</span> <span class="attr">class</span>=<span class="string">&quot;el-icon-arrow-left&quot;</span> @<span class="attr">click</span>=<span class="string">&quot;goBack&quot;</span>&gt;</span><span class="tag">&lt;/<span class="name">i</span>&gt;</span>&#123;&#123;title&#125;&#125;</span><br><span class="line">                <span class="tag">&lt;/<span class="name">div</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 class="tag">&lt;<span class="name">div</span> <span class="attr">class</span>=<span class="string">&quot;divContent&quot;</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;divItem&quot;</span>&gt;</span></span><br><span class="line">                   <span class="tag">&lt;<span class="name">span</span>&gt;</span>联系人：<span class="tag">&lt;/<span class="name">span</span>&gt;</span> </span><br><span class="line">                   <span class="tag">&lt;<span class="name">el-input</span> <span class="attr">placeholder</span>=<span class="string">&quot; 请填写收货人的姓名&quot;</span> <span class="attr">v-model</span>=<span class="string">&quot;form.consignee&quot;</span>  <span class="attr">maxlength</span>=<span class="string">&#x27;10&#x27;</span> <span class="attr">class</span>=<span class="string">&quot;inputUser&quot;</span>/&gt;</span><span class="tag">&lt;/<span class="name">el-input</span>&gt;</span></span><br><span class="line">                   <span class="tag">&lt;<span class="name">span</span> <span class="attr">class</span>=<span class="string">&quot;spanChecked&quot;</span> @<span class="attr">click</span>=<span class="string">&quot;form.sex = &#x27;1&#x27;&quot;</span>&gt;</span></span><br><span class="line">                    <span class="tag">&lt;<span class="name">i</span> <span class="attr">:class</span>=<span class="string">&quot;&#123;iActive:form.sex === &#x27;1&#x27;&#125;&quot;</span>&gt;</span><span class="tag">&lt;/<span class="name">i</span>&gt;</span></span><br><span class="line">                    先生</span><br><span class="line">                   <span class="tag">&lt;/<span class="name">span</span>&gt;</span></span><br><span class="line">                   <span class="tag">&lt;<span class="name">span</span> <span class="attr">class</span>=<span class="string">&quot;spanChecked&quot;</span> @<span class="attr">click</span>=<span class="string">&quot;form.sex = &#x27;0&#x27;&quot;</span>&gt;</span></span><br><span class="line">                    <span class="tag">&lt;<span class="name">i</span> <span class="attr">:class</span>=<span class="string">&quot;&#123;iActive:form.sex === &#x27;0&#x27;&#125;&quot;</span>&gt;</span><span class="tag">&lt;/<span class="name">i</span>&gt;</span></span><br><span class="line">                    女士</span><br><span class="line">                <span class="tag">&lt;/<span class="name">span</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 class="tag">&lt;<span class="name">div</span> <span class="attr">class</span>=<span class="string">&quot;divItem&quot;</span>&gt;</span></span><br><span class="line">                    <span class="tag">&lt;<span class="name">span</span>&gt;</span>邮箱：<span class="tag">&lt;/<span class="name">span</span>&gt;</span></span><br><span class="line">                    <span class="tag">&lt;<span class="name">el-input</span> <span class="attr">placeholder</span>=<span class="string">&quot; 请填写收货人邮箱&quot;</span> <span class="attr">v-model</span>=<span class="string">&quot;form.phone&quot;</span>  <span class="attr">maxlength</span>=<span class="string">&#x27;20&#x27;</span> <span class="attr">style</span>=<span class="string">&quot;width: calc(100% - 80rem);&quot;</span>/&gt;</span><span class="tag">&lt;/<span class="name">el-input</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 class="tag">&lt;<span class="name">div</span> <span class="attr">class</span>=<span class="string">&quot;divItem&quot;</span>&gt;</span></span><br><span class="line">                    <span class="tag">&lt;<span class="name">span</span>&gt;</span>收货地址：<span class="tag">&lt;/<span class="name">span</span>&gt;</span> </span><br><span class="line">                    <span class="tag">&lt;<span class="name">el-input</span> <span class="attr">placeholder</span>=<span class="string">&quot; 请输入收货地址&quot;</span> <span class="attr">v-model</span>=<span class="string">&quot;form.detail&quot;</span>  <span class="attr">maxlength</span>=<span class="string">&#x27;140&#x27;</span>/&gt;</span><span class="tag">&lt;/<span class="name">el-input</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 class="tag">&lt;<span class="name">div</span> <span class="attr">class</span>=<span class="string">&quot;divItem &quot;</span>&gt;</span></span><br><span class="line">                    <span class="tag">&lt;<span class="name">span</span>&gt;</span>标签：<span class="tag">&lt;/<span class="name">span</span>&gt;</span> </span><br><span class="line">                    <span class="tag">&lt;<span class="name">span</span> <span class="attr">v-for</span>=<span class="string">&quot;(item,index) in labelList&quot;</span> <span class="attr">:key</span>=<span class="string">&quot;index&quot;</span> @<span class="attr">click</span>=<span class="string">&quot;form.label = item;activeIndex = index&quot;</span> <span class="attr">:class</span>=<span class="string">&quot;&#123;spanItem:true,spanActiveSchool:activeIndex === index&#125;&quot;</span>&gt;</span>&#123;&#123;item&#125;&#125;<span class="tag">&lt;/<span class="name">span</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 class="tag">&lt;<span class="name">div</span> <span class="attr">class</span>=<span class="string">&quot;divSave&quot;</span> @<span class="attr">click</span>=<span class="string">&quot;saveAddress&quot;</span>&gt;</span>保存地址<span class="tag">&lt;/<span class="name">div</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;divDelete&quot;</span> @<span class="attr">click</span>=<span class="string">&quot;deleteAddress&quot;</span> <span class="attr">v-if</span>=<span class="string">&quot;id&quot;</span>&gt;</span>删除地址<span class="tag">&lt;/<span class="name">div</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 class="tag">&lt;/<span class="name">div</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">script</span> <span class="attr">src</span>=<span class="string">&quot;../../backend/plugins/vue/vue.js&quot;</span>&gt;</span><span class="tag">&lt;/<span class="name">script</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">script</span> <span class="attr">src</span>=<span class="string">&quot;../../backend/plugins/element-ui/index.js&quot;</span>&gt;</span><span class="tag">&lt;/<span class="name">script</span>&gt;</span></span><br><span class="line">        <span class="comment">&lt;!-- 引入vant样式 --&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">script</span> <span class="attr">src</span>=<span class="string">&quot;./../js/vant.min.js&quot;</span>&gt;</span><span class="tag">&lt;/<span class="name">script</span>&gt;</span>       </span><br><span class="line">        <span class="tag">&lt;<span class="name">script</span> <span class="attr">src</span>=<span class="string">&quot;./../js/common.js&quot;</span>&gt;</span><span class="tag">&lt;/<span class="name">script</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">script</span> <span class="attr">src</span>=<span class="string">&quot;./../api/address.js&quot;</span>&gt;</span><span class="tag">&lt;/<span class="name">script</span>&gt;</span></span><br><span class="line">        <span class="comment">&lt;!-- 引入axios --&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">script</span> <span class="attr">src</span>=<span class="string">&quot;../../backend/plugins/axios/axios.min.js&quot;</span>&gt;</span><span class="tag">&lt;/<span class="name">script</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">script</span> <span class="attr">src</span>=<span class="string">&quot;./../js/request.js&quot;</span>&gt;</span><span class="tag">&lt;/<span class="name">script</span>&gt;</span></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">new</span> <span class="title class_">Vue</span>(&#123;</span></span><br><span class="line"><span class="language-javascript">                <span class="attr">el</span>:<span class="string">&quot;#address_edit&quot;</span>,</span></span><br><span class="line"><span class="language-javascript">                <span class="title function_">data</span>(<span class="params"></span>)&#123;</span></span><br><span class="line"><span class="language-javascript">                    <span class="keyword">return</span> &#123;</span></span><br><span class="line"><span class="language-javascript">                        <span class="attr">title</span>:<span class="string">&#x27;新增收货地址&#x27;</span>,</span></span><br><span class="line"><span class="language-javascript">                        <span class="attr">form</span>:&#123;</span></span><br><span class="line"><span class="language-javascript">                            <span class="attr">consignee</span>:<span class="string">&#x27;&#x27;</span>,<span class="comment">//联系人</span></span></span><br><span class="line"><span class="language-javascript">                            <span class="attr">phone</span>:<span class="literal">undefined</span>,<span class="comment">//手机号</span></span></span><br><span class="line"><span class="language-javascript">                            <span class="attr">sex</span>:<span class="string">&#x27;1&#x27;</span>,<span class="comment">//0表示女 1 表示男</span></span></span><br><span class="line"><span class="language-javascript">                            <span class="attr">detail</span>:<span class="string">&#x27;&#x27;</span>,<span class="comment">//收货地址</span></span></span><br><span class="line"><span class="language-javascript">                            <span class="attr">label</span>:<span class="string">&#x27;公司&#x27;</span>,<span class="comment">//标签</span></span></span><br><span class="line"><span class="language-javascript">                        &#125;,</span></span><br><span class="line"><span class="language-javascript">                        <span class="attr">labelList</span>:[</span></span><br><span class="line"><span class="language-javascript">                            <span class="string">&#x27;无&#x27;</span>,<span class="string">&#x27;公司&#x27;</span>,<span class="string">&#x27;家&#x27;</span>,<span class="string">&#x27;学校&#x27;</span></span></span><br><span class="line"><span class="language-javascript">                        ],</span></span><br><span class="line"><span class="language-javascript">                        <span class="attr">id</span>:<span class="literal">undefined</span>,</span></span><br><span class="line"><span class="language-javascript">                        activeIndex :<span class="number">0</span></span></span><br><span class="line"><span class="language-javascript">                    &#125;</span></span><br><span class="line"><span class="language-javascript">                &#125;,</span></span><br><span class="line"><span class="language-javascript">                <span class="attr">computed</span>:&#123;&#125;,</span></span><br><span class="line"><span class="language-javascript">                <span class="title function_">created</span>(<span class="params"></span>)&#123;</span></span><br><span class="line"><span class="language-javascript">                    <span class="variable language_">this</span>.<span class="title function_">initData</span>()</span></span><br><span class="line"><span class="language-javascript">                &#125;,</span></span><br><span class="line"><span class="language-javascript">                <span class="title function_">mounted</span>(<span class="params"></span>)&#123;</span></span><br><span class="line"><span class="language-javascript">                &#125;,</span></span><br><span class="line"><span class="language-javascript">                <span class="attr">methods</span>:&#123;</span></span><br><span class="line"><span class="language-javascript">                    <span class="title function_">goBack</span>(<span class="params"></span>)&#123;</span></span><br><span class="line"><span class="language-javascript">                        history.<span class="title function_">go</span>(-<span class="number">1</span>)</span></span><br><span class="line"><span class="language-javascript">                    &#125;,</span></span><br><span class="line"><span class="language-javascript">                    <span class="keyword">async</span> <span class="title function_">initData</span>(<span class="params"></span>)&#123;</span></span><br><span class="line"><span class="language-javascript">                        <span class="keyword">const</span> params = <span class="title function_">parseUrl</span>(<span class="variable language_">window</span>.<span class="property">location</span>.<span class="property">search</span>)</span></span><br><span class="line"><span class="language-javascript">                        <span class="variable language_">this</span>.<span class="property">id</span> = params.<span class="property">id</span></span></span><br><span class="line"><span class="language-javascript">                        <span class="keyword">if</span>(params.<span class="property">id</span>)&#123;</span></span><br><span class="line"><span class="language-javascript">                            <span class="variable language_">this</span>.<span class="property">title</span> = <span class="string">&#x27;编辑收货地址&#x27;</span></span></span><br><span class="line"><span class="language-javascript">                            <span class="keyword">const</span> res = <span class="keyword">await</span> <span class="title function_">addressFindOneApi</span>(params.<span class="property">id</span>)</span></span><br><span class="line"><span class="language-javascript">                            <span class="keyword">if</span>(res.<span class="property">code</span> === <span class="number">1</span>)&#123;</span></span><br><span class="line"><span class="language-javascript">                                <span class="variable language_">this</span>.<span class="property">form</span> = res.<span class="property">data</span></span></span><br><span class="line"><span class="language-javascript">                            &#125;<span class="keyword">else</span>&#123;</span></span><br><span class="line"><span class="language-javascript">                                <span class="variable language_">this</span>.$notify(&#123; <span class="attr">type</span>:<span class="string">&#x27;warning&#x27;</span>, <span class="attr">message</span>:res.<span class="property">msg</span>&#125;);</span></span><br><span class="line"><span class="language-javascript">                            &#125;</span></span><br><span class="line"><span class="language-javascript">                        &#125;</span></span><br><span class="line"><span class="language-javascript">                    &#125;,</span></span><br><span class="line"><span class="language-javascript">                    <span class="keyword">async</span> <span class="title function_">saveAddress</span>(<span class="params"></span>)&#123;</span></span><br><span class="line"><span class="language-javascript">                        <span class="keyword">const</span> form = <span class="variable language_">this</span>.<span class="property">form</span></span></span><br><span class="line"><span class="language-javascript">                        <span class="keyword">if</span>(!form.<span class="property">consignee</span>)&#123;</span></span><br><span class="line"><span class="language-javascript">                            <span class="variable language_">this</span>.$notify(&#123; <span class="attr">type</span>:<span class="string">&#x27;warning&#x27;</span>, <span class="attr">message</span>:<span class="string">&#x27;请输入联系人&#x27;</span>&#125;);</span></span><br><span class="line"><span class="language-javascript">                            <span class="keyword">return</span> </span></span><br><span class="line"><span class="language-javascript">                        &#125;</span></span><br><span class="line"><span class="language-javascript">                        <span class="keyword">if</span>(!form.<span class="property">phone</span>)&#123;</span></span><br><span class="line"><span class="language-javascript">                            <span class="variable language_">this</span>.$notify(&#123; <span class="attr">type</span>:<span class="string">&#x27;warning&#x27;</span>, <span class="attr">message</span>:<span class="string">&#x27;请输入邮箱&#x27;</span>&#125;);</span></span><br><span class="line"><span class="language-javascript">                            <span class="keyword">return</span> </span></span><br><span class="line"><span class="language-javascript">                        &#125;</span></span><br><span class="line"><span class="language-javascript">                        <span class="keyword">if</span>(!form.<span class="property">detail</span>)&#123;</span></span><br><span class="line"><span class="language-javascript">                            <span class="variable language_">this</span>.$notify(&#123; <span class="attr">type</span>:<span class="string">&#x27;warning&#x27;</span>, <span class="attr">message</span>:<span class="string">&#x27;请输入收货地址&#x27;</span>&#125;);</span></span><br><span class="line"><span class="language-javascript">                            <span class="keyword">return</span> </span></span><br><span class="line"><span class="language-javascript">                        &#125;</span></span><br><span class="line"><span class="language-javascript">                        <span class="keyword">const</span> reg = <span class="regexp">/^\w+([-+.]\w+)*@\w+([-.]\w+)*\.\w+([-.]\w+)*$/</span></span></span><br><span class="line"><span class="language-javascript">                        <span class="keyword">if</span>(!reg.<span class="title function_">test</span>(form.<span class="property">phone</span>))&#123;</span></span><br><span class="line"><span class="language-javascript">                            <span class="variable language_">this</span>.$notify(&#123; <span class="attr">type</span>:<span class="string">&#x27;warning&#x27;</span>, <span class="attr">message</span>:<span class="string">&#x27;邮箱不合法&#x27;</span>&#125;);</span></span><br><span class="line"><span class="language-javascript">                            <span class="keyword">return</span>  </span></span><br><span class="line"><span class="language-javascript">                        &#125;</span></span><br><span class="line"><span class="language-javascript">                        <span class="keyword">let</span> res= &#123;&#125;</span></span><br><span class="line"><span class="language-javascript">                        <span class="keyword">if</span>(<span class="variable language_">this</span>.<span class="property">id</span>)&#123;</span></span><br><span class="line"><span class="language-javascript">                            res = <span class="keyword">await</span> <span class="title function_">updateAddressApi</span>(<span class="variable language_">this</span>.<span class="property">form</span>)</span></span><br><span class="line"><span class="language-javascript">                        &#125;<span class="keyword">else</span>&#123;</span></span><br><span class="line"><span class="language-javascript">                            res = <span class="keyword">await</span> <span class="title function_">addAddressApi</span>(<span class="variable language_">this</span>.<span class="property">form</span>)</span></span><br><span class="line"><span class="language-javascript">                        &#125;</span></span><br><span class="line"><span class="language-javascript">                        </span></span><br><span class="line"><span class="language-javascript">                        <span class="keyword">if</span>(res.<span class="property">code</span> === <span class="number">1</span>)&#123;</span></span><br><span class="line"><span class="language-javascript">                            <span class="variable language_">window</span>.<span class="title function_">requestAnimationFrame</span>(<span class="function">()=&gt;</span>&#123;</span></span><br><span class="line"><span class="language-javascript">                                <span class="variable language_">window</span>.<span class="property">location</span>.<span class="title function_">replace</span>(<span class="string">&#x27;/front/page/address.html&#x27;</span>)</span></span><br><span class="line"><span class="language-javascript">                            &#125;)</span></span><br><span class="line"><span class="language-javascript">                        &#125;<span class="keyword">else</span>&#123;</span></span><br><span class="line"><span class="language-javascript">                            <span class="variable language_">this</span>.$notify(&#123; <span class="attr">type</span>:<span class="string">&#x27;warning&#x27;</span>, <span class="attr">message</span>:res.<span class="property">msg</span>&#125;);</span></span><br><span class="line"><span class="language-javascript">                        &#125;</span></span><br><span class="line"><span class="language-javascript">                    &#125;,</span></span><br><span class="line"><span class="language-javascript">                    <span class="title function_">deleteAddress</span>(<span class="params"></span>)&#123;</span></span><br><span class="line"><span class="language-javascript">                        <span class="variable language_">this</span>.<span class="property">$dialog</span>.<span class="title function_">confirm</span>(&#123;</span></span><br><span class="line"><span class="language-javascript">                            <span class="attr">title</span>: <span class="string">&#x27;确认删除&#x27;</span>,</span></span><br><span class="line"><span class="language-javascript">                            <span class="attr">message</span>: <span class="string">&#x27;确认要删除当前地址吗？&#x27;</span>,</span></span><br><span class="line"><span class="language-javascript">                        &#125;)</span></span><br><span class="line"><span class="language-javascript">                        .<span class="title function_">then</span>( <span class="title function_">async</span> () =&gt; &#123;</span></span><br><span class="line"><span class="language-javascript">                            <span class="keyword">const</span> res = <span class="keyword">await</span> <span class="title function_">deleteAddressApi</span>(&#123;<span class="attr">ids</span>:<span class="variable language_">this</span>.<span class="property">id</span> &#125;)</span></span><br><span class="line"><span class="language-javascript">                            <span class="keyword">if</span>(res.<span class="property">code</span> === <span class="number">1</span>)&#123;</span></span><br><span class="line"><span class="language-javascript">                                <span class="variable language_">window</span>.<span class="title function_">requestAnimationFrame</span>(<span class="function">()=&gt;</span>&#123;</span></span><br><span class="line"><span class="language-javascript">                                    <span class="variable language_">window</span>.<span class="property">location</span>.<span class="title function_">replace</span>(<span class="string">&#x27;/front/page/address.html&#x27;</span>)</span></span><br><span class="line"><span class="language-javascript">                                &#125;)</span></span><br><span class="line"><span class="language-javascript">                            &#125;<span class="keyword">else</span>&#123;</span></span><br><span class="line"><span class="language-javascript">                                <span class="variable language_">this</span>.$notify(&#123; <span class="attr">type</span>:<span class="string">&#x27;warning&#x27;</span>, <span class="attr">message</span>:res.<span class="property">msg</span>&#125;);</span></span><br><span class="line"><span class="language-javascript">                            &#125;</span></span><br><span class="line"><span class="language-javascript">                        &#125;)</span></span><br><span class="line"><span class="language-javascript">                        .<span class="title function_">catch</span>(<span class="function">() =&gt;</span> &#123;</span></span><br><span class="line"><span class="language-javascript">                        &#125;);</span></span><br><span class="line"><span class="language-javascript">                    &#125;,</span></span><br><span class="line"><span class="language-javascript">                &#125;</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></li><li><p>填写表单，点击保存，发送请求</p></li></ul><blockquote><p>请求网址: <a href="http://localhost/addressBook">http://localhost/addressBook</a><br>请求方法: POST</p></blockquote><ul><li><p>请求路径是<code>/addressBook</code>，请求方式为<code>POST</code>，那么我们在<code>AddressBookController</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></pre></td><td class="code"><pre><span class="line"><span class="meta">@PostMapping</span></span><br><span class="line"><span class="keyword">public</span> Result&lt;AddressBook&gt; <span class="title function_">addAddress</span><span class="params">(<span class="meta">@RequestBody</span> AddressBook addressBook)</span> &#123;</span><br><span class="line">    addressBook.setUserId(BaseContext.getCurrentId());</span><br><span class="line">    log.info(<span class="string">&quot;addressBook:&#123;&#125;&quot;</span>, addressBook);</span><br><span class="line">    addressBookService.save(addressBook);</span><br><span class="line">    <span class="keyword">return</span> Result.success(addressBook);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></li><li><p>添加完地址后的效果</p><div class="img-wrap"><div class="img-bg"><img class="img" src="https://raw.githubusercontent.com/silvan2077/markdown_pic/main/Picgo/20251106193205.png"/></div></div></li></ul><h3 id="设置默认地址"><a href="#设置默认地址" class="headerlink" title="设置默认地址"></a>设置默认地址</h3><ul><li>先来想想怎么设置默认地址<ul><li>默认地址，按理说数据库中，有且仅有一条数据为默认地址，也就是<code>is_default</code>字段为1</li></ul></li><li><p>如何保证整个表中的<code>is_default</code>字段只有一条为1</p><ul><li>每次设置默认地址的时候，将当前用户所有地址的<code>is_default</code>字段设为0，随后将当前地址的<code>is_default</code>字段设为1</li></ul></li><li><p>当我们点击上图的设为默认按钮的时候，会发送请求</p><blockquote><p>请求网址: <a href="http://localhost/addressBook/default">http://localhost/addressBook/default</a><br>请求方法: PUT</p></blockquote></li><li><p>请求路径为<code>/addressBook/default</code>，请求方式为<code>PUT</code>，现在在<code>AddressBookController</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></pre></td><td class="code"><pre><span class="line"><span class="meta">@PutMapping(&quot;/default&quot;)</span></span><br><span class="line"><span class="keyword">public</span> Result&lt;AddressBook&gt; <span class="title function_">setDefaultAddress</span><span class="params">(<span class="meta">@RequestBody</span> AddressBook addressBook)</span> &#123;</span><br><span class="line">    <span class="comment">//获取当前用户id</span></span><br><span class="line">    addressBook.setUserId(BaseContext.getCurrentId());</span><br><span class="line">    <span class="comment">//条件构造器</span></span><br><span class="line">    LambdaUpdateWrapper&lt;AddressBook&gt; queryWrapper = <span class="keyword">new</span> <span class="title class_">LambdaUpdateWrapper</span>&lt;&gt;();</span><br><span class="line">    <span class="comment">//条件：当前用户的地址</span></span><br><span class="line">    queryWrapper.eq(addressBook.getUserId() != <span class="literal">null</span>, AddressBook::getUserId, addressBook.getUserId());</span><br><span class="line">    <span class="comment">//将当前用户地址的is_default字段全部设为0</span></span><br><span class="line">    queryWrapper.set(AddressBook::getIsDefault, <span class="number">0</span>);</span><br><span class="line">    <span class="comment">//执行更新操作</span></span><br><span class="line">    addressBookService.update(queryWrapper);</span><br><span class="line">    <span class="comment">//随后再将当前地址的is_default字段设为1</span></span><br><span class="line">    addressBook.setIsDefault(<span class="number">1</span>);</span><br><span class="line">    <span class="comment">//再次执行更新操作</span></span><br><span class="line">    addressBookService.updateById(addressBook);</span><br><span class="line">    <span class="keyword">return</span> Result.success(addressBook);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></li></ul><div class="note warning no-icon flat"><p>这里的条件构造器是<code>LambdaUpdateWrapper</code>，而不是我们前面经常用的<code>LambdaQueryWrapper</code></p></div><h2 id="菜品展示"><a href="#菜品展示" class="headerlink" title="菜品展示"></a>菜品展示</h2><h3 id="需求分析-9"><a href="#需求分析-9" class="headerlink" title="需求分析"></a>需求分析</h3><ul><li>用户登陆成功之后，跳转到菜品页面，根据菜品分类来展示菜品和套餐</li><li>如果菜品设置了口味信息，则需要展示选择规格按钮，否则只展示+按钮（这部分前端来实现）</li></ul><h3 id="梳理交互过程-3"><a href="#梳理交互过程-3" class="headerlink" title="梳理交互过程"></a>梳理交互过程</h3><ol><li>页面(front/index.html)发送ajax请求，获取分类数据（菜品分类和套餐分类）</li><li>页面发送ajax请求，根据具体的菜品/套餐分类，展示对应分类中的具体菜品</li></ol><h3 id="前端分析-1"><a href="#前端分析-1" class="headerlink" title="前端分析"></a>前端分析</h3><ul><li><p>启动服务器，登录账号,看到登录到首页会发送两个请求</p><ul><li>分类<blockquote><p>请求网址: <code>http://localhost/category/list</code><br>请求方法: GET</p></blockquote></li><li>购物车<blockquote><p>请求网址: <code>http://localhost/shoppingCart/list</code><br>请求方法: GET</p></blockquote></li></ul></li><li><p>其中分类请求之前就写过了，且返回的状态码是200，但是页面并没有显示出来，看看前端代码来寻找原因：</p></li></ul><div class="tabs" id="菜品展示"><ul class="nav-tabs"><li class="tab active"><button type="button" data-href="#菜品展示-1">index.html</button></li><li class="tab"><button type="button" data-href="#菜品展示-2">categoryListApi</button></li><li class="tab"><button type="button" data-href="#菜品展示-3">/category/list</button></li><li class="tab"><button type="button" data-href="#菜品展示-4">cartListApi</button></li><li class="tab"><button type="button" data-href="#菜品展示-5">cartData.json</button></li></ul><div class="tab-contents"><div class="tab-item-content active" id="菜品展示-1"><p><code>Promise.all</code>在处理多个异步请求时，<strong>需要等待绑定的每个ajax请求返回数据以后才能正常显示</strong><br>虽然categoryListApi可以正常返回数据，但是cartListApi还不能，因为现在才正要开始处理，因此导致菜品数据没有正常显示</p><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><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="title function_">initData</span>(<span class="params"></span>)&#123;</span><br><span class="line">    <span class="title class_">Promise</span>.<span class="title function_">all</span>([<span class="title function_">categoryListApi</span>(),<span class="title function_">cartListApi</span>(&#123;&#125;)]).<span class="title function_">then</span>(<span class="function"><span class="params">res</span>=&gt;</span>&#123;</span><br><span class="line">    <span class="comment">//获取分类数据</span></span><br><span class="line">    <span class="keyword">if</span>(res[<span class="number">0</span>].<span class="property">code</span> === <span class="number">1</span>)&#123;</span><br><span class="line">        <span class="variable language_">this</span>.<span class="property">categoryList</span> = res[<span class="number">0</span>].<span class="property">data</span></span><br><span class="line">        <span class="keyword">if</span>(<span class="title class_">Array</span>.<span class="title function_">isArray</span>(res[<span class="number">0</span>].<span class="property">data</span>) &amp;&amp; res[<span class="number">0</span>].<span class="property">data</span>.<span class="property">length</span> &gt; <span class="number">0</span>)&#123;</span><br><span class="line">        <span class="variable language_">this</span>.<span class="property">categoryId</span> = res[<span class="number">0</span>].<span class="property">data</span>[<span class="number">0</span>].<span class="property">id</span></span><br><span class="line">        <span class="keyword">if</span>(res[<span class="number">0</span>].<span class="property">data</span>[<span class="number">0</span>].<span class="property">type</span> === <span class="number">1</span>)&#123;</span><br><span class="line">            <span class="variable language_">this</span>.<span class="title function_">getDishList</span>()</span><br><span class="line">        &#125;<span class="keyword">else</span>&#123;</span><br><span class="line">            <span class="variable language_">this</span>.<span class="title function_">getSetmealData</span>()</span><br><span class="line">        &#125;</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="variable language_">this</span>.$notify(&#123; <span class="attr">type</span>:<span class="string">&#x27;warning&#x27;</span>, <span class="attr">message</span>:res[<span class="number">0</span>].<span class="property">msg</span>&#125;);</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>(res[<span class="number">1</span>].<span class="property">code</span> === <span class="number">1</span>)&#123;</span><br><span class="line">    <span class="variable language_">this</span>.<span class="property">cartData</span> = res[<span class="number">1</span>].<span class="property">data</span></span><br><span class="line">    &#125;<span class="keyword">else</span>&#123;</span><br><span class="line">        <span class="variable language_">this</span>.$notify(&#123; <span class="attr">type</span>:<span class="string">&#x27;warning&#x27;</span>, <span class="attr">message</span>:res[<span class="number">1</span>].<span class="property">msg</span>&#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><button type="button" class="tab-to-top" aria-label="scroll to top"><i class="fas fa-arrow-up"></i></button></div><div class="tab-item-content" id="菜品展示-2"><p>该请求路径之前写过，能够正常返回<br><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><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">function</span> <span class="title function_">categoryListApi</span>(<span class="params"></span>) &#123;</span><br><span class="line">    <span class="keyword">return</span> $axios(&#123;</span><br><span class="line">        <span class="string">&#x27;url&#x27;</span>: <span class="string">&#x27;/category/list&#x27;</span>,</span><br><span class="line">        <span class="string">&#x27;method&#x27;</span>: <span class="string">&#x27;get&#x27;</span>,</span><br><span class="line">    &#125;)</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></p><button type="button" class="tab-to-top" aria-label="scroll to top"><i class="fas fa-arrow-up"></i></button></div><div class="tab-item-content" id="菜品展示-3"><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><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">@GetMapping(&quot;/list&quot;)</span></span><br><span class="line"><span class="keyword">public</span> Result&lt;List&lt;Dish&gt;&gt; <span class="title function_">get</span><span class="params">(Dish dish)</span> &#123;</span><br><span class="line">    <span class="comment">//条件查询器</span></span><br><span class="line">    LambdaQueryWrapper&lt;Dish&gt; queryWrapper = <span class="keyword">new</span> <span class="title class_">LambdaQueryWrapper</span>&lt;&gt;();</span><br><span class="line">    <span class="comment">//根据传进来的categoryId查询</span></span><br><span class="line">    queryWrapper.eq(dish.getCategoryId() != <span class="literal">null</span>, Dish::getCategoryId, dish.getCategoryId());</span><br><span class="line">    <span class="comment">//只查询状态为1的菜品（在售菜品）</span></span><br><span class="line">    queryWrapper.eq(Dish::getStatus, <span class="number">1</span>);</span><br><span class="line">    <span class="comment">//简单排下序</span></span><br><span class="line">    queryWrapper.orderByAsc(Dish::getSort).orderByDesc(Dish::getUpdateTime);</span><br><span class="line">    <span class="comment">//获取查询到的结果作为返回值</span></span><br><span class="line">    List&lt;Dish&gt; list = dishService.list(queryWrapper);</span><br><span class="line">    <span class="keyword">return</span> Result.success(list);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><button type="button" class="tab-to-top" aria-label="scroll to top"><i class="fas fa-arrow-up"></i></button></div><div class="tab-item-content" id="菜品展示-4"><p>这里是因为购物车数据还没有写，因此应当先将url设置为一个空的json文件，先让页面正常显示<br><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><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">function</span> <span class="title function_">cartListApi</span>(<span class="params">data</span>) &#123;</span><br><span class="line">    <span class="keyword">return</span> $axios(&#123;</span><br><span class="line">        <span class="string">&#x27;url&#x27;</span>: <span class="string">&#x27;/shoppingCart/list&#x27;</span>,</span><br><span class="line">        <span class="comment">//&#x27;url&#x27;: &#x27;/front/cartData.json&#x27;,</span></span><br><span class="line">        <span class="string">&#x27;method&#x27;</span>: <span class="string">&#x27;get&#x27;</span>,</span><br><span class="line">        <span class="attr">params</span>: &#123;...data&#125;</span><br><span class="line">    &#125;)</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></p><button type="button" class="tab-to-top" aria-label="scroll to top"><i class="fas fa-arrow-up"></i></button></div><div class="tab-item-content" id="菜品展示-5"><figure class="highlight json"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="punctuation">&#123;</span><span class="attr">&quot;code&quot;</span><span class="punctuation">:</span><span class="number">1</span><span class="punctuation">,</span><span class="attr">&quot;msg&quot;</span><span class="punctuation">:</span><span class="literal"><span class="keyword">null</span></span><span class="punctuation">,</span><span class="attr">&quot;data&quot;</span><span class="punctuation">:</span><span class="punctuation">[</span><span class="punctuation">]</span><span class="punctuation">,</span><span class="attr">&quot;map&quot;</span><span class="punctuation">:</span><span class="punctuation">&#123;</span><span class="punctuation">&#125;</span><span class="punctuation">&#125;</span></span><br></pre></td></tr></table></figure><button type="button" class="tab-to-top" aria-label="scroll to top"><i class="fas fa-arrow-up"></i></button></div></div></div><ul><li>此时再次重启服务器，打开无痕模式，看看首页是否能显示分类数据</li></ul><div class="img-wrap"><div class="img-bg"><img class="img" src="https://raw.githubusercontent.com/silvan2077/markdown_pic/main/Picgo/20251106210326.png"/></div></div><ul><li><p>现在还存在一个问题，我们的菜品是有口味数据的，那么这里的按钮不该是一个+，而应该是选择规格</p><figure class="highlight html"><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="tag">&lt;<span class="name">div</span> <span class="attr">class</span>=<span class="string">&quot;divTypes&quot;</span> <span class="attr">v-if</span>=<span class="string">&quot;detailsDialog.item.flavors &amp;&amp; detailsDialog.item.flavors.length &gt; 0 &amp;&amp; !detailsDialog.item.number &quot;</span> </span></span><br><span class="line"><span class="tag">@<span class="attr">click</span> =<span class="string">&quot;chooseFlavorClick(detailsDialog.item)&quot;</span>&gt;</span>选择规格<span class="tag">&lt;/<span class="name">div</span>&gt;</span>               </span><br></pre></td></tr></table></figure><p>通过前端代码可以分析得出，根据服务器返回的结果是否有flavors字段来决定，但此时在后端返回的是<code>List&lt;Dish&gt;</code>，其中并没有<code>flavors</code>属性，所以应该修改为返回<code>DishDto</code>。</p><h3 id="选择规格"><a href="#选择规格" class="headerlink" title="选择规格"></a>选择规格</h3></li><li><p>修改原本的list方法</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="meta">@GetMapping(&quot;/list&quot;)</span></span><br><span class="line"><span class="keyword">public</span> Result&lt;List&lt;DishDto&gt;&gt; <span class="title function_">get</span><span class="params">(Dish dish)</span> &#123;</span><br><span class="line">    <span class="comment">//条件查询器</span></span><br><span class="line">    LambdaQueryWrapper&lt;Dish&gt; queryWrapper = <span class="keyword">new</span> <span class="title class_">LambdaQueryWrapper</span>&lt;&gt;();</span><br><span class="line">    <span class="comment">//根据传进来的categoryId查询</span></span><br><span class="line">    queryWrapper.eq(dish.getCategoryId() != <span class="literal">null</span>, Dish::getCategoryId, dish.getCategoryId());</span><br><span class="line">    <span class="comment">//只查询状态为1的菜品（在售菜品）</span></span><br><span class="line">    queryWrapper.eq(Dish::getStatus, <span class="number">1</span>);</span><br><span class="line">    <span class="comment">//简单排下序，其实也没啥太大作用</span></span><br><span class="line">    queryWrapper.orderByAsc(Dish::getSort).orderByDesc(Dish::getUpdateTime);</span><br><span class="line">    <span class="comment">//获取查询到的结果作为返回值</span></span><br><span class="line">    List&lt;Dish&gt; list = dishService.list(queryWrapper);</span><br><span class="line">    log.info(<span class="string">&quot;查询到的菜品信息list:&#123;&#125;&quot;</span>,list);</span><br><span class="line">    <span class="comment">//item就是list中的每一条数据，相当于遍历了</span></span><br><span class="line">    List&lt;DishDto&gt; dishDtoList = list.stream().map((item) -&gt; &#123;</span><br><span class="line">        <span class="comment">//创建一个dishDto对象</span></span><br><span class="line">        <span class="type">DishDto</span> <span class="variable">dishDto</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">DishDto</span>();</span><br><span class="line">        <span class="comment">//将item的属性全都copy到dishDto里</span></span><br><span class="line">        BeanUtils.copyProperties(item, dishDto);</span><br><span class="line">        <span class="comment">//由于dish表中没有categoryName属性，只存了categoryId</span></span><br><span class="line">        <span class="type">Long</span> <span class="variable">categoryId</span> <span class="operator">=</span> item.getCategoryId();</span><br><span class="line">        <span class="comment">//所以我们要根据categoryId查询对应的category</span></span><br><span class="line">        <span class="type">Category</span> <span class="variable">category</span> <span class="operator">=</span> categoryService.getById(categoryId);</span><br><span class="line">        <span class="keyword">if</span> (category != <span class="literal">null</span>) &#123;</span><br><span class="line">            <span class="comment">//然后取出categoryName，赋值给dishDto</span></span><br><span class="line">            dishDto.setCategoryName(category.getName());</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="comment">//然后获取一下菜品id，根据菜品id去dishFlavor表中查询对应的口味，并赋值给dishDto</span></span><br><span class="line">        <span class="type">Long</span> <span class="variable">itemId</span> <span class="operator">=</span> item.getId();</span><br><span class="line">        <span class="comment">//条件构造器</span></span><br><span class="line">        LambdaQueryWrapper&lt;DishFlavor&gt; lambdaQueryWrapper = <span class="keyword">new</span> <span class="title class_">LambdaQueryWrapper</span>&lt;&gt;();</span><br><span class="line">        <span class="comment">//条件就是菜品id</span></span><br><span class="line">        lambdaQueryWrapper.eq(itemId != <span class="literal">null</span>, DishFlavor::getDishId, itemId);</span><br><span class="line">        <span class="comment">//根据菜品id，查询到菜品口味</span></span><br><span class="line">        List&lt;DishFlavor&gt; flavors = dishFlavorService.list(lambdaQueryWrapper);</span><br><span class="line">        <span class="comment">//赋给dishDto的对应属性</span></span><br><span class="line">        dishDto.setFlavors(flavors);</span><br><span class="line">        <span class="comment">//并将dishDto作为结果返回</span></span><br><span class="line">        <span class="keyword">return</span> dishDto;</span><br><span class="line">        <span class="comment">//将所有返回结果收集起来，封装成List</span></span><br><span class="line">    &#125;).collect(Collectors.toList());</span><br><span class="line">    <span class="keyword">return</span> Result.success(dishDtoList);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></li><li><p>至此，菜品展示功能就做好了</p></li></ul><h2 id="套餐展示"><a href="#套餐展示" class="headerlink" title="套餐展示"></a>套餐展示</h2><ul><li><p>菜品展示和套餐展示类似，但用的不是同一个controller，因此还需要再设置一遍</p><blockquote><p>请求网址: <a href="http://localhost/setmeal/list?categoryId=1413342269393674242&amp;status=1">http://localhost/setmeal/list?categoryId=1413342269393674242&amp;status=1</a><br>请求方法: GET</p></blockquote></li><li><p>那么我们现在就在<code>SetmealController</code>中编写对应的方法<br>套餐没有口味数据，因此更加简单</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">@GetMapping(&quot;/list&quot;)</span></span><br><span class="line"><span class="keyword">public</span> Result&lt;List&lt;Setmeal&gt;&gt; <span class="title function_">list</span><span class="params">(Setmeal setmeal)</span> &#123;</span><br><span class="line">    <span class="comment">//条件构造器</span></span><br><span class="line">    LambdaQueryWrapper&lt;Setmeal&gt; queryWrapper = <span class="keyword">new</span> <span class="title class_">LambdaQueryWrapper</span>&lt;&gt;();</span><br><span class="line">    <span class="comment">//添加条件</span></span><br><span class="line">    queryWrapper.eq(setmeal.getCategoryId() != <span class="literal">null</span>, Setmeal::getCategoryId, setmeal.getCategoryId());</span><br><span class="line">    queryWrapper.eq(setmeal.getStatus() != <span class="literal">null</span>, Setmeal::getStatus, <span class="number">1</span>);</span><br><span class="line">    <span class="comment">//排序</span></span><br><span class="line">    queryWrapper.orderByDesc(Setmeal::getUpdateTime);</span><br><span class="line">    List&lt;Setmeal&gt; setmealList = setmealService.list(queryWrapper);</span><br><span class="line">    <span class="keyword">return</span> Result.success(setmealList);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></li></ul><h2 id="购物车"><a href="#购物车" class="headerlink" title="购物车"></a>购物车</h2><h3 id="需求分析-10"><a href="#需求分析-10" class="headerlink" title="需求分析"></a>需求分析</h3><ul><li>移动端用户可以将菜品/套餐添加到购物车</li><li>对于菜品来说，如果设置了口味信息，则需要选择规格后才能加入购物车（前端实现）</li><li>对于套餐来说，可以直接点击当前套餐加入购物车</li><li>在购物车中可以修改菜品/套餐的数量，也可以清空购物车</li></ul><h3 id="数据模型-4"><a href="#数据模型-4" class="headerlink" title="数据模型"></a>数据模型</h3><div class="img-wrap"><div class="img-bg"><img class="img" src="https://raw.githubusercontent.com/silvan2077/markdown_pic/main/Picgo/20251106211721.png"/></div></div><h3 id="梳理交互过程-4"><a href="#梳理交互过程-4" class="headerlink" title="梳理交互过程"></a>梳理交互过程</h3><ol><li>点击加入购物车按钮，页面发送ajax请求，请求服务端，将菜品/套餐添加到购物车</li><li>点击购物车图标，页面发送ajax请求，请求服务端，查询购物车中的菜品和套餐</li><li>点击清空购物车按钮，页面发送ajax请求，请求服务端来执行清空购物车操作</li></ol><h3 id="准备工作-6"><a href="#准备工作-6" class="headerlink" title="准备工作"></a>准备工作</h3><p>在开发业务功能之前，先将需要用到的类和接口的基本结构都创建好</p><ol><li><p>实体类<code>ShoppingCart</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></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_">ShoppingCart</span> <span class="keyword">implements</span> <span class="title class_">Serializable</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> <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> Long id;</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><br><span class="line"></span><br><span class="line">    <span class="comment">//用户id</span></span><br><span class="line">    <span class="keyword">private</span> Long userId;</span><br><span class="line"></span><br><span class="line">    <span class="comment">//菜品id</span></span><br><span class="line">    <span class="keyword">private</span> Long dishId;</span><br><span class="line"></span><br><span class="line">    <span class="comment">//套餐id</span></span><br><span class="line">    <span class="keyword">private</span> Long setmealId;</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 dishFlavor;</span><br><span class="line"></span><br><span class="line">    <span class="comment">//数量</span></span><br><span class="line">    <span class="keyword">private</span> Integer number;</span><br><span class="line"></span><br><span class="line">    <span class="comment">//金额</span></span><br><span class="line">    <span class="keyword">private</span> BigDecimal amount;</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 image;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">private</span> LocalDateTime createTime;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></li><li><p><code>Mapper</code>接口<code>ShoppingCartMapper</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></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_">ShoppingCartMapper</span> <span class="keyword">extends</span> <span class="title class_">BaseMapper</span>&lt;ShoppingCart&gt; &#123;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></li><li><p>业务层接口<code>ShoppingCartService</code></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="keyword">public</span> <span class="keyword">interface</span> <span class="title class_">ShoppingCartService</span> <span class="keyword">extends</span> <span class="title class_">IService</span>&lt;ShoppingCart&gt; &#123;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></li><li><p>业务层实现类<code>ShoppingCartServiceImpl</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></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_">ShoppingCartServiceImpl</span> <span class="keyword">extends</span> <span class="title class_">ServiceImpl</span>&lt;ShoppingCartMapper, ShoppingCart&gt; <span class="keyword">implements</span> <span class="title class_">ShoppingCartService</span> &#123;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></li><li><p>控制层<code>ShoppingCartController</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></pre></td><td class="code"><pre><span class="line"><span class="meta">@RestController</span></span><br><span class="line"><span class="meta">@Slf4j</span></span><br><span class="line"><span class="meta">@RequestMapping(&quot;/shoppingCart&quot;)</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">ShoppingCartController</span> &#123;</span><br><span class="line">    <span class="meta">@Autowired</span></span><br><span class="line">    <span class="keyword">private</span> ShoppingCartService shoppingCartService;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></li></ol><h3 id="代码开发-6"><a href="#代码开发-6" class="headerlink" title="代码开发"></a>代码开发</h3><h4 id="加入购物车"><a href="#加入购物车" class="headerlink" title="加入购物车"></a>加入购物车</h4><ul><li><p>点击<code>加入购物车</code>后，页面会发送请求</p><blockquote><p>请求网址: <a href="http://localhost/shoppingCart/add">http://localhost/shoppingCart/add</a><br>请求方法: POST</p></blockquote></li><li><p>以json的形式将数据发给服务端</p><div class="img-wrap"><div class="img-bg"><img class="img" src="https://raw.githubusercontent.com/silvan2077/markdown_pic/main/Picgo/20251106212301.png"/></div></div></li></ul><ul><li><p>那么我们在<code>ShoppingCartController</code>添加对应的方法<br>养成随时测试的习惯</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></pre></td><td class="code"><pre><span class="line"><span class="meta">@PostMapping(&quot;/add&quot;)</span></span><br><span class="line"><span class="keyword">public</span> Result&lt;ShoppingCart&gt; <span class="title function_">add</span><span class="params">(<span class="meta">@RequestBody</span> ShoppingCart shoppingCart)</span>&#123;</span><br><span class="line">    log.info(<span class="string">&quot;购物车添加信息：&#123;&#125;&quot;</span>,shoppingCart);</span><br><span class="line">    <span class="keyword">return</span> <span class="literal">null</span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></li><li><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></pre></td><td class="code"><pre><span class="line"><span class="meta">@PostMapping(&quot;/add&quot;)</span></span><br><span class="line"><span class="keyword">public</span> Result&lt;ShoppingCart&gt; <span class="title function_">add</span><span class="params">(<span class="meta">@RequestBody</span> ShoppingCart shoppingCart)</span> &#123;</span><br><span class="line">    log.info(<span class="string">&quot;shoppingCart=&#123;&#125;&quot;</span>, shoppingCart);</span><br><span class="line">    <span class="comment">//获取当前用户id</span></span><br><span class="line">    <span class="type">Long</span> <span class="variable">currentId</span> <span class="operator">=</span> BaseContext.getCurrentId();</span><br><span class="line">    <span class="comment">//设置当前用户id</span></span><br><span class="line">    shoppingCart.setUserId(currentId);</span><br><span class="line">    <span class="comment">//获取当前菜品id</span></span><br><span class="line">    <span class="type">Long</span> <span class="variable">dishId</span> <span class="operator">=</span> shoppingCart.getDishId();</span><br><span class="line">    <span class="comment">//条件构造器</span></span><br><span class="line">    LambdaQueryWrapper&lt;ShoppingCart&gt; queryWrapper = <span class="keyword">new</span> <span class="title class_">LambdaQueryWrapper</span>&lt;&gt;();</span><br><span class="line">    <span class="comment">//判断添加的是菜品还是套餐</span></span><br><span class="line">    <span class="keyword">if</span> (dishId != <span class="literal">null</span>) &#123;</span><br><span class="line">        queryWrapper.eq(ShoppingCart::getDishId, dishId);</span><br><span class="line">    &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">        queryWrapper.eq(ShoppingCart::getSetmealId, shoppingCart.getSetmealId());</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="comment">//查询当前菜品或者套餐是否在购物车中</span></span><br><span class="line">    <span class="type">ShoppingCart</span> <span class="variable">cartServiceOne</span> <span class="operator">=</span> shoppingCartService.getOne(queryWrapper);</span><br><span class="line">    <span class="keyword">if</span> (cartServiceOne != <span class="literal">null</span>) &#123;</span><br><span class="line">        <span class="comment">//如果已存在就在当前的数量上加1</span></span><br><span class="line">        <span class="type">Integer</span> <span class="variable">number</span> <span class="operator">=</span> cartServiceOne.getNumber();</span><br><span class="line">        cartServiceOne.setNumber(number + <span class="number">1</span>);</span><br><span class="line">        shoppingCartService.updateById(cartServiceOne);</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">        shoppingCart.setCreateTime(LocalDateTime.now());</span><br><span class="line">        <span class="comment">//如果不存在，则添加到购物车，数量默认为1</span></span><br><span class="line">        shoppingCartService.save(shoppingCart);</span><br><span class="line">        <span class="comment">//这里是为了统一结果，最后都返回cartServiceOne会比较方便</span></span><br><span class="line">        cartServiceOne = shoppingCart;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">return</span> Result.success(cartServiceOne);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></li><li><p>此时可以测试能否成功添加购物车，不过现在还不会显示，因为之前设置前端页面为死数据，现在可以去数据库查看购物车的数据是否添加上</p></li></ul><h4 id="查看购物车"><a href="#查看购物车" class="headerlink" title="查看购物车"></a>查看购物车</h4><ul><li><p>之前为了页面展示不报错，直接将购物车的地址换成了一个死数据，现在购物车的功能以及完成，因此应当换回真数据</p><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><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">function</span> <span class="title function_">cartListApi</span>(<span class="params">data</span>) &#123;</span><br><span class="line">    <span class="keyword">return</span> $axios(&#123;</span><br><span class="line">        <span class="comment">// &#x27;url&#x27;: &#x27;/shoppingCart/list&#x27;,</span></span><br><span class="line">        <span class="string">&#x27;url&#x27;</span>: <span class="string">&#x27;/front/cartData.json&#x27;</span>,</span><br><span class="line">        <span class="string">&#x27;method&#x27;</span>: <span class="string">&#x27;get&#x27;</span>,</span><br><span class="line">        <span class="attr">params</span>: &#123;...data&#125;</span><br><span class="line">    &#125;)</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></li></ul><blockquote><p>请求路径：<a href="http://localhost/shoppingCart/list">http://localhost/shoppingCart/list</a><br>请求方式：GET</p></blockquote><ul><li><p>在<code>ShoppingCartController</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="meta">@GetMapping(&quot;/list&quot;)</span></span><br><span class="line"><span class="keyword">public</span> Result&lt;List&lt;ShoppingCart&gt;&gt; <span class="title function_">list</span><span class="params">()</span> &#123;</span><br><span class="line">    LambdaQueryWrapper&lt;ShoppingCart&gt; queryWrapper = <span class="keyword">new</span> <span class="title class_">LambdaQueryWrapper</span>&lt;&gt;();</span><br><span class="line">    <span class="type">Long</span> <span class="variable">userId</span> <span class="operator">=</span> BaseContext.getCurrentId();</span><br><span class="line">    queryWrapper.eq(ShoppingCart::getUserId, userId);</span><br><span class="line">    List&lt;ShoppingCart&gt; shoppingCarts = shoppingCartService.list(queryWrapper);</span><br><span class="line">    <span class="keyword">return</span> Result.success(shoppingCarts);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></li></ul><ul><li>此时就可以添加菜品/套餐到购物车中了，不过现在还不能完成购物车的其他功能，比如减少购物车中的数量，删除购物车中的商品等</li></ul><h4 id="清空购物车"><a href="#清空购物车" class="headerlink" title="清空购物车"></a>清空购物车</h4><ul><li><p>点击清空按钮，发现此时又发送了请求</p><blockquote><p>请求网址: <a href="http://localhost/shoppingCart/clean">http://localhost/shoppingCart/clean</a><br>请求方法: DELETE</p></blockquote></li><li><p>清空购物车比较简单，只需要获取用户id，然后去<code>shopping__cart</code>表中删除对应id的数据即可<br>在<code>ShoppingCartController</code>中编写对应的方法</p></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></pre></td><td class="code"><pre><span class="line"><span class="meta">@DeleteMapping(&quot;/clean&quot;)</span></span><br><span class="line"><span class="keyword">public</span> Result&lt;String&gt; <span class="title function_">clean</span><span class="params">()</span> &#123;</span><br><span class="line">    <span class="comment">//条件构造器</span></span><br><span class="line">    LambdaQueryWrapper&lt;ShoppingCart&gt; queryWrapper = <span class="keyword">new</span> <span class="title class_">LambdaQueryWrapper</span>&lt;&gt;();</span><br><span class="line">    <span class="comment">//获取当前用户id</span></span><br><span class="line">    <span class="type">Long</span> <span class="variable">userId</span> <span class="operator">=</span> BaseContext.getCurrentId();</span><br><span class="line">    queryWrapper.eq(userId != <span class="literal">null</span>, ShoppingCart::getUserId, userId);</span><br><span class="line">    <span class="comment">//删除当前用户id的所有购物车数据</span></span><br><span class="line">    shoppingCartService.remove(queryWrapper);</span><br><span class="line">    <span class="keyword">return</span> Result.success(<span class="string">&quot;成功清空购物车&quot;</span>);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h2 id="用户下单"><a href="#用户下单" class="headerlink" title="用户下单"></a>用户下单</h2><h3 id="需求分析-11"><a href="#需求分析-11" class="headerlink" title="需求分析"></a>需求分析</h3><ul><li>移动端用户将菜品或者套餐加入购物车后，可以点击购物车中的去结算按钮，页面跳转到订单确认页面，点击去支付按钮，完成下单操作</li></ul><h3 id="数据模型-5"><a href="#数据模型-5" class="headerlink" title="数据模型"></a>数据模型</h3><p>户下单业务对应的数据表为<code>orders</code>表和<code>order_detail</code>表</p><div class="tabs" id="用户下单数据模型"><ul class="nav-tabs"><li class="tab active"><button type="button" data-href="#用户下单数据模型-1">orders表</button></li><li class="tab"><button type="button" data-href="#用户下单数据模型-2">order_detail表</button></li></ul><div class="tab-contents"><div class="tab-item-content active" id="用户下单数据模型-1"><div class="img-wrap"><div class="img-bg"><img class="img" src="https://raw.githubusercontent.com/silvan2077/markdown_pic/main/Picgo/20251106214008.png"/></div></div><button type="button" class="tab-to-top" aria-label="scroll to top"><i class="fas fa-arrow-up"></i></button></div><div class="tab-item-content" id="用户下单数据模型-2"><div class="img-wrap"><div class="img-bg"><img class="img" src="https://raw.githubusercontent.com/silvan2077/markdown_pic/main/Picgo/20251106214106.png"/></div></div><button type="button" class="tab-to-top" aria-label="scroll to top"><i class="fas fa-arrow-up"></i></button></div></div></div><h3 id="梳理交互过程-5"><a href="#梳理交互过程-5" class="headerlink" title="梳理交互过程"></a>梳理交互过程</h3><ol><li>在购物车中点击去结算按钮，页面跳转到订单确认页面</li><li>在订单确认页面中，发送ajax请求，请求服务端，获取当前登录用户的默认地址</li><li>在订单确认页面，发送ajax请求，请求服务端，获取当前登录用户的购物车数据</li><li>在订单确认页面点击去支付按钮，发送ajax请求，请求服务端，完成下单操作</li></ol><h3 id="准备工作-7"><a href="#准备工作-7" class="headerlink" title="准备工作"></a>准备工作</h3><ol><li>实体类<code>Orders</code>和<code>OrderDetail</code><div class="tabs" id="orders"><ul class="nav-tabs"><li class="tab active"><button type="button" data-href="#orders-1">orders</button></li><li class="tab"><button type="button" data-href="#orders-2">orderDetail</button></li></ul><div class="tab-contents"><div class="tab-item-content active" id="orders-1"><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><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">/**</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_">Orders</span> <span class="keyword">implements</span> <span class="title class_">Serializable</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> <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> Long id;</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 number;</span><br><span class="line"></span><br><span class="line">    <span class="comment">//订单状态 1待付款，2待派送，3已派送，4已完成，5已取消</span></span><br><span class="line">    <span class="keyword">private</span> Integer status;</span><br><span class="line"></span><br><span class="line"></span><br><span class="line">    <span class="comment">//下单用户id</span></span><br><span class="line">    <span class="keyword">private</span> Long userId;</span><br><span class="line"></span><br><span class="line">    <span class="comment">//地址id</span></span><br><span class="line">    <span class="keyword">private</span> Long addressBookId;</span><br><span class="line"></span><br><span class="line"></span><br><span class="line">    <span class="comment">//下单时间</span></span><br><span class="line">    <span class="keyword">private</span> LocalDateTime orderTime;</span><br><span class="line"></span><br><span class="line"></span><br><span class="line">    <span class="comment">//结账时间</span></span><br><span class="line">    <span class="keyword">private</span> LocalDateTime checkoutTime;</span><br><span class="line"></span><br><span class="line"></span><br><span class="line">    <span class="comment">//支付方式 1微信，2支付宝</span></span><br><span class="line">    <span class="keyword">private</span> Integer payMethod;</span><br><span class="line"></span><br><span class="line"></span><br><span class="line">    <span class="comment">//实收金额</span></span><br><span class="line">    <span class="keyword">private</span> BigDecimal amount;</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 remark;</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="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="keyword">private</span> String address;</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 consignee;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><button type="button" class="tab-to-top" aria-label="scroll to top"><i class="fas fa-arrow-up"></i></button></div><div class="tab-item-content" id="orders-2"><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><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">@Data</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">OrderDetail</span> <span class="keyword">implements</span> <span class="title class_">Serializable</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> <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> Long id;</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><br><span class="line"></span><br><span class="line">    <span class="comment">//订单id</span></span><br><span class="line">    <span class="keyword">private</span> Long orderId;</span><br><span class="line"></span><br><span class="line"></span><br><span class="line">    <span class="comment">//菜品id</span></span><br><span class="line">    <span class="keyword">private</span> Long dishId;</span><br><span class="line"></span><br><span class="line"></span><br><span class="line">    <span class="comment">//套餐id</span></span><br><span class="line">    <span class="keyword">private</span> Long setmealId;</span><br><span class="line"></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 dishFlavor;</span><br><span class="line"></span><br><span class="line"></span><br><span class="line">    <span class="comment">//数量</span></span><br><span class="line">    <span class="keyword">private</span> Integer number;</span><br><span class="line"></span><br><span class="line">    <span class="comment">//金额</span></span><br><span class="line">    <span class="keyword">private</span> BigDecimal amount;</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 image;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><button type="button" class="tab-to-top" aria-label="scroll to top"><i class="fas fa-arrow-up"></i></button></div></div></div></li><li>Mapper接口<code>OrdersMapper</code>和<code>OrderDetailMapper</code><div class="tabs" id="ordersmapper"><ul class="nav-tabs"><li class="tab active"><button type="button" data-href="#ordersmapper-1">ordersMapper</button></li><li class="tab"><button type="button" data-href="#ordersmapper-2">orderDetailMapper</button></li></ul><div class="tab-contents"><div class="tab-item-content active" id="ordersmapper-1"><figure class="highlight java"><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="meta">@Mapper</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">interface</span> <span class="title class_">OrderMapper</span> <span class="keyword">extends</span> <span class="title class_">BaseMapper</span>&lt;Orders&gt; &#123;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><button type="button" class="tab-to-top" aria-label="scroll to top"><i class="fas fa-arrow-up"></i></button></div><div class="tab-item-content" id="ordersmapper-2"><figure class="highlight java"><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="meta">@Mapper</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">interface</span> <span class="title class_">OrderDetailMapper</span> <span class="keyword">extends</span> <span class="title class_">BaseMapper</span>&lt;OrderDetail&gt; &#123;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><button type="button" class="tab-to-top" aria-label="scroll to top"><i class="fas fa-arrow-up"></i></button></div></div></div></li><li>Service接口<code>OrdersService</code>和<code>OrderDetailService</code><div class="tabs" id="ordersservice"><ul class="nav-tabs"><li class="tab active"><button type="button" data-href="#ordersservice-1">ordersService</button></li><li class="tab"><button type="button" data-href="#ordersservice-2">orderDetailService</button></li></ul><div class="tab-contents"><div class="tab-item-content active" id="ordersservice-1"><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="keyword">public</span> <span class="keyword">interface</span> <span class="title class_">OrderService</span> <span class="keyword">extends</span> <span class="title class_">IService</span>&lt;Orders&gt; &#123;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><button type="button" class="tab-to-top" aria-label="scroll to top"><i class="fas fa-arrow-up"></i></button></div><div class="tab-item-content" id="ordersservice-2"><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="keyword">public</span> <span class="keyword">interface</span> <span class="title class_">OrderDetailService</span> <span class="keyword">extends</span> <span class="title class_">IService</span>&lt;OrderDetail&gt; &#123;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><button type="button" class="tab-to-top" aria-label="scroll to top"><i class="fas fa-arrow-up"></i></button></div></div></div></li><li>Service实现类<code>OrdersServiceImpl</code>和<code>OrderDetailServiceImpl</code><div class="tabs" id="ordersserviceimpl"><ul class="nav-tabs"><li class="tab active"><button type="button" data-href="#ordersserviceimpl-1">ordersServiceImpl</button></li><li class="tab"><button type="button" data-href="#ordersserviceimpl-2">orderDetailServiceImpl</button></li></ul><div class="tab-contents"><div class="tab-item-content active" id="ordersserviceimpl-1"><figure class="highlight java"><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="meta">@Service</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">OrderServiceImpl</span> <span class="keyword">extends</span> <span class="title class_">ServiceImpl</span>&lt;OrderMapper, Orders&gt; <span class="keyword">implements</span> <span class="title class_">OrderService</span> &#123;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><button type="button" class="tab-to-top" aria-label="scroll to top"><i class="fas fa-arrow-up"></i></button></div><div class="tab-item-content" id="ordersserviceimpl-2"><figure class="highlight java"><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="meta">@Service</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">OrderDetailServiceImpl</span> <span class="keyword">extends</span> <span class="title class_">ServiceImpl</span>&lt;OrderDetailMapper, OrderDetail&gt; <span class="keyword">implements</span> <span class="title class_">OrderDetailService</span> &#123;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><button type="button" class="tab-to-top" aria-label="scroll to top"><i class="fas fa-arrow-up"></i></button></div></div></div></li><li>Controller接口<code>OrdersController</code>和<code>OrderDetailController</code><div class="tabs" id="orderscontroller"><ul class="nav-tabs"><li class="tab active"><button type="button" data-href="#orderscontroller-1">ordersController</button></li><li class="tab"><button type="button" data-href="#orderscontroller-2">orderDetailController</button></li></ul><div class="tab-contents"><div class="tab-item-content active" id="orderscontroller-1"><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><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="meta">@RestController</span></span><br><span class="line"><span class="meta">@Slf4j</span></span><br><span class="line"><span class="meta">@RequestMapping(&quot;/order&quot;)</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">OrderController</span> &#123;</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">&#125;</span><br></pre></td></tr></table></figure><button type="button" class="tab-to-top" aria-label="scroll to top"><i class="fas fa-arrow-up"></i></button></div><div class="tab-item-content" id="orderscontroller-2"><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><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="meta">@RestController</span></span><br><span class="line"><span class="meta">@Slf4j</span></span><br><span class="line"><span class="meta">@RequestMapping(&quot;/orderDetail&quot;)</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">OrderDetailController</span> &#123;</span><br><span class="line">    <span class="meta">@Autowired</span></span><br><span class="line">    <span class="keyword">private</span> OrderDetailService orderDetailService;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><button type="button" class="tab-to-top" aria-label="scroll to top"><i class="fas fa-arrow-up"></i></button></div></div></div></li></ol><h3 id="前端分析-2"><a href="#前端分析-2" class="headerlink" title="前端分析"></a>前端分析</h3><ul><li><p>点击去结算按钮，然后查看发送的请求url和方式</p><blockquote><p>请求网址: <a href="http://localhost/addressBook/default">http://localhost/addressBook/default</a><br>请求方法: GET</p></blockquote></li><li><p>页面跳转到确认订单页面，发送ajax请求，用于获取用户的默认地址，但是请求失败，服务端没有对应的映射</p></li><li><p>根据请求路径<code>/addressBook/default</code>，请求方式<code>GET</code>,进入到<code>AddressBookController</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></pre></td><td class="code"><pre><span class="line"><span class="meta">@GetMapping(&quot;/default&quot;)</span></span><br><span class="line"><span class="keyword">public</span> Result&lt;AddressBook&gt; <span class="title function_">defaultAddress</span><span class="params">()</span> &#123;</span><br><span class="line">    <span class="comment">//获取当前用户id</span></span><br><span class="line">    <span class="type">Long</span> <span class="variable">userId</span> <span class="operator">=</span> BaseContext.getCurrentId();</span><br><span class="line">    <span class="comment">//条件构造器</span></span><br><span class="line">    LambdaQueryWrapper&lt;AddressBook&gt; queryWrapper = <span class="keyword">new</span> <span class="title class_">LambdaQueryWrapper</span>&lt;&gt;();</span><br><span class="line">    <span class="comment">//当前用户</span></span><br><span class="line">    queryWrapper.eq(userId != <span class="literal">null</span>, AddressBook::getUserId, userId);</span><br><span class="line">    <span class="comment">//默认地址</span></span><br><span class="line">    queryWrapper.eq(AddressBook::getIsDefault, <span class="number">1</span>);</span><br><span class="line">    <span class="type">AddressBook</span> <span class="variable">addressBook</span> <span class="operator">=</span> addressBookService.getOne(queryWrapper);</span><br><span class="line">    <span class="keyword">return</span> Result.success(addressBook);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></li><li><p>重启服务器，再次点击按钮就能看到地址了</p><div class="img-wrap"><div class="img-bg"><img class="img" src="https://raw.githubusercontent.com/silvan2077/markdown_pic/main/Picgo/20251107133613.png"/></div></div></li></ul><h3 id="结算"><a href="#结算" class="headerlink" title="结算"></a>结算</h3><ul><li><p>继续点击去支付，查看发送的请求url与请求方式</p><blockquote><p>请求网址: <a href="http://localhost/order/submit">http://localhost/order/submit</a><br>请求方法: POST</p></blockquote></li><li><p>提交给服务端的数据格式为JSON</p></li></ul><div class="img-wrap"><div class="img-bg"><img class="img" src="https://raw.githubusercontent.com/silvan2077/markdown_pic/main/Picgo/20251107133711.png"/></div></div><ul><li>请求路径<code>/order/submit</code>，请求方式<code>POST</code>，现在去<code>OrderController</code>中开发对应的功能<br>具体的<code>submit</code>方法放在<code>OrderService</code>写，<code>OrderController</code>调用写好的<code>submit</code>方法即可</li></ul><div class="tabs" id="orderservice结算方法"><ul class="nav-tabs"><li class="tab active"><button type="button" data-href="#orderservice结算方法-1">OrderService 3</button></li><li class="tab"><button type="button" data-href="#orderservice结算方法-2">OrderServiceImpl</button></li><li class="tab"><button type="button" data-href="#orderservice结算方法-3">OrderController</button></li></ul><div class="tab-contents"><div class="tab-item-content active" id="orderservice结算方法-1"><figure class="highlight java"><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="keyword">public</span> <span class="keyword">interface</span> <span class="title class_">OrderService</span> <span class="keyword">extends</span> <span class="title class_">IService</span>&lt;Orders&gt; &#123;</span><br><span class="line">    <span class="keyword">void</span> <span class="title function_">submit</span><span class="params">(Orders orders)</span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><button type="button" class="tab-to-top" aria-label="scroll to top"><i class="fas fa-arrow-up"></i></button></div><div class="tab-item-content" id="orderservice结算方法-2"><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><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="meta">@Service</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">OrderServiceImpl</span> <span class="keyword">extends</span> <span class="title class_">ServiceImpl</span>&lt;OrderMapper, Orders&gt; <span class="keyword">implements</span> <span class="title class_">OrderService</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_">submit</span><span class="params">(Orders orders)</span> &#123;</span><br><span class="line"></span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><button type="button" class="tab-to-top" aria-label="scroll to top"><i class="fas fa-arrow-up"></i></button></div><div class="tab-item-content" id="orderservice结算方法-3"><p>养成测试接收数据的习惯<br><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><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="meta">@PostMapping(&quot;/submit&quot;)</span></span><br><span class="line"><span class="keyword">public</span> Result&lt;String&gt; <span class="title function_">submit</span><span class="params">(<span class="meta">@RequestBody</span> Orders orders)</span> &#123;</span><br><span class="line">    log.info(<span class="string">&quot;orders:&#123;&#125;&quot;</span>, orders);</span><br><span class="line">    orderService.submit(orders);</span><br><span class="line">    <span class="keyword">return</span> Result.success(<span class="string">&quot;用户下单成功&quot;</span>);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></p><button type="button" class="tab-to-top" aria-label="scroll to top"><i class="fas fa-arrow-up"></i></button></div></div></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></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">interface</span> <span class="title class_">OrderService</span> <span class="keyword">extends</span> <span class="title class_">IService</span>&lt;Orders&gt; &#123;</span><br><span class="line">    <span class="keyword">void</span> <span class="title function_">submit</span><span class="params">(Orders orders)</span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>此时可以测试一下是否能接收到数据</p><blockquote><p>orders:Orders(id=null, number=null, status=null, userId=null, addressBookId=1986397979540123650, orderTime=null, checkoutTime=null, payMethod=1, amount=null, remark=, userName=null, phone=null, address=null, consignee=null)</p></blockquote><ul><li><p>编写具体的submit方法的逻辑代码，需要先分析下单功能需要获取哪些数据</p><ul><li>获取当前用户id</li><li>根据用户id查询其购物车数据</li><li>根据查询到的购物车数据，对订单表插入数据（1条）</li><li><p>根据查询到的购物车数据，对订单明细表插入数据（多条）</p></li><li><p>下单后清空购物车数据</p></li></ul></li></ul><p>具体实现：<br><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><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">@Service</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">OrderServiceImpl</span> <span class="keyword">extends</span> <span class="title class_">ServiceImpl</span>&lt;OrderMapper, Orders&gt; <span class="keyword">implements</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> ShoppingCartService shoppingCartService;</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">@Autowired</span></span><br><span class="line">    <span class="keyword">private</span> AddressBookService addressBookService;</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> OrderDetailService orderDetailService;</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_">submit</span><span class="params">(Orders orders)</span> &#123;</span><br><span class="line">        <span class="comment">//获取当前用户id</span></span><br><span class="line">        <span class="type">Long</span> <span class="variable">userId</span> <span class="operator">=</span> BaseContext.getCurrentId();</span><br><span class="line">        <span class="comment">//条件构造器</span></span><br><span class="line">        LambdaQueryWrapper&lt;ShoppingCart&gt; shoppingCartLambdaQueryWrapper = <span class="keyword">new</span> <span class="title class_">LambdaQueryWrapper</span>&lt;&gt;();</span><br><span class="line">        <span class="comment">//根据当前用户id查询其购物车数据</span></span><br><span class="line">        shoppingCartLambdaQueryWrapper.eq(userId != <span class="literal">null</span>, ShoppingCart::getUserId, userId);</span><br><span class="line">        List&lt;ShoppingCart&gt; shoppingCarts = shoppingCartService.list(shoppingCartLambdaQueryWrapper);</span><br><span class="line">        <span class="comment">//判断一下购物车是否为空</span></span><br><span class="line">        <span class="keyword">if</span> (shoppingCarts == <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_">CustomException</span>(<span class="string">&quot;购物车数据为空，不能下单&quot;</span>);</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="comment">//判断一下地址是否有误</span></span><br><span class="line">        <span class="type">Long</span> <span class="variable">addressBookId</span> <span class="operator">=</span> orders.getAddressBookId();</span><br><span class="line">        <span class="type">AddressBook</span> <span class="variable">addressBook</span> <span class="operator">=</span> addressBookService.getById(addressBookId);</span><br><span class="line">        <span class="keyword">if</span> (addressBookId == <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_">CustomException</span>(<span class="string">&quot;地址信息有误，不能下单&quot;</span>);</span><br><span class="line">        &#125;</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.getById(userId);</span><br><span class="line">        <span class="type">long</span> <span class="variable">orderId</span> <span class="operator">=</span> IdWorker.getId();</span><br><span class="line">        <span class="type">AtomicInteger</span> <span class="variable">amount</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 class="comment">//向订单细节表设置属性</span></span><br><span class="line">        List&lt;OrderDetail&gt; orderDetailList= shoppingCarts.stream().map((item) -&gt; &#123;</span><br><span class="line">            <span class="type">OrderDetail</span> <span class="variable">orderDetail</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">OrderDetail</span>();</span><br><span class="line">            orderDetail.setOrderId(orderId);</span><br><span class="line">            orderDetail.setName(item.getName());</span><br><span class="line">            orderDetail.setImage(item.getImage());</span><br><span class="line">            orderDetail.setDishId(item.getDishId());</span><br><span class="line">            orderDetail.setSetmealId(item.getSetmealId());</span><br><span class="line">            orderDetail.setDishFlavor(item.getDishFlavor());</span><br><span class="line">            orderDetail.setNumber(item.getNumber());</span><br><span class="line">            orderDetail.setAmount(item.getAmount());</span><br><span class="line">            amount.addAndGet(item.getAmount().multiply(<span class="keyword">new</span> <span class="title class_">BigDecimal</span>(item.getNumber())).intValue());</span><br><span class="line"></span><br><span class="line">            <span class="keyword">return</span> orderDetail;</span><br><span class="line">        &#125;).collect(Collectors.toList());</span><br><span class="line"></span><br><span class="line">        <span class="comment">//向订单表设置属性</span></span><br><span class="line">        orders.setId(orderId);</span><br><span class="line">        orders.setNumber(String.valueOf(orderId));</span><br><span class="line">        orders.setStatus(<span class="number">2</span>);</span><br><span class="line">        orders.setUserId(userId);</span><br><span class="line">        orders.setAddressBookId(addressBookId);</span><br><span class="line">        orders.setOrderTime(LocalDateTime.now());</span><br><span class="line">        orders.setCheckoutTime(LocalDateTime.now());</span><br><span class="line">        orders.setAmount(<span class="keyword">new</span> <span class="title class_">BigDecimal</span>(amount.get()));</span><br><span class="line">        orders.setPhone(addressBook.getPhone());</span><br><span class="line">        orders.setUserName(user.getName());</span><br><span class="line">        orders.setConsignee(addressBook.getConsignee());</span><br><span class="line">        orders.setAddress(</span><br><span class="line">                (addressBook.getProvinceName() == <span class="literal">null</span> ? <span class="string">&quot;&quot;</span>:addressBook.getProvinceName())+</span><br><span class="line">                        (addressBook.getCityName() == <span class="literal">null</span> ? <span class="string">&quot;&quot;</span>:addressBook.getCityName())+</span><br><span class="line">                        (addressBook.getDistrictName() == <span class="literal">null</span> ? <span class="string">&quot;&quot;</span>:addressBook.getDistrictName())+</span><br><span class="line">                        (addressBook.getDetail() == <span class="literal">null</span> ? <span class="string">&quot;&quot;</span>:addressBook.getDetail())</span><br><span class="line">        );</span><br><span class="line"></span><br><span class="line">        <span class="comment">//根据查询到的购物车数据，对订单表插入数据（1条）</span></span><br><span class="line">        <span class="built_in">super</span>.save(orders);</span><br><span class="line">        <span class="comment">//根据查询到的购物车数据，对订单明细表插入数据（多条）</span></span><br><span class="line">        orderDetailService.saveBatch(orderDetailList);</span><br><span class="line">        <span class="comment">//清空购物车数据</span></span><br><span class="line">        shoppingCartService.remove(shoppingCartLambdaQueryWrapper);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></p><ul><li><p>代码量很多，但是大部分都是赋值操作，由于购物车数据与订单数据和订单详情的重复字段不是很多，所以这里就没采用<code>BeanUtils.copyProperties()</code>来复制属性，而是自己一个一个set的</p></li><li><p>重启服务器，测试结算按钮，下单后就可以在数据库的<code>orders</code>表中找到相关数据了</p></li></ul><h2 id="移动端补充功能"><a href="#移动端补充功能" class="headerlink" title="移动端补充功能"></a>移动端补充功能</h2><h3 id="历史订单功能"><a href="#历史订单功能" class="headerlink" title="历史订单功能"></a>历史订单功能</h3><ul><li><p>每次访问个人中心/历史订单时，都会发送请求</p><blockquote><p>请求网址: <a href="http://localhost/order/userPage?page=1&amp;pageSize=1">http://localhost/order/userPage?page=1&amp;pageSize=1</a><br>请求方法: GET</p></blockquote></li><li><p>根据请求网址，看样子是个分页请求，我们之前把订单数据存进了order表中，那么该功能，大概率就是从表中查出数据然后返回给前端</p></li><li><p>直接在<code>OrderController</code>中编写对应的方法</p><ul><li><p>在此之前，先创建一个OrderDto，传输固定的属性</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></pre></td><td class="code"><pre><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_">OrdersDto</span> <span class="keyword">extends</span> <span class="title class_">Orders</span> &#123;</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="keyword">private</span> String phone;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">private</span> String address;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">private</span> String consignee;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">private</span> List&lt;OrderDetail&gt; orderDetails;</span><br><span class="line">    </span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></li><li><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></pre></td><td class="code"><pre><span class="line"><span class="meta">@GetMapping(&quot;/userPage&quot;)</span></span><br><span class="line"><span class="keyword">public</span> Result&lt;Page&gt; <span class="title function_">page</span><span class="params">(<span class="type">int</span> page, <span class="type">int</span> pageSize)</span> &#123;</span><br><span class="line">    <span class="comment">//获取当前id</span></span><br><span class="line">    <span class="type">Long</span> <span class="variable">userId</span> <span class="operator">=</span> BaseContext.getCurrentId();</span><br><span class="line">    Page&lt;Orders&gt; pageInfo = <span class="keyword">new</span> <span class="title class_">Page</span>&lt;&gt;(page, pageSize);</span><br><span class="line">    Page&lt;OrdersDto&gt; ordersDtoPage = <span class="keyword">new</span> <span class="title class_">Page</span>&lt;&gt;(page, pageSize);</span><br><span class="line">    <span class="comment">//条件构造器</span></span><br><span class="line">    LambdaQueryWrapper&lt;Orders&gt; queryWrapper = <span class="keyword">new</span> <span class="title class_">LambdaQueryWrapper</span>&lt;&gt;();</span><br><span class="line">    <span class="comment">//查询当前用户id订单数据</span></span><br><span class="line">    queryWrapper.eq(userId != <span class="literal">null</span>, Orders::getUserId, userId);</span><br><span class="line">    <span class="comment">//按时间降序排序</span></span><br><span class="line">    queryWrapper.orderByDesc(Orders::getOrderTime);</span><br><span class="line">    orderService.page(pageInfo, queryWrapper);</span><br><span class="line">    List&lt;OrdersDto&gt; list = pageInfo.getRecords().stream().map((item) -&gt; &#123;</span><br><span class="line">        <span class="type">OrdersDto</span> <span class="variable">ordersDto</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">OrdersDto</span>();</span><br><span class="line">        <span class="comment">//获取orderId,然后根据这个id，去orderDetail表中查数据</span></span><br><span class="line">        <span class="type">Long</span> <span class="variable">orderId</span> <span class="operator">=</span> item.getId();</span><br><span class="line">        LambdaQueryWrapper&lt;OrderDetail&gt; wrapper = <span class="keyword">new</span> <span class="title class_">LambdaQueryWrapper</span>&lt;&gt;();</span><br><span class="line">        wrapper.eq(OrderDetail::getOrderId, orderId);</span><br><span class="line">        List&lt;OrderDetail&gt; details = orderDetailService.list(wrapper);</span><br><span class="line">        BeanUtils.copyProperties(item, ordersDto);</span><br><span class="line">        <span class="comment">//之后set一下属性</span></span><br><span class="line">        ordersDto.setOrderDetails(details);</span><br><span class="line">        <span class="keyword">return</span> ordersDto;</span><br><span class="line">    &#125;).collect(Collectors.toList());</span><br><span class="line">    BeanUtils.copyProperties(pageInfo, ordersDtoPage, <span class="string">&quot;records&quot;</span>);</span><br><span class="line">    ordersDtoPage.setRecords(list);</span><br><span class="line">    <span class="comment">//日志输出看一下</span></span><br><span class="line">    log.info(<span class="string">&quot;list:&#123;&#125;&quot;</span>, list);</span><br><span class="line">    <span class="keyword">return</span> Result.success(ordersDtoPage);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></li></ul></li></ul><h3 id="登出功能-1"><a href="#登出功能-1" class="headerlink" title="登出功能"></a>登出功能</h3><ul><li><p>这个应该算简单的了吧，清除该用户的session记录即可，点击退出登录，请求如下</p><blockquote><p>请求网址: <a href="http://localhost/user/loginout">http://localhost/user/loginout</a><br>请求方法: POST</p></blockquote></li><li><p>请求路径<code>/user/loginout</code>，请求方式<code>POST</code><br>所以我们应该去UserController中编写对应的方法</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></pre></td><td class="code"><pre><span class="line"><span class="meta">@PostMapping(&quot;/loginout&quot;)</span></span><br><span class="line"><span class="keyword">public</span> Result&lt;String&gt; <span class="title function_">logout</span><span class="params">(HttpServletRequest request)</span> &#123;</span><br><span class="line">    request.getSession().removeAttribute(<span class="string">&quot;user&quot;</span>);</span><br><span class="line">    <span class="keyword">return</span> Result.success(<span class="string">&quot;退出成功&quot;</span>);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></li></ul><div class="note warning no-icon flat"><p>前提login方法的是写的<code>setAttribute(&quot;user&quot;, user.getId());</code>，字段名要对上</p></div><h3 id="修改-删除地址"><a href="#修改-删除地址" class="headerlink" title="修改/删除地址"></a>修改/删除地址</h3><ul><li><p><code>数据回显</code></p><ul><li>点击地址选项卡的<code>铅笔图案</code>，会发送修改请求并跳转到修改地址页面<blockquote><p>请求网址: <a href="http://localhost:8080/addressBook/1986395330853879810">http://localhost:8080/addressBook/1986395330853879810</a><br>请求方法: GET</p></blockquote></li><li><p>可以看到请求地址非常像Restful风格，因此推测请求路径大概率为<code>/addressBook/&#123;id&#125;</code></p></li><li><p>在<code>AddressBookController</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="meta">@GetMapping(&quot;/&#123;id&#125;&quot;)</span></span><br><span class="line"><span class="keyword">public</span> Result&lt;AddressBook&gt; <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="type">AddressBook</span> <span class="variable">addressBook</span> <span class="operator">=</span> addressBookService.getById(id);</span><br><span class="line">    <span class="keyword">if</span> (addressBook == <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_">CustomException</span>(<span class="string">&quot;地址信息不存在&quot;</span>);</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">return</span> Result.success(addressBook);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></li></ul></li></ul><ul><li>此时再点击修改按钮，就可以看到该地址的数据回显到修改页面了<div class="img-wrap"><div class="img-bg"><img class="img" src="https://raw.githubusercontent.com/silvan2077/markdown_pic/main/Picgo/20251107141529.png"/></div></div></li></ul><ul><li><p>修改地址</p><ul><li><p>点击上图中的<code>保存地址</code>按钮，查看发送的请求</p><blockquote><p>请求网址: <a href="http://localhost/addressBook">http://localhost/addressBook</a><br>请求方法: PUT</p></blockquote></li><li><p>请求方式<code>PUT</code>，直接在<code>AddressBookController</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="meta">@PutMapping</span></span><br><span class="line"><span class="keyword">public</span> Result&lt;String&gt; <span class="title function_">updateAdd</span><span class="params">(<span class="meta">@RequestBody</span> AddressBook addressBook)</span> &#123;</span><br><span class="line">    <span class="keyword">if</span> (addressBook == <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_">CustomException</span>(<span class="string">&quot;地址信息不存在，请刷新重试&quot;</span>);</span><br><span class="line">    &#125;</span><br><span class="line">    addressBookService.updateById(addressBook);</span><br><span class="line">    <span class="keyword">return</span> Result.success(<span class="string">&quot;地址修改成功&quot;</span>);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></li></ul></li><li><p>删除地址</p><ul><li><p>点击上图中的<code>删除地址</code>按钮，查看发送的请求</p><blockquote><p>请求网址: <a href="http://localhost/addressBook?ids=1579828298672885762">http://localhost/addressBook?ids=1579828298672885762</a><br>请求方法: DELETE</p></blockquote></li><li><p>在<code>AddressBookController</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></pre></td><td class="code"><pre><span class="line"><span class="meta">@DeleteMapping()</span></span><br><span class="line"><span class="keyword">public</span> Result&lt;String&gt; <span class="title function_">deleteAdd</span><span class="params">(<span class="meta">@RequestParam(&quot;ids&quot;)</span> Long id)</span> &#123;</span><br><span class="line">    <span class="keyword">if</span> (id == <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_">CustomException</span>(<span class="string">&quot;地址信息不存在，请刷新重试&quot;</span>);</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="type">AddressBook</span> <span class="variable">addressBook</span> <span class="operator">=</span> addressBookService.getById(id);</span><br><span class="line">    <span class="keyword">if</span> (addressBook == <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_">CustomException</span>(<span class="string">&quot;地址信息不存在，请刷新重试&quot;</span>);</span><br><span class="line">    &#125;</span><br><span class="line">    addressBookService.removeById(id);</span><br><span class="line">    <span class="keyword">return</span> Result.success(<span class="string">&quot;地址删除成功&quot;</span>);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></li></ul></li></ul><div class="note info no-icon flat"><p>此时对于地址的操作基本已经完成，可以自己去测试一下，看看效果</p></div><h3 id="再来一单"><a href="#再来一单" class="headerlink" title="再来一单"></a>再来一单</h3><ul><li>这个功能其实比较隐晦，因为当订单状态为<code>已完成</code>时才会出现这个按钮（修改orders表中的status字段为4）<div class="img-wrap"><div class="img-bg"><img class="img" src="https://raw.githubusercontent.com/silvan2077/markdown_pic/main/Picgo/20251107142326.png"/></div></div></li></ul><p>前端代码<br><div class="tabs" id="再来一单按钮"><ul class="nav-tabs"></ul><div class="tab-contents"></div></div></p><ul><li><p>点击<code>再来一单</code>，查看发送的请求</p><blockquote><p>请求网址: <a href="http://localhost/order/again">http://localhost/order/again</a><br>请求方法: POST</p></blockquote></li><li><p>数据只携带了一个json格式的id数据，根据常识，这个id只能是orders表中的订单id，即<code>order_id</code></p><figure class="highlight json"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="punctuation">&#123;</span>id<span class="punctuation">:</span> <span class="string">&quot;1986672088484409346&quot;</span><span class="punctuation">&#125;</span></span><br></pre></td></tr></table></figure></li><li><p>现在传回的数据只有一个order_id，因此要根据它去查询对应的下单信息</p></li><li><p>分析<code>再来一单</code>具体实现思路（参考一下当初我们怎么添加购物车的）</p><ul><li>参考点外卖的经验，再来一单应当是将该订单的数据添加到购物车，并跳转到之前的下单页面</li><li>之前是我们手动选择数据（菜品/套餐）添加到购物车，现在相当于手里有发票，根据发票上的数据，再买一遍</li></ul></li><li><p>来<code>OrderController</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></pre></td><td class="code"><pre><span class="line"><span class="meta">@PostMapping(&quot;/again&quot;)</span></span><br><span class="line"><span class="keyword">public</span> Result&lt;String&gt; <span class="title function_">again</span><span class="params">(<span class="meta">@RequestBody</span> Map&lt;String,String&gt; map)</span>&#123;</span><br><span class="line">    <span class="comment">//获取order_id</span></span><br><span class="line">    <span class="type">Long</span> <span class="variable">orderId</span> <span class="operator">=</span> Long.valueOf(map.get(<span class="string">&quot;id&quot;</span>));</span><br><span class="line">    <span class="comment">//条件构造器</span></span><br><span class="line">    LambdaQueryWrapper&lt;OrderDetail&gt; queryWrapper = <span class="keyword">new</span> <span class="title class_">LambdaQueryWrapper</span>&lt;&gt;();</span><br><span class="line">    <span class="comment">//查询订单的口味细节数据</span></span><br><span class="line">    queryWrapper.eq(OrderDetail::getOrderId,orderId);</span><br><span class="line">    List&lt;OrderDetail&gt; details = orderDetailService.list(queryWrapper);</span><br><span class="line">    <span class="comment">//获取用户id，待会需要set操作</span></span><br><span class="line">    <span class="type">Long</span> <span class="variable">userId</span> <span class="operator">=</span> BaseContext.getCurrentId();</span><br><span class="line">    List&lt;ShoppingCart&gt; shoppingCarts = details.stream().map((item) -&gt;&#123;</span><br><span class="line">        <span class="type">ShoppingCart</span> <span class="variable">shoppingCart</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">ShoppingCart</span>();</span><br><span class="line">        <span class="comment">//Copy对应属性值</span></span><br><span class="line">        BeanUtils.copyProperties(item,shoppingCart);</span><br><span class="line">        <span class="comment">//设置一下userId</span></span><br><span class="line">        shoppingCart.setUserId(userId);</span><br><span class="line">        <span class="comment">//设置一下创建时间为当前时间</span></span><br><span class="line">        shoppingCart.setCreateTime(LocalDateTime.now());</span><br><span class="line">        <span class="keyword">return</span> shoppingCart;</span><br><span class="line">    &#125;).collect(Collectors.toList());</span><br><span class="line">    <span class="comment">//加入购物车</span></span><br><span class="line">    shoppingCartService.saveBatch(shoppingCarts);</span><br><span class="line">    <span class="keyword">return</span> Result.success(<span class="string">&quot;喜欢吃就再来一单吖~&quot;</span>);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><div class="note info no-icon flat"><p>这里直接用<code>BeanUtils.copyProperties</code>进行复制，地址是选择当前默认地址（如果改了默认地址，那么不是之前的地址，很合理）</p></div></li></ul><h3 id="减号按钮"><a href="#减号按钮" class="headerlink" title="减号按钮"></a>减号按钮</h3><ul><li>之前下单的时候，只有加号按钮能用，减号按钮还没配置，我们点击<code>减号</code>，看看发送了什么请求<blockquote><p>请求网址: <a href="http://localhost/shoppingCart/sub">http://localhost/shoppingCart/sub</a><br>请求方法: POST</p></blockquote></li></ul><ul><li><p>返回的json数据如下，只有<code>dishId</code>和<code>setmealId</code></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></pre></td><td class="code"><pre><span class="line"><span class="punctuation">&#123;</span>   </span><br><span class="line">  dishId<span class="punctuation">:</span> <span class="literal"><span class="keyword">null</span></span><span class="punctuation">,</span> </span><br><span class="line">  setmealId<span class="punctuation">:</span> <span class="string">&quot;1986672088484409346&quot;</span></span><br><span class="line"><span class="punctuation">&#125;</span></span><br></pre></td></tr></table></figure></li><li><p>思路分析: 根据返回的id，来对不同的菜品/套餐的number属性修改（对应的数量-1），如果number等于0，则删除</p></li><li><p>在<code>ShoppingCartController</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></pre></td><td class="code"><pre><span class="line"><span class="meta">@PostMapping(&quot;/sub&quot;)</span></span><br><span class="line"><span class="keyword">public</span> Result&lt;ShoppingCart&gt; <span class="title function_">sub</span><span class="params">(<span class="meta">@RequestBody</span> ShoppingCart shoppingCart)</span> &#123;</span><br><span class="line">    <span class="type">Long</span> <span class="variable">dishId</span> <span class="operator">=</span> shoppingCart.getDishId();</span><br><span class="line">    <span class="type">Long</span> <span class="variable">setmealId</span> <span class="operator">=</span> shoppingCart.getSetmealId();</span><br><span class="line">    <span class="comment">//条件构造器</span></span><br><span class="line">    LambdaQueryWrapper&lt;ShoppingCart&gt; queryWrapper = <span class="keyword">new</span> <span class="title class_">LambdaQueryWrapper</span>&lt;&gt;();</span><br><span class="line">    <span class="comment">//只查询当前用户ID的购物车</span></span><br><span class="line">    queryWrapper.eq(ShoppingCart::getUserId, BaseContext.getCurrentId());</span><br><span class="line">    <span class="comment">//代表数量减少的是菜品数量</span></span><br><span class="line">    <span class="keyword">if</span> (dishId != <span class="literal">null</span>) &#123;</span><br><span class="line">        <span class="comment">//通过dishId查出购物车菜品数据</span></span><br><span class="line">        queryWrapper.eq(ShoppingCart::getDishId, dishId);</span><br><span class="line">        <span class="type">ShoppingCart</span> <span class="variable">dishCart</span> <span class="operator">=</span> shoppingCartService.getOne(queryWrapper);</span><br><span class="line">        <span class="comment">//将查出来的数据的数量-1</span></span><br><span class="line">        dishCart.setNumber(dishCart.getNumber() - <span class="number">1</span>);</span><br><span class="line">        <span class="type">Integer</span> <span class="variable">currentNum</span> <span class="operator">=</span> dishCart.getNumber();</span><br><span class="line">        <span class="comment">//然后判断</span></span><br><span class="line">        <span class="keyword">if</span> (currentNum &gt; <span class="number">0</span>) &#123;</span><br><span class="line">            <span class="comment">//大于0则更新</span></span><br><span class="line">            shoppingCartService.updateById(dishCart);</span><br><span class="line">        &#125; <span class="keyword">else</span> <span class="keyword">if</span> (currentNum == <span class="number">0</span>) &#123;</span><br><span class="line">            <span class="comment">//小于0则删除</span></span><br><span class="line">            shoppingCartService.removeById(dishCart.getId());</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">return</span> Result.success(dishCart);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">if</span> (setmealId != <span class="literal">null</span>) &#123;</span><br><span class="line">        <span class="comment">//通过setmealId查询购物车套餐数据</span></span><br><span class="line">        queryWrapper.eq(ShoppingCart::getSetmealId, setmealId);</span><br><span class="line">        <span class="type">ShoppingCart</span> <span class="variable">setmealCart</span> <span class="operator">=</span> shoppingCartService.getOne(queryWrapper);</span><br><span class="line">        <span class="comment">//将查出来的数据的数量-1</span></span><br><span class="line">        setmealCart.setNumber(setmealCart.getNumber() - <span class="number">1</span>);</span><br><span class="line">        <span class="type">Integer</span> <span class="variable">currentNum</span> <span class="operator">=</span> setmealCart.getNumber();</span><br><span class="line">        <span class="comment">//然后判断</span></span><br><span class="line">        <span class="keyword">if</span> (currentNum &gt; <span class="number">0</span>) &#123;</span><br><span class="line">            <span class="comment">//大于0则更新</span></span><br><span class="line">            shoppingCartService.updateById(setmealCart);</span><br><span class="line">        &#125; <span class="keyword">else</span> <span class="keyword">if</span> (currentNum == <span class="number">0</span>) &#123;</span><br><span class="line">            <span class="comment">//等于0则删除</span></span><br><span class="line">            shoppingCartService.removeById(setmealCart.getId());</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">return</span> Result.success(setmealCart);</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">return</span> Result.error(<span class="string">&quot;系统繁忙，请稍后再试&quot;</span>);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></li></ul><h2 id="后台系统补充功能"><a href="#后台系统补充功能" class="headerlink" title="后台系统补充功能"></a>后台系统补充功能</h2><h3 id="菜品启售-停售"><a href="#菜品启售-停售" class="headerlink" title="菜品启售/停售"></a>菜品启售/停售</h3><ul><li><p>点击停售按钮，查看发送的请求</p><blockquote><p>请求网址: <a href="http://localhost:8080/dish/status/0?ids=1985250880349995010">http://localhost:8080/dish/status/0?ids=1985250880349995010</a><br>请求方法: POST</p></blockquote></li><li><p>当前商品为启售状态，其status为1，但点击停售按钮时，发送的status为0，前端是直接对这个status取反了，我们直接用发送的这个status来更新我们的商品状态就好了，不用在后端再次进行判断</p></li><li><p>在<code>DishController</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></pre></td><td class="code"><pre><span class="line"><span class="meta">@PostMapping(&quot;/status/&#123;status&#125;&quot;)</span></span><br><span class="line"><span class="keyword">public</span> Result&lt;String&gt; <span class="title function_">status</span><span class="params">(<span class="meta">@PathVariable</span> Integer status, Long ids)</span> &#123;</span><br><span class="line">    log.info(<span class="string">&quot;status:&#123;&#125;,ids:&#123;&#125;&quot;</span>, status, ids);</span><br><span class="line">    <span class="type">Dish</span> <span class="variable">dish</span> <span class="operator">=</span> dishService.getById(ids);</span><br><span class="line">    <span class="keyword">if</span> (dish != <span class="literal">null</span>) &#123;</span><br><span class="line">        <span class="comment">//直接用它传进来的这个status改就行</span></span><br><span class="line">        dish.setStatus(status);</span><br><span class="line">        dishService.updateById(dish);</span><br><span class="line">        <span class="keyword">return</span> Result.success(<span class="string">&quot;售卖状态修改成功&quot;</span>);</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">return</span> Result.error(<span class="string">&quot;系统繁忙，请稍后再试&quot;</span>);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></li></ul><h3 id="菜品批量启售-停售"><a href="#菜品批量启售-停售" class="headerlink" title="菜品批量启售/停售"></a>菜品批量启售/停售</h3><ul><li><p>这个其实就是传进来了一个ids的数组，我们在上面的方法上稍作修改就好了，直接用<code>LambdaUpdateWrapper</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></pre></td><td class="code"><pre><span class="line"><span class="meta">@PostMapping(&quot;/status/&#123;status&#125;&quot;)</span></span><br><span class="line"><span class="keyword">public</span> Result&lt;String&gt; <span class="title function_">status</span><span class="params">(<span class="meta">@PathVariable</span> Integer status, <span class="meta">@RequestParam</span> List&lt;Long&gt; ids)</span> &#123;</span><br><span class="line">    log.info(<span class="string">&quot;status:&#123;&#125;,ids:&#123;&#125;&quot;</span>, status, ids);</span><br><span class="line">    LambdaUpdateWrapper&lt;Dish&gt; updateWrapper = <span class="keyword">new</span> <span class="title class_">LambdaUpdateWrapper</span>&lt;&gt;();</span><br><span class="line">    updateWrapper.in(ids != <span class="literal">null</span>, Dish::getId, ids);</span><br><span class="line">    updateWrapper.set(Dish::getStatus, status);</span><br><span class="line">    dishService.update(updateWrapper);</span><br><span class="line">    <span class="keyword">return</span> Result.success(<span class="string">&quot;批量操作成功&quot;</span>);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></li></ul><h3 id="菜品批量删除"><a href="#菜品批量删除" class="headerlink" title="菜品批量删除"></a>菜品批量删除</h3><ul><li><p>删除跟批量删除应该也是同一个操作，点击删除按钮，查看请求</p><blockquote><p>请求网址: <a href="http://localhost:8080/dish?ids=1985250880349995010,1413384757047271425">http://localhost:8080/dish?ids=1985250880349995010,1413384757047271425</a><br>请求方法: DELETE</p></blockquote></li><li><p>按理说，这里应该是逻辑删除，表中有一个字段为<code>is_delete</code>，但是要按逻辑删除的话，还得改前面的<code>list</code>和<code>page</code>代码，因为查询的时候，没涉及到逻辑删除，模型类中也没有isDelete属性</p></li><li><p>所以这里为了省事选择直接删除，但如果是逻辑删除，执行的是update，将逻辑删除字段设为1表示逻辑删除，查询的时候只查询逻辑删除字段为0的数据，表示未删除的数据</p></li><li><p>需要注意的是，如果选中的删除列表中，存在启售状态商品，则不允许删除</p></li><li><p>在<code>DishController</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></pre></td><td class="code"><pre><span class="line"><span class="meta">@DeleteMapping</span></span><br><span class="line"><span class="keyword">public</span> Result&lt;String&gt; <span class="title function_">delete</span><span class="params">(<span class="meta">@RequestParam</span> List&lt;Long&gt; ids)</span> &#123;</span><br><span class="line">    log.info(<span class="string">&quot;删除的ids：&#123;&#125;&quot;</span>, ids);</span><br><span class="line">    LambdaQueryWrapper&lt;Dish&gt; queryWrapper = <span class="keyword">new</span> <span class="title class_">LambdaQueryWrapper</span>&lt;&gt;();</span><br><span class="line">    queryWrapper.in(Dish::getId, ids);</span><br><span class="line">    queryWrapper.eq(Dish::getStatus, <span class="number">1</span>);</span><br><span class="line">    <span class="type">long</span> <span class="variable">count</span> <span class="operator">=</span> dishService.count(queryWrapper);</span><br><span class="line">    <span class="keyword">if</span> (count &gt; <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_">CustomException</span>(<span class="string">&quot;删除列表中存在启售状态商品，无法删除&quot;</span>);</span><br><span class="line">    &#125;</span><br><span class="line">    dishService.removeByIds(ids);</span><br><span class="line">    <span class="keyword">return</span> Result.success(<span class="string">&quot;删除成功&quot;</span>);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></li></ul><div class="note info no-icon flat"><p>至此关于菜品的操作功能，就相对完善了</p></div><h3 id="套餐批量启售-停售"><a href="#套餐批量启售-停售" class="headerlink" title="套餐批量启售/停售"></a>套餐批量启售/停售</h3><ul><li><p>点击批量停售按钮，查看发送的请求</p><blockquote><p>请求网址: <a href="http://localhost:8080/setmeal/status/0?ids=1986422148185178113,1415580119015145474">http://localhost:8080/setmeal/status/0?ids=1986422148185178113,1415580119015145474</a><br>请求方法: POST</p></blockquote></li><li><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></pre></td><td class="code"><pre><span class="line"><span class="meta">@PostMapping(&quot;/status/&#123;status&#125;&quot;)</span></span><br><span class="line"><span class="keyword">public</span> Result&lt;String&gt; <span class="title function_">status</span><span class="params">(<span class="meta">@PathVariable</span> String status, <span class="meta">@RequestParam</span> List&lt;Long&gt; ids)</span> &#123;</span><br><span class="line">    LambdaUpdateWrapper&lt;Setmeal&gt; updateWrapper = <span class="keyword">new</span> <span class="title class_">LambdaUpdateWrapper</span>&lt;&gt;();</span><br><span class="line">    updateWrapper.in(Setmeal::getId, ids);</span><br><span class="line">    updateWrapper.set(Setmeal::getStatus, status);</span><br><span class="line">    setmealService.update(updateWrapper);</span><br><span class="line">    <span class="keyword">return</span> Result.success(<span class="string">&quot;批量操作成功&quot;</span>);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></li></ul><h3 id="套餐修改"><a href="#套餐修改" class="headerlink" title="套餐修改"></a>套餐修改</h3><ul><li><p><code>数据回显</code></p><ul><li><p>点击修改按钮，查看发送的请求</p><blockquote><p>请求网址: <a href="http://localhost:8080/setmeal/1986422148185178113">http://localhost:8080/setmeal/1986422148185178113</a><br>请求方法: GET</p></blockquote></li><li><p>这个请求大概率是用于处理数据回显的，请求路径<code>/setmeal/&#123;setmealId&#125;</code>，请求方式<code>GET</code></p></li><li><p>普通的<code>Setmeal</code>实体类肯定是不够用的，因此要使用<code>SetmealDto</code></p></li><li><p>在<code>SetmealController</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></pre></td><td class="code"><pre><span class="line"><span class="meta">@GetMapping(&quot;/&#123;id&#125;&quot;)</span></span><br><span class="line"><span class="keyword">public</span> Result&lt;SetmealDto&gt; <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="type">Setmeal</span> <span class="variable">setmeal</span> <span class="operator">=</span> setmealService.getById(id);</span><br><span class="line">    <span class="type">SetmealDto</span> <span class="variable">setmealDto</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">SetmealDto</span>();</span><br><span class="line">    <span class="comment">//拷贝数据</span></span><br><span class="line">    BeanUtils.copyProperties(setmeal, setmealDto);</span><br><span class="line">    <span class="comment">//条件构造器</span></span><br><span class="line">    LambdaQueryWrapper&lt;SetmealDish&gt; queryWrapper = <span class="keyword">new</span> <span class="title class_">LambdaQueryWrapper</span>&lt;&gt;();</span><br><span class="line">    <span class="comment">//根据setmealId查询具体的setmealDish</span></span><br><span class="line">    queryWrapper.eq(SetmealDish::getSetmealId, id);</span><br><span class="line">    List&lt;SetmealDish&gt; setmealDishes = setmealDishService.list(queryWrapper);</span><br><span class="line">    <span class="comment">//然后再设置属性</span></span><br><span class="line">    setmealDto.setSetmealDishes(setmealDishes);</span><br><span class="line">    <span class="comment">//作为结果返回</span></span><br><span class="line">    <span class="keyword">return</span> Result.success(setmealDto);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></li></ul></li><li><p><code>套餐修改</code></p><ul><li><p>点击保存按钮，查看发送的请求</p><blockquote><p>请求网址: <a href="http://localhost/setmeal">http://localhost/setmeal</a><br>请求方法: PUT</p></blockquote></li><li><p>携带的数据如下</p><div class="img-wrap"><div class="img-bg"><img class="img" src="https://raw.githubusercontent.com/silvan2077/markdown_pic/main/Picgo/20251107150452.png"/></div></div></li><li><p>请求路径<code>/setmeal</code>，请求方式<code>PUT</code></p></li><li><p>在<code>SetmealController</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></pre></td><td class="code"><pre><span class="line"><span class="meta">@PutMapping</span></span><br><span class="line">    <span class="keyword">public</span> Result&lt;Setmeal&gt; <span class="title function_">updateWithDish</span><span class="params">(<span class="meta">@RequestBody</span> SetmealDto setmealDto)</span> &#123;</span><br><span class="line">    List&lt;SetmealDish&gt; setmealDishes = setmealDto.getSetmealDishes();</span><br><span class="line">    <span class="type">Long</span> <span class="variable">setmealId</span> <span class="operator">=</span> setmealDto.getId();</span><br><span class="line">    <span class="comment">//先根据id把setmealDish表中对应套餐的数据删了</span></span><br><span class="line">    LambdaQueryWrapper&lt;SetmealDish&gt; queryWrapper = <span class="keyword">new</span> <span class="title class_">LambdaQueryWrapper</span>&lt;&gt;();</span><br><span class="line">    queryWrapper.eq(SetmealDish::getSetmealId,setmealId);</span><br><span class="line">    setmealDishService.remove(queryWrapper);</span><br><span class="line">    <span class="comment">//然后在重新添加</span></span><br><span class="line">    setmealDishes = setmealDishes.stream().map((item) -&gt;&#123;</span><br><span class="line">        <span class="comment">//这属性没有，需要我们手动设置一下</span></span><br><span class="line">        item.setSetmealId(setmealId);</span><br><span class="line">        <span class="keyword">return</span> item;</span><br><span class="line">    &#125;).collect(Collectors.toList());</span><br><span class="line">    <span class="comment">//更新套餐数据</span></span><br><span class="line">    setmealService.updateById(setmealDto);</span><br><span class="line">    <span class="comment">//更新套餐对应菜品数据</span></span><br><span class="line">    setmealDishService.saveBatch(setmealDishes);</span><br><span class="line">    <span class="keyword">return</span> Result.success(setmealDto);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></li></ul></li></ul><h3 id="订单明细"><a href="#订单明细" class="headerlink" title="订单明细"></a>订单明细</h3><ul><li><p>点击订单明细，输入查询条件，查看发送的请求</p><blockquote><p>请求网址: <a href="http://localhost:8080/order/page?page=1&amp;pageSize=10&amp;number=111&amp;beginTime=2025-11-01%2000%3A00%3A00&amp;endTime=2025-12-01%2023%3A59%3A59">http://localhost:8080/order/page?page=1&amp;pageSize=10&amp;number=111&amp;beginTime=2025-11-01%2000%3A00%3A00&amp;endTime=2025-12-01%2023%3A59%3A59</a><br>请求方法: GET</p></blockquote></li><li><p>在前面移动端的历史订单功能，其实跟这个差不多，稍作修改即可</p></li></ul><div class="tabs" id="订单明细"><ul class="nav-tabs"><li class="tab active"><button type="button" data-href="#订单明细-1">历史订单</button></li><li class="tab"><button type="button" data-href="#订单明细-2">订单明细</button></li></ul><div class="tab-contents"><div class="tab-item-content active" id="订单明细-1"><p>主要是删除了按当前userId查询，新增了按订单号和事件段查询<br><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><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="meta">@GetMapping(&quot;/userPage&quot;)</span></span><br><span class="line"><span class="keyword">public</span> Result&lt;Page&gt; <span class="title function_">userPage</span><span class="params">(<span class="type">int</span> page, <span class="type">int</span> pageSize)</span> &#123;</span><br><span class="line">    <span class="comment">//获取当前id</span></span><br><span class="line">    <span class="type">Long</span> <span class="variable">userId</span> <span class="operator">=</span> BaseContext.getCurrentId();</span><br><span class="line">    Page&lt;Orders&gt; pageInfo = <span class="keyword">new</span> <span class="title class_">Page</span>&lt;&gt;(page, pageSize);</span><br><span class="line">    Page&lt;OrdersDto&gt; ordersDtoPage = <span class="keyword">new</span> <span class="title class_">Page</span>&lt;&gt;(page, pageSize);</span><br><span class="line">    <span class="comment">//条件构造器</span></span><br><span class="line">    LambdaQueryWrapper&lt;Orders&gt; queryWrapper = <span class="keyword">new</span> <span class="title class_">LambdaQueryWrapper</span>&lt;&gt;();</span><br><span class="line">    <span class="comment">//查询当前用户id订单数据</span></span><br><span class="line">    queryWrapper.eq(userId != <span class="literal">null</span>, Orders::getUserId, userId);</span><br><span class="line">    <span class="comment">//按时间降序排序</span></span><br><span class="line">    queryWrapper.orderByDesc(Orders::getOrderTime);</span><br><span class="line">    orderService.page(pageInfo, queryWrapper);</span><br><span class="line">    List&lt;OrdersDto&gt; list = pageInfo.getRecords().stream().map((item) -&gt; &#123;</span><br><span class="line">        <span class="type">OrdersDto</span> <span class="variable">ordersDto</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">OrdersDto</span>();</span><br><span class="line">        <span class="comment">//获取orderId,然后根据这个id，去orderDetail表中查数据</span></span><br><span class="line">        <span class="type">Long</span> <span class="variable">orderId</span> <span class="operator">=</span> item.getId();</span><br><span class="line">        LambdaQueryWrapper&lt;OrderDetail&gt; wrapper = <span class="keyword">new</span> <span class="title class_">LambdaQueryWrapper</span>&lt;&gt;();</span><br><span class="line">        wrapper.eq(OrderDetail::getOrderId, orderId);</span><br><span class="line">        List&lt;OrderDetail&gt; details = orderDetailService.list(wrapper);</span><br><span class="line">        BeanUtils.copyProperties(item, ordersDto);</span><br><span class="line">        <span class="comment">//之后set一下属性</span></span><br><span class="line">        ordersDto.setOrderDetails(details);</span><br><span class="line">        <span class="keyword">return</span> ordersDto;</span><br><span class="line">    &#125;).collect(Collectors.toList());</span><br><span class="line">    BeanUtils.copyProperties(pageInfo, ordersDtoPage, <span class="string">&quot;records&quot;</span>);</span><br><span class="line">    ordersDtoPage.setRecords(list);</span><br><span class="line">    <span class="comment">//日志输出看一下</span></span><br><span class="line">    log.info(<span class="string">&quot;list:&#123;&#125;&quot;</span>, list);</span><br><span class="line">    <span class="keyword">return</span> Result.success(ordersDtoPage);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></p><button type="button" class="tab-to-top" aria-label="scroll to top"><i class="fas fa-arrow-up"></i></button></div><div class="tab-item-content" id="订单明细-2"><p>历史订单是只查询指定用户的数据，那我们后台这里，查询所有的用户数据就行，也就不用指定userId<br>但是需要判断输入的订单号和时间段，这个要写动态SQL，不过我们可以用MP来帮我们完成<br><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><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="meta">@GetMapping(&quot;/page&quot;)</span></span><br><span class="line"><span class="keyword">public</span> Result&lt;Page&gt; <span class="title function_">page</span><span class="params">(<span class="type">int</span> page, <span class="type">int</span> pageSize, Long number, String beginTime, String endTime)</span> &#123;</span><br><span class="line">    <span class="comment">//获取当前id</span></span><br><span class="line">    Page&lt;Orders&gt; pageInfo = <span class="keyword">new</span> <span class="title class_">Page</span>&lt;&gt;(page, pageSize);</span><br><span class="line">    Page&lt;OrdersDto&gt; ordersDtoPage = <span class="keyword">new</span> <span class="title class_">Page</span>&lt;&gt;(page, pageSize);</span><br><span class="line">    <span class="comment">//条件构造器</span></span><br><span class="line">    LambdaQueryWrapper&lt;Orders&gt; queryWrapper = <span class="keyword">new</span> <span class="title class_">LambdaQueryWrapper</span>&lt;&gt;();</span><br><span class="line">    <span class="comment">//按时间降序排序</span></span><br><span class="line">    queryWrapper.orderByDesc(Orders::getOrderTime);</span><br><span class="line">    <span class="comment">//订单号</span></span><br><span class="line">    queryWrapper.eq(number != <span class="literal">null</span>, Orders::getId, number);</span><br><span class="line">    <span class="comment">//时间段，大于开始，小于结束</span></span><br><span class="line">    queryWrapper.gt(!StringUtils.isEmpty(beginTime), Orders::getOrderTime, beginTime)</span><br><span class="line">            .lt(!StringUtils.isEmpty(endTime), Orders::getOrderTime, endTime);</span><br><span class="line">    orderService.page(pageInfo, queryWrapper);</span><br><span class="line">    List&lt;OrdersDto&gt; list = pageInfo.getRecords().stream().map((item) -&gt; &#123;</span><br><span class="line">        <span class="type">OrdersDto</span> <span class="variable">ordersDto</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">OrdersDto</span>();</span><br><span class="line">        <span class="comment">//获取orderId,然后根据这个id，去orderDetail表中查数据</span></span><br><span class="line">        <span class="type">Long</span> <span class="variable">orderId</span> <span class="operator">=</span> item.getId();</span><br><span class="line">        LambdaQueryWrapper&lt;OrderDetail&gt; wrapper = <span class="keyword">new</span> <span class="title class_">LambdaQueryWrapper</span>&lt;&gt;();</span><br><span class="line">        wrapper.eq(OrderDetail::getOrderId, orderId);</span><br><span class="line">        List&lt;OrderDetail&gt; details = orderDetailService.list(wrapper);</span><br><span class="line">        BeanUtils.copyProperties(item, ordersDto);</span><br><span class="line">        <span class="comment">//之后set一下属性</span></span><br><span class="line">        ordersDto.setOrderDetails(details);</span><br><span class="line">        <span class="keyword">return</span> ordersDto;</span><br><span class="line">    &#125;).collect(Collectors.toList());</span><br><span class="line">    BeanUtils.copyProperties(pageInfo, ordersDtoPage, <span class="string">&quot;records&quot;</span>);</span><br><span class="line">    ordersDtoPage.setRecords(list);</span><br><span class="line">    <span class="comment">//日志输出看一下</span></span><br><span class="line">    log.info(<span class="string">&quot;list:&#123;&#125;&quot;</span>, list);</span><br><span class="line">    <span class="keyword">return</span> Result.success(ordersDtoPage);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></p><button type="button" class="tab-to-top" aria-label="scroll to top"><i class="fas fa-arrow-up"></i></button></div></div></div><ul><li><p>最终效果如下，输入时间段/订单号也能正常查询</p><div class="img-wrap"><div class="img-bg"><img class="img" src="https://raw.githubusercontent.com/silvan2077/markdown_pic/main/Picgo/20251107151553.png"/></div></div></li><li><p>关于用户名字段为null，去修改前端代码<code>/backend/order/list.html</code>，找到用户，将<code>userName</code>改成<code>consignee</code>就好了，如果还不显示，清除浏览器缓存再刷新重试</p><figure class="highlight html"><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">&lt;!--&lt;el-table-column prop=&quot;userName&quot; label=&quot;用户&quot;&gt;&lt;/el-table-column&gt;--&gt;</span></span><br><span class="line"><span class="tag">&lt;<span class="name">el-table-column</span> <span class="attr">prop</span>=<span class="string">&quot;consignee&quot;</span> <span class="attr">label</span>=<span class="string">&quot;用户&quot;</span>&gt;</span><span class="tag">&lt;/<span class="name">el-table-column</span>&gt;</span></span><br></pre></td></tr></table></figure></li></ul><h3 id="修改订单状态"><a href="#修改订单状态" class="headerlink" title="修改订单状态"></a>修改订单状态</h3><ul><li><p>点击上图中的<code>派送</code>按钮，查看发送的请求</p><blockquote><p>请求网址: <a href="http://localhost/order">http://localhost/order</a><br>请求方法: PUT</p></blockquote></li><li><p>携带的json数据</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></pre></td><td class="code"><pre><span class="line"><span class="punctuation">&#123;</span>   </span><br><span class="line">    status<span class="punctuation">:</span> <span class="number">3</span><span class="punctuation">,</span> </span><br><span class="line">    id<span class="punctuation">:</span> <span class="string">&quot;1986674402569883649&quot;</span></span><br><span class="line"><span class="punctuation">&#125;</span></span><br></pre></td></tr></table></figure></li><li><p>携带的status为3，那该按钮的作用应该是将订单状态设置为传入的status</p><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><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">switch</span>(row.<span class="property">status</span>)&#123;</span><br><span class="line">    <span class="keyword">case</span> <span class="number">1</span>:</span><br><span class="line">        str =  <span class="string">&#x27;待付款&#x27;</span></span><br><span class="line">    <span class="keyword">break</span>;</span><br><span class="line">    <span class="keyword">case</span> <span class="number">2</span>:</span><br><span class="line">        str =  <span class="string">&#x27;正在派送&#x27;</span></span><br><span class="line">    <span class="keyword">break</span>;</span><br><span class="line">    <span class="keyword">case</span> <span class="number">3</span>:</span><br><span class="line">        str =  <span class="string">&#x27;已派送&#x27;</span></span><br><span class="line">    <span class="keyword">break</span>;</span><br><span class="line">    <span class="keyword">case</span> <span class="number">4</span>:</span><br><span class="line">        str =  <span class="string">&#x27;已完成&#x27;</span></span><br><span class="line">    <span class="keyword">break</span>;</span><br><span class="line">    <span class="keyword">case</span> <span class="number">5</span>:</span><br><span class="line">        str =  <span class="string">&#x27;已取消&#x27;</span></span><br><span class="line">    <span class="keyword">break</span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></li><li><p>在<code>OrderController</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></pre></td><td class="code"><pre><span class="line"><span class="meta">@PutMapping</span></span><br><span class="line"><span class="keyword">public</span> Result&lt;String&gt; <span class="title function_">changeStatus</span><span class="params">(<span class="meta">@RequestBody</span> Map&lt;String, String&gt; map)</span> &#123;</span><br><span class="line">    <span class="type">int</span> <span class="variable">status</span> <span class="operator">=</span> Integer.parseInt(map.get(<span class="string">&quot;status&quot;</span>));</span><br><span class="line">    <span class="type">Long</span> <span class="variable">orderId</span> <span class="operator">=</span> Long.valueOf(map.get(<span class="string">&quot;id&quot;</span>));</span><br><span class="line">    log.info(<span class="string">&quot;修改订单状态:status=&#123;status&#125;,id=&#123;id&#125;&quot;</span>, status, orderId);</span><br><span class="line">    LambdaUpdateWrapper&lt;Orders&gt; updateWrapper = <span class="keyword">new</span> <span class="title class_">LambdaUpdateWrapper</span>&lt;&gt;();</span><br><span class="line">    updateWrapper.eq(Orders::getId, orderId);</span><br><span class="line">    updateWrapper.set(Orders::getStatus, status);</span><br><span class="line">    orderService.update(updateWrapper);</span><br><span class="line">    <span class="keyword">return</span> Result.success(<span class="string">&quot;订单状态修改成功&quot;</span>);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></li></ul><p>那么至此，应该是把页面里的所有功能都实现了</p>]]></content>
    
    
    <summary type="html">快速入门</summary>
    
    
    
    
  </entry>
  
  <entry>
    <title></title>
    <link href="https://silvan.chat/2025/11/06/%E9%80%9A%E8%BF%87%E9%82%AE%E7%AE%B1%E8%8E%B7%E5%8F%96%E9%AA%8C%E8%AF%81%E7%A0%81/"/>
    <id>https://silvan.chat/2025/11/06/%E9%80%9A%E8%BF%87%E9%82%AE%E7%AE%B1%E8%8E%B7%E5%8F%96%E9%AA%8C%E8%AF%81%E7%A0%81/</id>
    <published>2025-11-06T09:03:08.229Z</published>
    <updated>2025-11-07T01:21:48.556Z</updated>
    
    <content type="html"><![CDATA[<div class="note info no-icon flat"><ul><li>通过SpringBoot整合邮件发送，来实现短信发送和接收验证码的功能，通常用来作为手机号登录获取验证码的平替。</li><li>实测使用不同邮箱发送邮件只需要更改配置文件中的账户、授权码和SMTP服务端口即可。</li></ul></div><h2 id="准备工作"><a href="#准备工作" class="headerlink" title="准备工作"></a>准备工作</h2><ol><li>注册邮箱——此处使用qq邮箱来发送和接收验证码。</li><li>开启SMTP服务——在邮箱的设置中开启SMTP服务，获取16位的授权码。<div class="img-wrap"><div class="img-bg"><img class="img" src="https://raw.githubusercontent.com/silvan2077/markdown_pic/main/Picgo/20251106171254.png"/></div></div></li><li>创建SpringBoot项目，引入邮件发送的依赖。<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.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-mail<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></li></ol><h2 id="开发简单邮件"><a href="#开发简单邮件" class="headerlink" title="开发简单邮件"></a>开发简单邮件</h2><div class="note info no-icon flat"><p>简单邮件：仅包含邮件标题，邮件正文的普通邮件（无附件，无图片）</p></div><ul><li><p>在配置文件中添加上右键配置需要的mail参数,<a href="https://wx.mail.qq.com/list/readtemplate?name=app_intro.html#/agreement/authorizationCode">qq邮箱的配置方法</a></p><div class="tabs" id="开发简单邮件"><ul class="nav-tabs"><li class="tab active"><button type="button" data-href="#开发简单邮件-1">配置文件</button></li><li class="tab"><button type="button" data-href="#开发简单邮件-2">说明</button></li></ul><div class="tab-contents"><div class="tab-item-content active" id="开发简单邮件-1"><figure class="highlight yml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><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="attr">spring:</span></span><br><span class="line">  <span class="attr">mail:</span></span><br><span class="line">    <span class="attr">password:</span> <span class="string">jliccztlnhnvjaji</span></span><br><span class="line">    <span class="attr">username:</span> <span class="string">邮件地址</span></span><br><span class="line">    <span class="attr">host:</span> <span class="string">邮件的SMTP服务器地址</span> <span class="comment"># qq邮箱的SMTP服务器地址是smtp.qq.com</span></span><br><span class="line">    <span class="attr">port:</span> <span class="number">465</span> <span class="comment"># 发送的端口号必须是 465 或 587</span></span><br><span class="line">    <span class="attr">properties:</span></span><br><span class="line">      <span class="attr">mail:</span></span><br><span class="line">        <span class="attr">smtp:</span></span><br><span class="line">          <span class="attr">auth:</span> <span class="literal">true</span></span><br><span class="line">          <span class="attr">ssl:</span></span><br><span class="line">            <span class="attr">enable:</span> <span class="literal">true</span></span><br></pre></td></tr></table></figure><button type="button" class="tab-to-top" aria-label="scroll to top"><i class="fas fa-arrow-up"></i></button></div><div class="tab-item-content" id="开发简单邮件-2"><div class="img-wrap"><div class="img-bg"><img class="img" src="https://raw.githubusercontent.com/silvan2077/markdown_pic/main/Picgo/20251106171903.png"/></div></div><button type="button" class="tab-to-top" aria-label="scroll to top"><i class="fas fa-arrow-up"></i></button></div></div></div></li><li><p>API</p><ul><li>JavaMailSender: 发送邮件的客户端<ul><li>send(mail)</li></ul></li><li>SimpleMailMessage: 封装简单邮件的Bean<ul><li>setSubject(标题)</li><li>setText(正文)</li><li>setFrom(发送人)</li><li>setTo(接收人)</li></ul></li></ul></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="meta">@SpringBootTest</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">MailTest</span> &#123;</span><br><span class="line">    <span class="meta">@Autowired</span></span><br><span class="line">    <span class="keyword">private</span> JavaMailSender sender;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">private</span> <span class="type">String</span> <span class="variable">subject</span> <span class="operator">=</span> <span class="string">&quot;邮件标题&quot;</span>;</span><br><span class="line">    <span class="keyword">private</span> <span class="type">String</span> <span class="variable">content</span> <span class="operator">=</span> <span class="string">&quot;邮件内容&quot;</span>;</span><br><span class="line">    <span class="keyword">private</span> <span class="type">String</span> <span class="variable">to</span> <span class="operator">=</span> <span class="string">&quot;1330132229@qq.com&quot;</span>;</span><br><span class="line">    <span class="keyword">private</span> <span class="type">String</span> <span class="variable">from</span> <span class="operator">=</span> to;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Test</span></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">testSimpleMail</span><span class="params">()</span>&#123;</span><br><span class="line">        <span class="type">SimpleMailMessage</span> <span class="variable">mail</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">SimpleMailMessage</span>();</span><br><span class="line">        mail.setSubject(subject);</span><br><span class="line">        mail.setTo(to);</span><br><span class="line">        mail.setFrom(from);</span><br><span class="line">        mail.setText(content);</span><br><span class="line">        sender.send(mail);</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><ul><li>此时就能接收到发送的邮件了。<div class="img-wrap"><div class="img-bg"><img class="img" src="https://raw.githubusercontent.com/silvan2077/markdown_pic/main/Picgo/20251106172736.png"/></div></div></li></ul><h2 id="开发复杂邮件"><a href="#开发复杂邮件" class="headerlink" title="开发复杂邮件"></a>开发复杂邮件</h2><div class="note info no-icon flat"><p>复杂邮件：包含邮件标题，邮件正文，附件，图片等的邮件</p></div><ul><li>API<ul><li>JavaMailSender: 发送邮件的客户端<ul><li>send(mail)</li></ul></li><li>MimeMessage: 封装复杂邮件的Bean<ul><li>sender.createMimeMessage()</li></ul></li><li>MimeMessageHelper: 封装复杂邮件的辅助工具<ul><li>setSubject(标题)</li><li>setText(正文, isHtml)</li><li>setFrom(发送人)</li><li>setTo(接收人)</li><li>addAttachment(附件名, 附件文件)</li></ul></li></ul></li><li>结果预览：<div class="img-wrap"><div class="img-bg"><img class="img" src="https://raw.githubusercontent.com/silvan2077/markdown_pic/main/Picgo/20251106180003.png"/></div></div></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></pre></td><td class="code"><pre><span class="line"><span class="meta">@SpringBootTest</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">MailTest</span> &#123;</span><br><span class="line">    <span class="meta">@Autowired</span></span><br><span class="line">    <span class="keyword">private</span> JavaMailSender sender;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">private</span> <span class="type">String</span> <span class="variable">subject</span> <span class="operator">=</span> <span class="string">&quot;邮件标题&quot;</span>;</span><br><span class="line">    <span class="keyword">private</span> <span class="type">String</span> <span class="variable">content</span> <span class="operator">=</span> <span class="string">&quot;邮件内容&quot;</span>;</span><br><span class="line">    <span class="keyword">private</span> <span class="type">String</span> <span class="variable">to</span> <span class="operator">=</span> <span class="string">&quot;18669513992@163.com&quot;</span>;</span><br><span class="line">    <span class="keyword">private</span> <span class="type">String</span> <span class="variable">from</span> <span class="operator">=</span> to;</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">@Test</span></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">testSimpleMail</span><span class="params">()</span>&#123;</span><br><span class="line">        <span class="type">SimpleMailMessage</span> <span class="variable">mail</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">SimpleMailMessage</span>();</span><br><span class="line">        mail.setSubject(subject);</span><br><span class="line">        mail.setTo(to);</span><br><span class="line">        mail.setFrom(from);</span><br><span class="line">        mail.setText(content);</span><br><span class="line">        sender.send(mail);</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">/**</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">@Test</span></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">testMimeMail</span><span class="params">()</span> <span class="keyword">throws</span> MessagingException &#123;</span><br><span class="line">        <span class="type">MimeMessage</span> <span class="variable">mail</span> <span class="operator">=</span> sender.createMimeMessage();</span><br><span class="line"><span class="comment">//        通过辅助工具来创建邮件,如果邮件中有附件则需要设置第二个参数为true，用来设置邮件结构为 Multipart（多部分）模式</span></span><br><span class="line">        <span class="type">MimeMessageHelper</span> <span class="variable">helper</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">MimeMessageHelper</span>(mail,<span class="literal">true</span>);</span><br><span class="line"></span><br><span class="line"><span class="comment">//        这几个方法有异常，需要抛出</span></span><br><span class="line">        helper.setSubject(subject);</span><br><span class="line"><span class="comment">//        第二个参数为标志位，用来确定是否解析邮件中的html语法，设置为false则不会解析html语法</span></span><br><span class="line">        helper.setText(content+<span class="string">&quot;&lt;img src=https://raw.githubusercontent.com/silvan2077/markdown_pic/main/Picgo/20251106171903.png &gt;&quot;</span>,<span class="literal">true</span>);</span><br><span class="line">        helper.setTo(to);</span><br><span class="line">        helper.setFrom(from);</span><br><span class="line"></span><br><span class="line"><span class="comment">//        添加附件</span></span><br><span class="line">        <span class="type">File</span> <span class="variable">file</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">File</span>(<span class="string">&quot;D:\\7jgyre.png&quot;</span>);</span><br><span class="line">        helper.addAttachment(file.getName(), file);</span><br><span class="line"></span><br><span class="line"><span class="comment">//        发送邮件</span></span><br><span class="line">        sender.send(mail);</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>]]></content>
    
    
      
      
    <summary type="html">&lt;div class=&quot;note info no-icon flat&quot;&gt;&lt;ul&gt;
&lt;li&gt;通过SpringBoot整合邮件发送，来实现短信发送和接收验证码的功能，通常用来作为手机号登录获取验证码的平替。&lt;/li&gt;
&lt;li&gt;实测使用不同邮箱发送邮件只需要更改配置文件中的账户、授权码</summary>
      
    
    
    
    
  </entry>
  
  <entry>
    <title>小破站概述</title>
    <link href="https://silvan.chat/2025/10/29/%E7%BD%91%E7%AB%99/"/>
    <id>https://silvan.chat/2025/10/29/%E7%BD%91%E7%AB%99/</id>
    <published>2025-10-29T10:23:49.389Z</published>
    <updated>2025-10-29T10:28:29.216Z</updated>
    
    <content type="html"><![CDATA[<p><strong><em>这只是一个初步接触计算机的懵懂大学生的小站，简单记录一下自己的学习笔记~~~</em></strong></p>]]></content>
    
    
    <summary type="html">这只是一个初步接触计算机的懵懂大学生的小站，简单记录一下自己的学习笔记~~~</summary>
    
    
    
    
  </entry>
  
  <entry>
    <title>GKD开屏广告跳过工具</title>
    <link href="https://silvan.chat/2025/10/29/GKD%E5%BC%80%E5%B1%8F%E5%B9%BF%E5%91%8A%E8%B7%B3%E8%BF%87%E5%B7%A5%E5%85%B7/"/>
    <id>https://silvan.chat/2025/10/29/GKD%E5%BC%80%E5%B1%8F%E5%B9%BF%E5%91%8A%E8%B7%B3%E8%BF%87%E5%B7%A5%E5%85%B7/</id>
    <published>2025-10-29T02:56:29.376Z</published>
    <updated>2025-11-13T04:50:52.764Z</updated>
    
    <content type="html"><![CDATA[<div class="note info no-icon flat"><p>GKD是一个开源的App开屏广告跳过工具，只需要开启无障碍服务并导入规则链接即可</p></div><ul><li>官网地址: <a href="https://gkd.li/">https://gkd.li/</a></li><li>第三方订阅规则: <a href="https://github.com/topics/gkd-subscription">https://github.com/topics/gkd-subscription</a></li><li>Github开源地址: <a href="https://github.com/gkd-kit/gkd">https://github.com/gkd-kit/gkd</a></li></ul><div class="img-wrap"><div class="img-bg"><img class="img" src="https://camo.githubusercontent.com/1480164eac3e6f5acf7601d74372ef29649573576dec632f32d67b4716772b6a/68747470733a2f2f652e676b642e6c692f37306161333235372d613766302d346162662d383162612d303234383636363363323438" style="width:200px;"/></div></div><p>作者提供的帮助文档：<a href="https://gkd.li/guide/">https://gkd.li/guide/</a><br>shizuku开源地址: <a href="https://github.com/RikkaApps/Shizuku">https://github.com/RikkaApps/Shizuku</a><br><div class="note warning no-icon flat"><p>耗电说明：</p><ul><li>GKD默认情况并不耗电，真正耗电在于<code>规则的启用</code>和<code>当前应用界面无障碍节点数量</code>以及<code>无障碍时间刷新频率</code></li><li>追求低耗电，应当开启少量的规则</li></ul></div></p>]]></content>
    
    
      
      
    <summary type="html">&lt;div class=&quot;note info no-icon flat&quot;&gt;&lt;p&gt;GKD是一个开源的App开屏广告跳过工具，只需要开启无障碍服务并导入规则链接即可&lt;/p&gt;
&lt;/div&gt;
&lt;ul&gt;
&lt;li&gt;官网地址: &lt;a href=&quot;https://gkd.li/&quot;&gt;https://</summary>
      
    
    
    
    
  </entry>
  
  <entry>
    <title>算法入门</title>
    <link href="https://silvan.chat/2025/10/28/%E6%95%B0%E6%8D%AE%E7%BB%93%E6%9E%84-%E7%AE%97%E6%B3%95/"/>
    <id>https://silvan.chat/2025/10/28/%E6%95%B0%E6%8D%AE%E7%BB%93%E6%9E%84-%E7%AE%97%E6%B3%95/</id>
    <published>2025-10-28T14:27:41.660Z</published>
    <updated>2025-10-28T14:27:41.660Z</updated>
    
    <content type="html"><![CDATA[<h1 id="从社会实验到算法"><a href="#从社会实验到算法" class="headerlink" title="从社会实验到算法"></a>从社会实验到算法</h1><div class="note no-icon flat"><p>一开始有100个人，每个人都有100元<br>在每一轮都做如下的事情 :<br>每个人都必须拿出1元钱给除自己以外的其他人，给谁完全随机<br>如果某个人在这一轮的钱数为0，那么他可以不给，但是可以接收<br>发生很多很多轮之后，这100人的社会财富分布很均匀吗？此时社会的<code>基尼系数</code>会是多少？</p></div><ul><li>首先从一个社会实验开始，该实验的规则看似非常公平，甚至对于0元者还有”优待”，所以乍一眼望去我们会感觉应当随着次数增多，金币的分布会越平均，于是我们编写一个Java程序来模拟这个实验，并计算出这个社会的基尼系数。</li></ul><div class="tabs" id="模拟实验"><ul class="nav-tabs"><li class="tab active"><button type="button" data-href="#模拟实验-1">实验模拟</button></li><li class="tab"><button type="button" data-href="#模拟实验-2">实验进行100次</button></li><li class="tab"><button type="button" data-href="#模拟实验-3">实验进行100000次</button></li></ul><div class="tab-contents"><div class="tab-item-content active" id="模拟实验-1"><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><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="keyword">public</span> <span class="keyword">class</span> <span class="title class_">Experiment</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;一个社会的基尼系数是一个在0~1之间的小数&quot;</span>);</span><br><span class="line">        System.out.println(<span class="string">&quot;基尼系数为0代表所有人的财富完全一样&quot;</span>);</span><br><span class="line">        System.out.println(<span class="string">&quot;基尼系数为1代表有1个人掌握了全社会的财富&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;在2022年，世界各国的平均基尼系数为0.44&quot;</span>);</span><br><span class="line">        System.out.println(<span class="string">&quot;目前普遍认为，当基尼系数到达 0.5 时&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 class="type">int</span> <span class="variable">n</span> <span class="operator">=</span> <span class="number">100</span>;</span><br><span class="line">        <span class="type">int</span> <span class="variable">t</span> <span class="operator">=</span> <span class="number">1000</span>;</span><br><span class="line">        System.out.println(<span class="string">&quot;人数 : &quot;</span> + n);</span><br><span class="line">        System.out.println(<span class="string">&quot;轮数 : &quot;</span> + t);</span><br><span class="line">        experiment(n, t);</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">// 完全按照说的来实验</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_">experiment</span><span class="params">(<span class="type">int</span> n, <span class="type">int</span> t)</span> &#123;</span><br><span class="line">        <span class="type">double</span>[] wealth = <span class="keyword">new</span> <span class="title class_">double</span>[n];</span><br><span class="line">        Arrays.fill(wealth, <span class="number">100</span>);</span><br><span class="line">        <span class="type">boolean</span>[] hasMoney = <span class="keyword">new</span> <span class="title class_">boolean</span>[n];</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; t; i++) &#123;</span><br><span class="line">            Arrays.fill(hasMoney, <span class="literal">false</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; n; j++) &#123;</span><br><span class="line">                <span class="keyword">if</span> (wealth[j] &gt; <span class="number">0</span>) &#123;</span><br><span class="line">                    hasMoney[j] = <span class="literal">true</span>;</span><br><span class="line">                &#125;</span><br><span class="line">            &#125;</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; n; j++) &#123;</span><br><span class="line">                <span class="keyword">if</span> (hasMoney[j]) &#123;</span><br><span class="line">                    <span class="type">int</span> <span class="variable">other</span> <span class="operator">=</span> j;</span><br><span class="line">                    <span class="keyword">do</span> &#123;</span><br><span class="line">                        <span class="comment">// (int) (Math.random() * n);</span></span><br><span class="line">                        <span class="comment">// int : 0 ~ n-1，等概率随机</span></span><br><span class="line">                        other = (<span class="type">int</span>) (Math.random() * n);</span><br><span class="line">                    &#125; <span class="keyword">while</span> (other == j);</span><br><span class="line">                    wealth[j]--;</span><br><span class="line">                    wealth[other]++;</span><br><span class="line">                &#125;</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;</span><br><span class="line">        Arrays.sort(wealth);</span><br><span class="line">        System.out.println(<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">0</span>; i &lt; n; i++) &#123;</span><br><span class="line">            System.out.print((<span class="type">int</span>) wealth[i] + <span class="string">&quot; &quot;</span>);</span><br><span class="line">            <span class="keyword">if</span> (i % <span class="number">10</span> == <span class="number">9</span>) &#123;</span><br><span class="line">                System.out.println();</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;</span><br><span class="line">        System.out.println();</span><br><span class="line">        System.out.println(<span class="string">&quot;这个社会的基尼系数为 : &quot;</span> + calculateGini(wealth));</span><br><span 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="type">double</span> <span class="title function_">calculateGini</span><span class="params">(<span class="type">double</span>[] wealth)</span> &#123;</span><br><span class="line">        <span class="type">double</span> <span class="variable">sumOfAbsoluteDifferences</span> <span class="operator">=</span> <span class="number">0</span>;</span><br><span class="line">        <span class="type">double</span> <span class="variable">sumOfWealth</span> <span class="operator">=</span> <span class="number">0</span>;</span><br><span class="line">        <span class="type">int</span> <span class="variable">n</span> <span class="operator">=</span> wealth.length;</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; n; i++) &#123;</span><br><span class="line">            sumOfWealth += wealth[i];</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; n; j++) &#123;</span><br><span class="line">                sumOfAbsoluteDifferences += Math.abs(wealth[i] - wealth[j]);</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">return</span> sumOfAbsoluteDifferences / (<span class="number">2</span> * n * sumOfWealth);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><button type="button" class="tab-to-top" aria-label="scroll to top"><i class="fas fa-arrow-up"></i></button></div><div class="tab-item-content" id="模拟实验-2"><figure class="highlight txt"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><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">一个社会的基尼系数是一个在0~1之间的小数</span><br><span class="line">基尼系数为0代表所有人的财富完全一样</span><br><span class="line">基尼系数为1代表有1个人掌握了全社会的财富</span><br><span class="line">基尼系数越小，代表社会财富分布越均衡；越大则代表财富分布越不均衡</span><br><span class="line">在2022年，世界各国的平均基尼系数为0.44</span><br><span class="line">目前普遍认为，当基尼系数到达 0.5 时</span><br><span class="line">就意味着社会贫富差距非常大，分布非常不均匀</span><br><span class="line">社会可能会因此陷入危机，比如大量的犯罪或者经历社会动荡</span><br><span class="line">测试开始</span><br><span class="line">人数 : 100</span><br><span class="line">轮数 : 100</span><br><span class="line">列出每个人的财富(贫穷到富有) : </span><br><span class="line">80 81 82 84 84 84 85 86 86 87 </span><br><span class="line">87 88 89 89 89 90 90 91 91 91 </span><br><span class="line">92 92 92 92 92 93 94 94 94 95 </span><br><span class="line">95 95 95 96 96 97 97 97 97 97 </span><br><span class="line">97 98 98 98 98 98 99 99 99 100 </span><br><span class="line">100 100 100 100 100 101 101 101 101 101 </span><br><span class="line">101 101 102 102 102 102 102 103 103 103 </span><br><span class="line">104 104 104 105 105 105 106 107 107 107 </span><br><span class="line">108 108 109 109 110 111 112 113 113 113 </span><br><span class="line">116 116 116 119 119 120 120 123 124 131 </span><br><span class="line"></span><br><span class="line">这个社会的基尼系数为 : 0.056606</span><br><span class="line">测试结束</span><br><span class="line"></span><br><span class="line">进程已结束，退出代码为 0</span><br></pre></td></tr></table></figure><button type="button" class="tab-to-top" aria-label="scroll to top"><i class="fas fa-arrow-up"></i></button></div><div class="tab-item-content" id="模拟实验-3"><figure class="highlight txt"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><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">一个社会的基尼系数是一个在0~1之间的小数</span><br><span class="line">基尼系数为0代表所有人的财富完全一样</span><br><span class="line">基尼系数为1代表有1个人掌握了全社会的财富</span><br><span class="line">基尼系数越小，代表社会财富分布越均衡；越大则代表财富分布越不均衡</span><br><span class="line">在2022年，世界各国的平均基尼系数为0.44</span><br><span class="line">目前普遍认为，当基尼系数到达 0.5 时</span><br><span class="line">就意味着社会贫富差距非常大，分布非常不均匀</span><br><span class="line">社会可能会因此陷入危机，比如大量的犯罪或者经历社会动荡</span><br><span class="line">测试开始</span><br><span class="line">人数 : 100</span><br><span class="line">轮数 : 1000000</span><br><span class="line">列出每个人的财富(贫穷到富有) : </span><br><span class="line">0 1 2 4 4 4 5 6 8 9 </span><br><span class="line">9 11 11 11 12 13 13 14 14 16 </span><br><span class="line">17 20 20 20 25 26 27 27 28 29 </span><br><span class="line">30 30 32 32 34 36 36 37 37 39 </span><br><span class="line">41 44 46 47 49 53 53 53 53 55 </span><br><span class="line">56 60 63 63 63 72 75 90 92 93 </span><br><span class="line">93 94 95 97 97 103 105 108 111 111 </span><br><span class="line">112 114 114 121 128 129 136 143 146 151 </span><br><span class="line">154 160 170 181 183 189 197 202 227 231 </span><br><span class="line">239 272 273 279 304 310 355 419 484 893 </span><br><span class="line"></span><br><span class="line">这个社会的基尼系数为 : 0.552244</span><br><span class="line">测试结束</span><br><span class="line"></span><br><span class="line">进程已结束，退出代码为 0</span><br><span class="line"></span><br></pre></td></tr></table></figure><button type="button" class="tab-to-top" aria-label="scroll to top"><i class="fas fa-arrow-up"></i></button></div></div></div><ul><li>这是一个乍一看很公平的实验，但实际中，随着模拟的次数越来越多，这个实验的基尼系数会非常高</li></ul><details class="folding-tag" cyan><summary> 实验原因 </summary>              <div class='content'>              <ul><li>刚开始次数过少，财富差距无法拉开明显差距所以基尼系数很小，但随着次数越来越多，逐渐出现“少数人掌握大量财富，多数人财富微薄甚至为 0” 的格局</li><li><strong>关键机制 1：<code>单轮内 “固定支出 + 随机收入” 的不对称，必然产生微小差距</code></strong><ul><li><strong>支出端（刚性固定）</strong>：只要手里有钱（&gt;0 元），无论你有 100 元还是 1 元，都必须拿出1 元（支出金额与自身财富无关，是 “定额税” 式的强制流出）；</li><li><strong>收入端（完全随机）</strong>：所有人拿出的钱（总捐赠额 = 当前非 0 元人数 ×1 元）会随机分给 100 人（包括 0 元者），每人收到的金额可能是 0 元、1 元、2 元…… 最多等于总捐赠额。</li></ul></li><li><strong>关键机制 2：<code>多轮 “差距累积效应”，让微小差距滚雪球式放大</code></strong><ul><li>单轮的微小差距（如 99 元 vs 101 元），不会因后续轮次 “随机抵消”，反而会复利式累积</li><li>财富基数的 “非对称影响”：同样是 1 元的支出 / 收入，对富人（如 200 元）和穷人（如 10 元）的影响天差地别 ——<ul><li>富人支出 1 元，仅减少 0.5% 财富，几乎无影响；</li><li>穷人支出 1 元，减少 10% 财富，极易陷入 “财富缩水→变 0 元” 的循环。</li></ul></li></ul></li><li><strong>关键机制 3：<code>0 元者 “豁免机制”，进一步固化差距</code></strong><ul><li>若 0 元者收到 1 元（变成 1 元），下一轮必须支出 1 元；若再没收到收入，又会回到 0 元，陷入 “0 元→1 元→0 元” 的死循环，无法形成财富基数。</li><li>非 0 元者的 “优势循环”：财富多的人（如 200 元）几乎不可能变 0 元，能持续参与 “支出 1 元 + 接收收入”—— 即使短期收到 0 元，财富仅从 200→199，仍有足够基数应对后续波动，且有更多机会 “接住” 他人的随机捐赠，财富持续增长。</li></ul></li><li><p>基尼系数随轮次上升的本质：<strong>财富离散度持续扩大</strong></p></li><li><p>总结：“随机分配”≠“均匀分配”</p></li><li><strong>这个实验的核心启示是：<code>规则的微小不对称，会通过 “累积效应” 被无限放大。</code></strong>即使初始绝对均匀，只要存在 “固定支出 + 随机收入” 的不对称，以及 “0 元者难以积累” 的机制，随机波动就不会抵消差距，反而会让财富逐渐向少数人集中 —— 这也是现实中 “马太效应”（富者愈富，贫者愈贫）的简化模型。</li></ul>              </div>            </details><ul><li>这个实验的目的仅仅是引起对学习算法的兴趣，告诉我们不要以感觉为标准来判断算法，应当认真思考，仔细学习。</li></ul><h1 id="选择、冒泡、插入排序"><a href="#选择、冒泡、插入排序" class="headerlink" title="选择、冒泡、插入排序"></a>选择、冒泡、插入排序</h1><ul><li>这是算法中最基础的排序方法，也有人叫他们：<psw>三傻排序</psw></li></ul><h2 id="选择排序"><a href="#选择排序" class="headerlink" title="选择排序"></a>选择排序</h2><ul><li>选择排序的原理：每次从待排序的元素中找出最小的元素，将其放到已排序的元素序列的末尾。</li><li><p>一句话总结：i~n-1的范围上，找到最小值并放在i位置，然后i+1~n-1的范围上，找到最小值并放在i+1位置，以此类推，直到n-1位置。</p></li><li><p>代码实现过程：</p><ul><li>先设置一个变量来不断存储最小值，初始值为第一个元素。</li><li>采用双层循环，外循环用来遍历数组，内循环在遍历过程中寻找每次的最小值。</li><li>在整个数组遍历中，不断比较每一个元素和最小值的大小，若找到更小的元素，则更新最小值，并记录下当前位置。</li><li>在执行完一次内循环以后，用交换最小值和i的位置，将最小值放到已排序的元素序列的末尾。</li></ul></li></ul><div class="tabs" id="选择排序"><ul class="nav-tabs"><li class="tab active"><button type="button" data-href="#选择排序-1">核心代码</button></li><li class="tab"><button type="button" data-href="#选择排序-2">完整代码</button></li></ul><div class="tab-contents"><div class="tab-item-content active" id="选择排序-1"><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><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">static</span> <span class="keyword">void</span> <span class="title function_">selectSort</span><span class="params">(<span class="type">int</span>[] arr)</span> &#123;</span><br><span class="line">    <span class="comment">//此时元素数量过少无需排序，直接返回</span></span><br><span class="line">    <span class="keyword">if</span>(arr ==<span class="literal">null</span>|| arr.length&lt;<span class="number">2</span>)&#123;</span><br><span class="line">        <span class="keyword">return</span>;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="comment">//记录最小值的下标</span></span><br><span class="line">    <span class="type">int</span> minIndex;</span><br><span class="line">    <span class="comment">//记录最小值</span></span><br><span class="line">    <span class="type">int</span> minVal;</span><br><span class="line">    <span class="keyword">for</span>(<span class="type">int</span> i=<span class="number">0</span>;i&lt;arr.length-<span class="number">1</span>;i++)&#123;</span><br><span class="line">        <span class="comment">// 每次开始循环更新最小值和下标，更新为当前进行的元素</span></span><br><span class="line">        minIndex = i;</span><br><span class="line">        minVal = arr[i];</span><br><span class="line">        <span class="comment">// 从i+1开始循环，找到最小值</span></span><br><span class="line">        <span class="keyword">for</span>(<span class="type">int</span> j=i+<span class="number">1</span>;j&lt;arr.length;j++)&#123;</span><br><span class="line">            <span class="comment">//找到最小值则将最小值的位置更新为当前元素</span></span><br><span class="line">            <span class="keyword">if</span>(arr[j]&lt;minVal)&#123;</span><br><span class="line">                minIndex = j;</span><br><span class="line">                minVal=arr[j];</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">        <span class="comment">//如果下标值不变则证明当前位置便是未排序的部分中最小的元素</span></span><br><span class="line">        <span class="keyword">if</span>(minIndex!=i)&#123;</span><br><span class="line">            swap(arr,i,minIndex);</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><button type="button" class="tab-to-top" aria-label="scroll to top"><i class="fas fa-arrow-up"></i></button></div><div class="tab-item-content" id="选择排序-2"><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><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">public</span> <span class="keyword">class</span> <span class="title class_">SelectSort</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_">swap</span><span class="params">(<span class="type">int</span>[] arr, <span class="type">int</span> i, <span class="type">int</span> j)</span> &#123;</span><br><span class="line">        <span class="type">int</span> <span class="variable">temp</span> <span class="operator">=</span> arr[i];</span><br><span class="line">        arr[i] = arr[j];</span><br><span class="line">        arr[j] = temp;</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>[] randomArray(<span class="type">int</span> n)&#123;</span><br><span class="line">        <span class="type">int</span>[] array = <span class="keyword">new</span> <span class="title class_">int</span>[n];</span><br><span class="line">        <span class="keyword">for</span>(<span class="type">int</span> i=<span class="number">0</span>;i&lt;n;i++)&#123;</span><br><span class="line">            array[i]=(<span class="type">int</span>)(Math.random()*<span class="number">100</span>);</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">return</span> array;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title function_">selectSort</span><span class="params">(<span class="type">int</span>[] arr)</span> &#123;</span><br><span class="line"></span><br><span class="line">        <span class="keyword">if</span>(arr ==<span class="literal">null</span>|| arr.length&lt;<span class="number">2</span>)&#123;</span><br><span class="line">            <span class="keyword">return</span>;</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="comment">//记录最小值的下标</span></span><br><span class="line">        <span class="type">int</span> minIndex;</span><br><span class="line">        <span class="comment">//记录最小值</span></span><br><span class="line">        <span class="type">int</span> <span class="variable">minVal</span> <span class="operator">=</span> arr[<span class="number">0</span>];</span><br><span class="line">        <span class="keyword">for</span>(<span class="type">int</span> i=<span class="number">0</span>;i&lt;arr.length-<span class="number">1</span>;i++)&#123;</span><br><span class="line">            minIndex = i;</span><br><span class="line">            minVal = arr[i];</span><br><span class="line">            <span class="keyword">for</span>(<span class="type">int</span> j=i+<span class="number">1</span>;j&lt;arr.length;j++)&#123;</span><br><span class="line">                <span class="keyword">if</span>(arr[j]&lt;minVal)&#123;</span><br><span class="line">                    minIndex = j;</span><br><span class="line">                    minVal=arr[j];</span><br><span class="line">                &#125;</span><br><span class="line">            &#125;</span><br><span class="line">            <span class="keyword">if</span>(minIndex!=i)&#123;</span><br><span class="line">                swap(arr,i,minIndex);</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">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>[] arr = randomArray(<span class="number">10</span>);</span><br><span class="line">        selectSort(arr);</span><br><span class="line">        <span class="keyword">for</span>(<span class="type">int</span> i=<span class="number">0</span>;i&lt;arr.length;i++)&#123;</span><br><span class="line">            System.out.print(arr[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></pre></td></tr></table></figure><button type="button" class="tab-to-top" aria-label="scroll to top"><i class="fas fa-arrow-up"></i></button></div></div></div><h2 id="冒泡排序"><a href="#冒泡排序" class="headerlink" title="冒泡排序"></a>冒泡排序</h2><ul><li>冒泡排序是一种简单直观的排序算法。它的工作原理是重复地走访过要排序的数列，一次比较两个元素，如果它们的顺序错误就把它们交换过来。走访数列的工作是重复地进行直到没有再需要交换，也就是说该数列已经排序完成。</li><li><p>每次循环从数组前边逐个筛选较大的值放到尾部</p></li><li><p>代码实现过程：</p><ul><li>循环判断后，将大的值放到数组尾部后，便不再判断尾部的值</li><li>可以理解为从数组最大的值开始找起，找到最大值后排好，再找下一个最大值，直到数组排序完成</li></ul></li></ul><div class="tabs" id="冒泡排序"><ul class="nav-tabs"><li class="tab active"><button type="button" data-href="#冒泡排序-1">核心代码</button></li><li class="tab"><button type="button" data-href="#冒泡排序-2">完整代码</button></li></ul><div class="tab-contents"><div class="tab-item-content active" id="冒泡排序-1"><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><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">static</span> <span class="keyword">void</span> <span class="title function_">bubbleSort</span><span class="params">(<span class="type">int</span>[] arr)</span> &#123;</span><br><span class="line">    <span class="keyword">if</span>(arr ==<span class="literal">null</span>|| arr.length&lt;<span class="number">2</span>)&#123;</span><br><span class="line">        <span class="keyword">return</span>;</span><br><span class="line">    &#125;</span><br><span class="line"><span class="comment">//        0~n-1</span></span><br><span class="line"><span class="comment">//        0~n-2</span></span><br><span class="line"><span class="comment">//        0~n-3</span></span><br><span class="line">    <span class="comment">//每结束一次循环代表找到一个最大值，排好一位数，以此往复</span></span><br><span class="line">    <span class="keyword">for</span>(<span class="type">int</span> end=arr.length-<span class="number">1</span>;end&gt;<span class="number">0</span>;end--)&#123;</span><br><span class="line">        <span class="keyword">for</span>(<span class="type">int</span> start=<span class="number">0</span>;start&lt;end;start++)&#123;</span><br><span class="line">            <span class="comment">//在数组中不断判断找到最大值</span></span><br><span class="line">            <span class="keyword">if</span>(arr[start]&gt;arr[start+<span class="number">1</span>])&#123;</span><br><span class="line">                swap(arr,start,start+<span class="number">1</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><button type="button" class="tab-to-top" aria-label="scroll to top"><i class="fas fa-arrow-up"></i></button></div><div class="tab-item-content" id="冒泡排序-2"><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><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">public</span> <span class="keyword">class</span> <span class="title class_">SelectSort</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_">swap</span><span class="params">(<span class="type">int</span>[] arr, <span class="type">int</span> i, <span class="type">int</span> j)</span> &#123;</span><br><span class="line">        <span class="type">int</span> <span class="variable">temp</span> <span class="operator">=</span> arr[i];</span><br><span class="line">        arr[i] = arr[j];</span><br><span class="line">        arr[j] = temp;</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>[] randomArray(<span class="type">int</span> n)&#123;</span><br><span class="line">        <span class="type">int</span>[] array = <span class="keyword">new</span> <span class="title class_">int</span>[n];</span><br><span class="line">        <span class="keyword">for</span>(<span class="type">int</span> i=<span class="number">0</span>;i&lt;n;i++)&#123;</span><br><span class="line">            array[i]=(<span class="type">int</span>)(Math.random()*<span class="number">100</span>);</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">return</span> array;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title function_">bubbleSort</span><span class="params">(<span class="type">int</span>[] arr)</span> &#123;</span><br><span class="line">        <span class="keyword">if</span>(arr ==<span class="literal">null</span>|| arr.length&lt;<span class="number">2</span>)&#123;</span><br><span class="line">            <span class="keyword">return</span>;</span><br><span class="line">        &#125;</span><br><span class="line"><span class="comment">//        0~n-1</span></span><br><span class="line"><span class="comment">//        0~n-2</span></span><br><span class="line"><span class="comment">//        0~n-3</span></span><br><span class="line">        <span class="comment">//每结束一次循环代表找到一个最大值，排好一位数，以此往复</span></span><br><span class="line">        <span class="keyword">for</span>(<span class="type">int</span> end=arr.length-<span class="number">1</span>;end&gt;<span class="number">0</span>;end--)&#123;</span><br><span class="line">            <span class="keyword">for</span>(<span class="type">int</span> start=<span class="number">0</span>;start&lt;end;start++)&#123;</span><br><span class="line">                <span class="comment">//在数组中不断判断找到最大值</span></span><br><span class="line">                <span class="keyword">if</span>(arr[start]&gt;arr[start+<span class="number">1</span>])&#123;</span><br><span class="line">                    swap(arr,start,start+<span class="number">1</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="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>[] arr = randomArray(<span class="number">10</span>);</span><br><span class="line">        bubbleSort(arr);</span><br><span class="line">        <span class="keyword">for</span>(<span class="type">int</span> i=<span class="number">0</span>;i&lt;arr.length;i++)&#123;</span><br><span class="line">            System.out.print(arr[i]+<span class="string">&quot; &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></pre></td></tr></table></figure><button type="button" class="tab-to-top" aria-label="scroll to top"><i class="fas fa-arrow-up"></i></button></div></div></div><h2 id="插入排序"><a href="#插入排序" class="headerlink" title="插入排序"></a>插入排序</h2><ul><li>插入排序的工作原理是将未排序的数组元素插入到已排序的数组中，从而得到一个新的有序数组。</li><li><p>类似于打扑克牌，将手上的牌排好一部分后，未排好的牌依次按顺序插入到已排好的牌中，最终达到整体有序的效果。</p></li><li><p>代码实现过程：</p><ul><li>将数组从0~1,0~2,0~3…0~n-1依次进行排序。</li><li>将0~1的元素插入到0~0的数组中，得到0~1有序的数组。</li><li>将0~2的元素插入到0~0,0~1的数组中，得到0~2有序的数组。</li><li>以此类推</li></ul></li></ul><div class="tabs" id="插入排序"><ul class="nav-tabs"><li class="tab active"><button type="button" data-href="#插入排序-1">核心代码</button></li><li class="tab"><button type="button" data-href="#插入排序-2">完整代码</button></li></ul><div class="tab-contents"><div class="tab-item-content active" id="插入排序-1"><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><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">static</span> <span class="keyword">void</span> <span class="title function_">insertionSort</span><span class="params">(<span class="type">int</span>[] arr)</span>&#123;</span><br><span class="line">    <span class="keyword">if</span>(arr==<span class="literal">null</span>||arr.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> i=<span class="number">0</span>;i&lt;arr.length-<span class="number">1</span>;i++)&#123;</span><br><span class="line">        <span class="keyword">for</span>(<span class="type">int</span> j=i;j&gt;=<span class="number">0</span>;j--)&#123;</span><br><span class="line">            <span class="keyword">if</span>(arr[j+<span class="number">1</span>]&lt;arr[j])&#123;</span><br><span class="line">                swap(arr,j,j+<span class="number">1</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><button type="button" class="tab-to-top" aria-label="scroll to top"><i class="fas fa-arrow-up"></i></button></div><div class="tab-item-content" id="插入排序-2"><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><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="keyword">public</span> <span class="keyword">class</span> <span class="title class_">SelectSort</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_">swap</span><span class="params">(<span class="type">int</span>[] arr, <span class="type">int</span> i, <span class="type">int</span> j)</span> &#123;</span><br><span class="line">        <span class="type">int</span> <span class="variable">temp</span> <span class="operator">=</span> arr[i];</span><br><span class="line">        arr[i] = arr[j];</span><br><span class="line">        arr[j] = temp;</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>[] randomArray(<span class="type">int</span> n)&#123;</span><br><span class="line">        <span class="type">int</span>[] array = <span class="keyword">new</span> <span class="title class_">int</span>[n];</span><br><span class="line">        <span class="keyword">for</span>(<span class="type">int</span> i=<span class="number">0</span>;i&lt;n;i++)&#123;</span><br><span class="line">            array[i]=(<span class="type">int</span>)(Math.random()*<span class="number">100</span>);</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">return</span> array;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title function_">insertionSort</span><span class="params">(<span class="type">int</span>[] arr)</span>&#123;</span><br><span class="line">        <span class="keyword">if</span>(arr==<span class="literal">null</span>||arr.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> i=<span class="number">0</span>;i&lt;arr.length-<span class="number">1</span>;i++)&#123;</span><br><span class="line">            <span class="keyword">for</span>(<span class="type">int</span> j=i;j&gt;=<span class="number">0</span>;j--)&#123;</span><br><span class="line">                <span class="keyword">if</span>(arr[j+<span class="number">1</span>]&lt;arr[j])&#123;</span><br><span class="line">                    swap(arr,j,j+<span class="number">1</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="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>[] arr = randomArray(<span class="number">10</span>);</span><br><span class="line">        insertionSort(arr);</span><br><span class="line">        <span class="keyword">for</span>(<span class="type">int</span> i=<span class="number">0</span>;i&lt;arr.length;i++)&#123;</span><br><span class="line">            System.out.print(arr[i]+<span class="string">&quot; &quot;</span>);</span><br><span class="line">        &#125;</span><br><span class="line"></span><br><span class="line"></span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><button type="button" class="tab-to-top" aria-label="scroll to top"><i class="fas fa-arrow-up"></i></button></div></div></div><h2 id="三种排序总结"><a href="#三种排序总结" class="headerlink" title="三种排序总结"></a>三种排序总结</h2><p><strong>选择排序：</strong></p><ul><li><strong>基本思想</strong>：每一趟从无序区选择最小（或最大）元素，放到有序区的末尾。</li><li><strong>时间复杂度</strong>：<ul><li>最好：O(n²)</li><li>最坏：O(n²)</li><li>平均：O(n²)</li></ul></li><li><strong>空间复杂度</strong>：O(1)</li><li><strong>稳定性</strong>：不稳定（可能改变相等元素的相对顺序）</li><li><strong>特点</strong>：<ul><li>交换次数少（最多 n-1 次交换），但比较次数多；</li><li>不管数据有序与否，时间复杂度都一样；</li><li>简单直观，但性能比插入排序稍差。</li></ul></li></ul><p><strong>冒泡排序：</strong></p><ul><li><strong>基本思想</strong>：通过相邻元素比较并交换，把较大（或较小）的元素逐步“冒泡”到序列的一端。</li><li><strong>时间复杂度</strong>：<ul><li>最好：O(n)（序列本身有序，只需一次冒泡即可）</li><li>最坏：O(n²)</li><li>平均：O(n²)</li></ul></li><li><strong>空间复杂度</strong>：O(1)</li><li><strong>稳定性</strong>：稳定（相邻元素相等时不交换顺序）</li><li><strong>特点</strong>：<ul><li>思想直观，常用于入门讲解；</li><li>效率较低，数据量大时不推荐。</li></ul></li></ul><p><strong>插入排序：</strong></p><ul><li><strong>基本思想</strong>：把序列分为有序区和无序区，每次将无序区的第一个元素插入到有序区合适的位置。</li><li><strong>时间复杂度</strong>：<ul><li>最好：O(n)（原本接近有序时）</li><li>最坏：O(n²)</li><li>平均：O(n²)</li></ul></li><li><strong>空间复杂度</strong>：O(1)（原地排序）</li><li><strong>稳定性</strong>：稳定（相等元素不会交换顺序）</li><li><strong>特点</strong>：<ul><li>简单，适合数据量小或基本有序的情况；</li><li>插入过程像整理扑克牌。</li></ul></li></ul><h1 id="二分搜索"><a href="#二分搜索" class="headerlink" title="二分搜索"></a>二分搜索</h1><ol><li>在有序数组中确定num存在还是不存在</li><li>在有序数组中找&gt;=num的最左位置</li><li>在有序数组中找&lt;=num的最右位置</li><li>二分搜索不一定发生在有序数组上（比如寻找峰值问题）</li></ol><h1 id="链表入门题"><a href="#链表入门题" class="headerlink" title="链表入门题"></a>链表入门题</h1><h2 id="单双链表及其反转"><a href="#单双链表及其反转" class="headerlink" title="单双链表及其反转"></a>单双链表及其反转</h2><h3 id="按值传递，按引用传递"><a href="#按值传递，按引用传递" class="headerlink" title="按值传递，按引用传递"></a>按值传递，按引用传递</h3><ul><li>一般基本数据类型默认是<code>按值传递</code>，函数会获得参数的副本，修改副本不会修改原始值</li><li>引用数据类型默认为<code>按引用传递</code>，在函数中的操作会直接影响原始值</li><li>C和C++可以通过传递指针来使函数在修改副本的同时也修改原始值</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><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="keyword">public</span> <span class="keyword">class</span> <span class="title class_">Main</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">// int、long、byte、short</span></span><br><span class="line">        <span class="comment">// char、float、double、boolean</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="comment">//在f(int)函数中修改a，但a并没有变化</span></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">        f(a);</span><br><span class="line">        System.out.println(a);</span><br><span class="line"></span><br><span class="line">        <span class="comment">// 其他类型按引用传递</span></span><br><span class="line">        <span class="comment">// 比如下面的Number是自定义的类</span></span><br><span class="line"></span><br><span class="line"><span class="comment">//        在内存的堆空间申请了一片区域，哪个区域保存了5</span></span><br><span class="line"><span class="comment">//        同时在栈空间新建了一个变量b,可以通过b找到堆空间的5</span></span><br><span class="line">        <span class="type">Number</span> <span class="variable">b</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">Number</span>(<span class="number">5</span>);</span><br><span class="line"><span class="comment">//        这里看似是b被设为空,实则是拷贝了一个b&#x27;进入函数，使b&#x27;也指向堆中的空间</span></span><br><span class="line"><span class="comment">//        所以函数内也只是将b&#x27;指向的位置改为空，对实际的b无影响</span></span><br><span class="line">        g1(b);</span><br><span class="line">        System.out.println(b.val);</span><br><span class="line"><span class="comment">//        这里g2将b的值改为6</span></span><br><span class="line"><span class="comment">//        实际上操作的是b&#x27;，将b&#x27;指向的地方的value改为了6，但b和b&#x27;指向了同一个地方</span></span><br><span class="line"><span class="comment">//        所以b的val也会变成6</span></span><br><span class="line">        g2(b);</span><br><span class="line">        System.out.println(b.val);</span><br><span class="line"></span><br><span class="line">        <span class="comment">// 比如下面的一维数组</span></span><br><span class="line">        <span class="type">int</span>[] c = &#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">        g3(c);</span><br><span class="line">        System.out.println(c[<span class="number">0</span>]);</span><br><span class="line">        g4(c);</span><br><span class="line">        System.out.println(c[<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_">f</span><span class="params">(<span class="type">int</span> a)</span> &#123;</span><br><span class="line">        a = <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">class</span> <span class="title class_">Number</span> &#123;</span><br><span class="line">        <span class="keyword">public</span> <span class="type">int</span> val;</span><br><span class="line"></span><br><span class="line">        <span class="keyword">public</span> <span class="title function_">Number</span><span class="params">(<span class="type">int</span> v)</span> &#123;</span><br><span class="line">            val = v;</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_">g1</span><span class="params">(Number b)</span> &#123;</span><br><span class="line">        b = <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> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title function_">g2</span><span class="params">(Number b)</span> &#123;</span><br><span class="line">        b.val = <span class="number">6</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_">g3</span><span class="params">(<span class="type">int</span>[] c)</span> &#123;</span><br><span class="line">        c = <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> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title function_">g4</span><span class="params">(<span class="type">int</span>[] c)</span> &#123;</span><br><span class="line">        c[<span class="number">0</span>] = <span class="number">100</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> <span class="keyword">class</span> <span class="title class_">ListNode</span> &#123;</span><br><span class="line">        <span class="keyword">public</span> <span class="type">int</span> val;</span><br><span class="line">        <span class="keyword">public</span> ListNode next;</span><br><span class="line"></span><br><span class="line">        <span class="keyword">public</span> <span class="title function_">ListNode</span><span class="params">(<span class="type">int</span> val)</span> &#123;</span><br><span class="line">            <span class="built_in">this</span>.val = val;</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_">ListNode</span><span class="params">(<span class="type">int</span> val, ListNode next)</span> &#123;</span><br><span class="line">            <span class="built_in">this</span>.val = val;</span><br><span class="line">            <span class="built_in">this</span>.next = next;</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h3 id="实现链表反转"><a href="#实现链表反转" class="headerlink" title="实现链表反转"></a>实现链表反转</h3><ul><li>假设一个链表的指向为: 11-&gt;22-&gt;33-&gt;44-&gt;55</li><li>反转后效果则为:       55-&gt;44-&gt;33-&gt;22-&gt;11</li><li>构造函数原型<ul><li>首先函数必须有一个参数，这个参数就是链表的头结点</li><li>其次，函数还应当有返回值，返回值就是反转后的链表头结点</li><li>我们可以使用堆栈的方式来实现链表反转</li></ul></li><li>题目链接：<a href="https://leetcode.cn/problems/reverse-linked-list/">https://leetcode.cn/problems/reverse-linked-list/</a></li><li><strong><em>反转链表实现：</em></strong><ul><li>从头结点开始，先将当前节点的下一个结点的位置用一个变量暂存起来(因为等会修改下一个节点的位置，不存储起来会导致链表断连)</li><li>然后修改当前节点的下一个结点为前一个结点(即使是单链表也可以，因为前一个节点是每次实现反转后，将当前结点存在前一个结点，存完后再向后移动链表)</li><li>让前一个结点等于当前结点(这就是上一步说的更新节点，链表反转后并后移以后，当前结点就是前结点)</li><li>将当前结点后移，进行下一轮循环</li><li>循环结束后，返回的是pre节点，这是反转后链表的头结点，不是head因为此时在循环的最后一步，head会移动到链表末尾，指向NULL</li></ul></li></ul><div class="tabs" id="单链表反转"><ul class="nav-tabs"><li class="tab active"><button type="button" data-href="#单链表反转-1">单链表反转</button></li><li class="tab"><button type="button" data-href="#单链表反转-2">双链表反转</button></li></ul><div class="tab-contents"><div class="tab-item-content active" id="单链表反转-1"><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><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">static</span> <span class="keyword">class</span> <span class="title class_">ListNode</span> &#123;</span><br><span class="line">        <span class="keyword">public</span> <span class="type">int</span> val;</span><br><span class="line">        <span class="keyword">public</span> ListNode next;</span><br><span class="line"></span><br><span class="line">        <span class="keyword">public</span> <span class="title function_">ListNode</span><span class="params">(<span class="type">int</span> val)</span> &#123;</span><br><span class="line">            <span class="built_in">this</span>.val = val;</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_">ListNode</span><span class="params">(<span class="type">int</span> val, ListNode next)</span> &#123;</span><br><span class="line">            <span class="built_in">this</span>.val = val;</span><br><span class="line">            <span class="built_in">this</span>.next = next;</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_">Solution</span> &#123;</span><br><span class="line"></span><br><span class="line">        <span class="keyword">public</span> <span class="keyword">static</span> ListNode <span class="title function_">reverseList</span><span class="params">(ListNode head)</span> &#123;</span><br><span class="line">            <span class="type">ListNode</span> <span class="variable">pre</span> <span class="operator">=</span> <span class="literal">null</span>;</span><br><span class="line">            <span class="type">ListNode</span> <span class="variable">next</span> <span class="operator">=</span> <span class="literal">null</span>;</span><br><span class="line">            <span class="keyword">while</span> (head != <span class="literal">null</span>) &#123;</span><br><span class="line">                next = head.next;</span><br><span class="line">                head.next = pre;</span><br><span class="line">                pre = head;</span><br><span class="line">                head = next;</span><br><span class="line">            &#125;</span><br><span class="line">            <span class="keyword">return</span> pre;</span><br><span class="line">        &#125;</span><br><span class="line"></span><br><span class="line">    &#125;</span><br></pre></td></tr></table></figure><button type="button" class="tab-to-top" aria-label="scroll to top"><i class="fas fa-arrow-up"></i></button></div><div class="tab-item-content" id="单链表反转-2"><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><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">static</span> <span class="keyword">class</span> <span class="title class_">DoubleListNode</span> &#123;</span><br><span class="line">    <span class="keyword">public</span> <span class="type">int</span> value;</span><br><span class="line">    <span class="keyword">public</span> DoubleListNode last;</span><br><span class="line">    <span class="keyword">public</span> DoubleListNode next;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">public</span> <span class="title function_">DoubleListNode</span><span class="params">(<span class="type">int</span> v)</span> &#123;</span><br><span class="line">        value = v;</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="keyword">public</span> <span class="keyword">static</span> DoubleListNode <span class="title function_">reverseDoubleList</span><span class="params">(DoubleListNode head)</span> &#123;</span><br><span class="line">    <span class="type">DoubleListNode</span> <span class="variable">pre</span> <span class="operator">=</span> <span class="literal">null</span>;</span><br><span class="line">    <span class="type">DoubleListNode</span> <span class="variable">next</span> <span class="operator">=</span> <span class="literal">null</span>;</span><br><span class="line">    <span class="keyword">while</span> (head != <span class="literal">null</span>) &#123;</span><br><span class="line">        next = head.next;</span><br><span class="line">        head.next = pre;</span><br><span class="line">        head.last = next;</span><br><span class="line">        pre = head;</span><br><span class="line">        head = next;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">return</span> pre;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><button type="button" class="tab-to-top" aria-label="scroll to top"><i class="fas fa-arrow-up"></i></button></div></div></div><h2 id="合并两个有序链表"><a href="#合并两个有序链表" class="headerlink" title="合并两个有序链表"></a>合并两个有序链表</h2><ul><li>要求：将两个升序链表合并为一个新的升序链表并返回。新链表是通过拼接给定的两个链表的所有节点组成的。</li></ul><div class="img-wrap"><div class="img-bg"><img class="img" src="https://raw.githubusercontent.com/silvan2077/markdown_pic/main/Picgo/20250911165520.png"/></div></div><ul><li><p><strong><em>合并链表实现：</em></strong></p><ul><li>先判断两个有序链表谁的头结点更小，将头结点小的结点作为合并后的链表的头结点。</li><li>分别定义两个指针cur1和cur2，一个连接在头结点小的那个结点的下一个结点上，一个连接在头结点较大的那个结点上，假设此题选第一个链表的1作为头结点，那么cur1将指向2，cur2将指向第二个链表的1，并设置pre作为前驱结点，pre指向第一步比较中较小的那个结点</li><li>定义循环，循环的结束条件是有一条链表的元素全部遍历完</li><li>在循环中，不断比较cur1和cur2的值，将较小的值连接到新的链表上，并向后移动移动对应指针，更新pre的值为当前较小的结点，并继续循环</li><li>当循环结束时，直接将另一条有元素未比较的链表的剩余元素链接到新的链表上，因为链表是有序的，所以直接链接即可</li><li>返回新链表头结点</li></ul></li><li><p><a href="https://leetcode.cn/problems/merge-two-sorted-lists/">测试连接</a></p></li><li><div class="tabs" id="合并链表"><ul class="nav-tabs"><li class="tab active"><button type="button" data-href="#合并链表-1">代码</button></li><li class="tab"><button type="button" data-href="#合并链表-2">leetcode测试代码</button></li></ul><div class="tab-contents"><div class="tab-item-content active" id="合并链表-1"><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><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="keyword">class</span> <span class="title class_">Solution</span> &#123;</span><br><span class="line"></span><br><span class="line">        <span class="keyword">public</span> <span class="keyword">static</span> ListNode <span class="title function_">mergeTwoLists</span><span class="params">(ListNode head1, ListNode head2)</span> &#123;</span><br><span class="line">            <span class="comment">//一方为空，则无需合并直接返回另一个链表即可</span></span><br><span class="line">            <span class="keyword">if</span> (head1 == <span class="literal">null</span> || head2 == <span class="literal">null</span>) &#123;</span><br><span class="line">                <span class="keyword">return</span> head1 == <span class="literal">null</span> ? head2 : head1;</span><br><span class="line">            &#125;</span><br><span class="line">            <span class="comment">//判断谁的头结点小谁做新生成的链表的头(在前面确定好head后，在中间连接过程head没有任何变化，最后作为返回值返回)</span></span><br><span class="line">            <span class="type">ListNode</span> <span class="variable">head</span> <span class="operator">=</span> head1.val &lt;= head2.val ? head1 : head2;</span><br><span class="line">            <span class="comment">//cur1连接头结点后一个结点（）</span></span><br><span class="line">            <span class="type">ListNode</span> <span class="variable">cur1</span> <span class="operator">=</span> head.next;</span><br><span class="line">            <span class="comment">//cur2连接未做头结点的那个结点的后一个结点</span></span><br><span class="line">            <span class="type">ListNode</span> <span class="variable">cur2</span> <span class="operator">=</span> head == head1 ? head2 : head1;</span><br><span class="line"><span class="comment">//            pre保存当前结点的前驱，cur1更新后继</span></span><br><span class="line">            <span class="type">ListNode</span> <span class="variable">pre</span> <span class="operator">=</span> head;</span><br><span class="line"><span class="comment">//            当两链表都不为空就不断比较谁小，谁作为下一个结点</span></span><br><span class="line">            <span class="keyword">while</span> (cur1 != <span class="literal">null</span> &amp;&amp; cur2 != <span class="literal">null</span>) &#123;</span><br><span class="line">                <span class="keyword">if</span> (cur1.val &lt;= cur2.val) &#123;</span><br><span class="line">                    pre.next = cur1;</span><br><span class="line">                    cur1 = cur1.next;</span><br><span class="line">                &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">                    pre.next = cur2;</span><br><span class="line">                    cur2 = cur2.next;</span><br><span class="line">                &#125;</span><br><span class="line">                pre = pre.next;</span><br><span class="line">            &#125;</span><br><span class="line"><span class="comment">//            有一个链表全部比较完了，此时未比较完的链表剩余元素一定大于比较完的链表的每一个元素，故直接将未比较的剩余链表元素直接连到刚刚排序好的新链表</span></span><br><span class="line">            pre.next = cur1 != <span class="literal">null</span> ? cur1 : cur2;</span><br><span class="line">            <span class="keyword">return</span> head;</span><br><span class="line">        &#125;</span><br><span class="line"></span><br><span class="line">    &#125;</span><br></pre></td></tr></table></figure><button type="button" class="tab-to-top" aria-label="scroll to top"><i class="fas fa-arrow-up"></i></button></div><div class="tab-item-content" id="合并链表-2"><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><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"> * Definition for singly-linked list.</span></span><br><span class="line"><span class="comment"> * public class ListNode &#123;</span></span><br><span class="line"><span class="comment"> *     int val;</span></span><br><span class="line"><span class="comment"> *     ListNode next;</span></span><br><span class="line"><span class="comment"> *     ListNode() &#123;&#125;</span></span><br><span class="line"><span class="comment"> *     ListNode(int val) &#123; this.val = val; &#125;</span></span><br><span class="line"><span class="comment"> *     ListNode(int val, ListNode next) &#123; this.val = val; this.next = next; &#125;</span></span><br><span class="line"><span class="comment"> * &#125;</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="keyword">class</span> <span class="title class_">Solution</span> &#123;</span><br><span class="line">    <span class="keyword">public</span> ListNode <span class="title function_">mergeTwoLists</span><span class="params">(ListNode list1, ListNode list2)</span> &#123;</span><br><span class="line">        <span class="keyword">if</span>(list1==<span class="literal">null</span>||list2==<span class="literal">null</span>)</span><br><span class="line">            <span class="keyword">return</span> list1==<span class="literal">null</span>?list2:list1;</span><br><span class="line">        <span class="type">ListNode</span> <span class="variable">head</span> <span class="operator">=</span> list1.val&gt;list2.val ? list2:list1;</span><br><span class="line">        ListNode cur1=head.next;</span><br><span class="line">        ListNode cur2= head==list1?list2:list1;</span><br><span class="line">        ListNode pre=head;</span><br><span class="line">        <span class="keyword">while</span>(cur1!=<span class="literal">null</span>&amp;&amp;cur2!=<span class="literal">null</span>)&#123;</span><br><span class="line">            <span class="keyword">if</span>(cur1.val&lt;cur2.val)&#123;</span><br><span class="line">                pre.next=cur1;</span><br><span class="line">                cur1=cur1.next;</span><br><span class="line">            &#125;</span><br><span class="line">            <span class="keyword">else</span>&#123;</span><br><span class="line">                pre.next=cur2;</span><br><span class="line">                cur2=cur2.next;</span><br><span class="line">            &#125;</span><br><span class="line">            pre=pre.next;</span><br><span class="line">        &#125;</span><br><span class="line">        pre.next=cur1==<span class="literal">null</span>?cur2:cur1;</span><br><span class="line">        <span class="keyword">return</span> head;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><button type="button" class="tab-to-top" aria-label="scroll to top"><i class="fas fa-arrow-up"></i></button></div></div></div><h2 id="两个链表相加"><a href="#两个链表相加" class="headerlink" title="两个链表相加"></a>两个链表相加</h2></li><li>实际上就是利用链表实现<code>高精度算法</code>的加法</li><li>它们每位数字都是按照 <code>逆序</code> 的方式存储的，并且每个节点只能存储一位数字</li><li>题目链接：<a href="https://leetcode.cn/problems/add-two-numbers/"></a></li></ul><ul><li><strong><em>链表相加实现：</em></strong><ul><li>创建一个ans结点，该结点只用来作为结果链表的头结点返回</li><li>创建一个cur结点，该结点来记录当前操作位置，每次计算更新位置</li><li>创建一个进位变量carry，记录当前位相加的结果是否超过10，超过则进位</li><li>定义循环，每次循环移动两条链表到下一位，链表的结束条件为两条链表都为空</li><li>在循环中，直接计算两个链表数值相加的结果，并分别记录下结果的个位和十位</li><li>判断是否为第一次计算，如果是第一次计算，则将cur指向ans结点，如果不是则将cur指向下一个结点</li><li>不断循环，直到两条链表都为空</li><li>最后跳出循环后，如果carry为1，则再创建一个数值为1的结点，并添加到链表末尾</li></ul></li></ul><div class="tabs" id="链表相加"><ul class="nav-tabs"><li class="tab active"><button type="button" data-href="#链表相加-1">代码</button></li><li class="tab"><button type="button" data-href="#链表相加-2">提交代码</button></li></ul><div class="tab-contents"><div class="tab-item-content active" id="链表相加-1"><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><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="keyword">public</span> <span class="keyword">class</span> <span class="title class_">Main</span>&#123;</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">class</span> <span class="title class_">ListNode</span> &#123;</span><br><span class="line">        <span class="keyword">public</span> <span class="type">int</span> val;</span><br><span class="line">        <span class="keyword">public</span> ListNode next;</span><br><span class="line"></span><br><span class="line">        <span class="keyword">public</span> <span class="title function_">ListNode</span><span class="params">(<span class="type">int</span> val)</span> &#123;</span><br><span class="line">            <span class="built_in">this</span>.val = val;</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_">ListNode</span><span class="params">(<span class="type">int</span> val, ListNode next)</span> &#123;</span><br><span class="line">            <span class="built_in">this</span>.val = val;</span><br><span class="line">            <span class="built_in">this</span>.next = next;</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">static</span> <span class="keyword">class</span> <span class="title class_">Solution</span> &#123;</span><br><span class="line">        <span class="keyword">public</span> <span class="keyword">static</span> ListNode <span class="title function_">addTwoNumbers</span><span class="params">(ListNode h1, ListNode h2)</span> &#123;</span><br><span class="line"><span class="comment">//            ans作为结果链表的头结点，第一次计算初始化后便不再改变，cur为记录当前操作的位置，每次计算更新</span></span><br><span class="line">            <span class="type">ListNode</span> <span class="variable">ans</span> <span class="operator">=</span> <span class="literal">null</span>, cur = <span class="literal">null</span>;</span><br><span class="line">            <span class="comment">//carry表示当前位是否进位</span></span><br><span class="line">            <span class="type">int</span> <span class="variable">carry</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> sum, val; <span class="comment">// 声明变量</span></span><br><span class="line">                 h1 != <span class="literal">null</span> || h2 != <span class="literal">null</span>; <span class="comment">// 终止条件，需要两条链表都为空，结果才为false，跳出循环</span></span><br><span class="line">                 <span class="comment">//移动指针到下一位（为空时保持 null）</span></span><br><span class="line">                 h1 = h1 == <span class="literal">null</span> ? <span class="literal">null</span> : h1.next, <span class="comment">// 每一步h1的跳转</span></span><br><span class="line">                         h2 = h2 == <span class="literal">null</span> ? <span class="literal">null</span> : h2.next <span class="comment">// 每一步h2的跳转</span></span><br><span class="line">            ) &#123;</span><br><span class="line">                <span class="comment">//sum来暂时存储每一位相加的结果</span></span><br><span class="line">                sum = (h1 == <span class="literal">null</span> ? <span class="number">0</span> : h1.val)</span><br><span class="line">                        + (h2 == <span class="literal">null</span> ? <span class="number">0</span> : h2.val)</span><br><span class="line">                        + carry;</span><br><span class="line"></span><br><span class="line">                val = sum % <span class="number">10</span>;</span><br><span class="line">                carry = sum / <span class="number">10</span>;</span><br><span class="line">                <span class="keyword">if</span> (ans == <span class="literal">null</span>) &#123;</span><br><span class="line">                    ans = <span class="keyword">new</span> <span class="title class_">ListNode</span>(val);</span><br><span class="line">                    cur = ans;</span><br><span class="line">                &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">                    cur.next = <span class="keyword">new</span> <span class="title class_">ListNode</span>(val);</span><br><span class="line">                    cur = cur.next;</span><br><span class="line">                &#125;</span><br><span class="line">            &#125;</span><br><span class="line">            <span class="keyword">if</span> (carry == <span class="number">1</span>) &#123;</span><br><span class="line">                cur.next = <span class="keyword">new</span> <span class="title class_">ListNode</span>(<span class="number">1</span>);</span><br><span class="line">            &#125;</span><br><span class="line">            <span class="keyword">return</span> ans;</span><br><span class="line">        &#125;</span><br><span class="line"></span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><button type="button" class="tab-to-top" aria-label="scroll to top"><i class="fas fa-arrow-up"></i></button></div><div class="tab-item-content" id="链表相加-2"><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><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"> * Definition for singly-linked list.</span></span><br><span class="line"><span class="comment"> * public class ListNode &#123;</span></span><br><span class="line"><span class="comment"> *     int val;</span></span><br><span class="line"><span class="comment"> *     ListNode next;</span></span><br><span class="line"><span class="comment"> *     ListNode() &#123;&#125;</span></span><br><span class="line"><span class="comment"> *     ListNode(int val) &#123; this.val = val; &#125;</span></span><br><span class="line"><span class="comment"> *     ListNode(int val, ListNode next) &#123; this.val = val; this.next = next; &#125;</span></span><br><span class="line"><span class="comment"> * &#125;</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="keyword">class</span> <span class="title class_">Solution</span> &#123;</span><br><span class="line">    <span class="keyword">public</span> ListNode <span class="title function_">addTwoNumbers</span><span class="params">(ListNode l1, ListNode l2)</span> &#123;</span><br><span class="line">        ListNode ans=<span class="literal">null</span>;</span><br><span class="line">        ListNode cur=<span class="literal">null</span>;</span><br><span class="line">        <span class="type">int</span> carry=<span class="number">0</span>;</span><br><span class="line">        <span class="keyword">for</span>(<span class="type">int</span> sum,val;</span><br><span class="line">            l1!=<span class="literal">null</span>||l2!=<span class="literal">null</span>;</span><br><span class="line">            l1=l1==<span class="literal">null</span>?<span class="literal">null</span>:l1.next,</span><br><span class="line">            l2=l2==<span class="literal">null</span>?<span class="literal">null</span>:l2.next)&#123;</span><br><span class="line">                sum=(l1==<span class="literal">null</span>?<span class="number">0</span>:l1.val)+(l2==<span class="literal">null</span>?<span class="number">0</span>:l2.val)+carry;</span><br><span class="line">                val=sum%<span class="number">10</span>;</span><br><span class="line">                carry=sum/<span class="number">10</span>;</span><br><span class="line">                <span class="keyword">if</span>(ans==<span class="literal">null</span>)&#123;</span><br><span class="line">                    ans= <span class="keyword">new</span> <span class="title class_">ListNode</span>(val);</span><br><span class="line">                    cur=ans;</span><br><span class="line">                &#125;</span><br><span class="line">                <span class="keyword">else</span>&#123;</span><br><span class="line">                    cur.next=<span class="keyword">new</span> <span class="title class_">ListNode</span>(val);</span><br><span class="line">                    cur=cur.next;</span><br><span class="line">                &#125;</span><br><span class="line">            &#125;</span><br><span class="line">            <span class="keyword">if</span>(carry!=<span class="number">0</span>)&#123;</span><br><span class="line">                cur.next=<span class="keyword">new</span> <span class="title class_">ListNode</span>(carry);</span><br><span class="line">            &#125;</span><br><span class="line">            <span class="keyword">return</span> ans;</span><br><span class="line">    &#125;</span><br><span class="line">    </span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><button type="button" class="tab-to-top" aria-label="scroll to top"><i class="fas fa-arrow-up"></i></button></div></div></div><h2 id="划分链表"><a href="#划分链表" class="headerlink" title="划分链表"></a>划分链表</h2><ul><li>题目要求：将链表分为两个部分，前一部分小于x，后一部分大于等于x，且划分后不改变链表的相对次序</li><li><p>题目链接：<a href="https://leetcode.cn/problems/partition-list/">https://leetcode.cn/problems/partition-list/</a></p></li><li><p><strong><em>划分链表实现：</em></strong></p><ul><li>首先定义四个结点，分别表示小于x的链表头结点，小于x的链表尾结点，大于等于x的链表头结点，大于等于x的链表尾结点，以及当前遍历的结点的下一个结点的位置</li><li>遍历链表，首先先更新next结点为下一个结点</li><li>然后判断当前结点的数值是否小于x，如果是，则将当前结点插入到小于x的链表末尾，并更新小于x的链表尾结点为当前结点</li><li>如果当前结点的数值大于等于x，则将当前结点插入到大于等于x的链表末尾，并更新大于等于x的链表尾结点为当前结点</li><li>如果这是第一个元素还需要记录该链表的头为当前结点</li><li>更新当前链表到下一个结点</li><li>最后遍历结束，判断小于x的链表是否有值，</li><li>若没有则直接返回大于x的链表的头指针，若有值则将大于等于x的链表头结点连接到小于x的链表尾结点的next位置</li><li>返回小于x的链表头结点</li></ul></li></ul><div class="tabs" id="划分代码"><ul class="nav-tabs"><li class="tab active"><button type="button" data-href="#划分代码-1">代码</button></li><li class="tab"><button type="button" data-href="#划分代码-2">提交代码</button></li></ul><div class="tab-contents"><div class="tab-item-content active" id="划分代码-1"><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><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="keyword">public</span> <span class="keyword">class</span> <span class="title class_">Main</span> &#123;</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">class</span> <span class="title class_">ListNode</span> &#123;</span><br><span class="line">        <span class="keyword">public</span> <span class="type">int</span> val;</span><br><span class="line">        <span class="keyword">public</span> ListNode next;</span><br><span class="line"></span><br><span class="line">        <span class="keyword">public</span> <span class="title function_">ListNode</span><span class="params">(<span class="type">int</span> val)</span> &#123;</span><br><span class="line">            <span class="built_in">this</span>.val = val;</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_">ListNode</span><span class="params">(<span class="type">int</span> val, ListNode next)</span> &#123;</span><br><span class="line">            <span class="built_in">this</span>.val = val;</span><br><span class="line">            <span class="built_in">this</span>.next = next;</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">static</span> <span class="keyword">class</span> <span class="title class_">Solution</span> &#123;</span><br><span class="line">        <span class="keyword">public</span> <span class="keyword">static</span> ListNode <span class="title function_">partition</span><span class="params">(ListNode head, <span class="type">int</span> x)</span> &#123;</span><br><span class="line">            <span class="comment">//&lt; x的区域的链表的头和尾</span></span><br><span class="line">            <span class="type">ListNode</span> <span class="variable">leftHead</span> <span class="operator">=</span> <span class="literal">null</span>, leftTail = <span class="literal">null</span>;</span><br><span class="line">            <span class="comment">//&gt;= x的区域的链表的头和尾</span></span><br><span class="line">            <span class="type">ListNode</span> <span class="variable">rightHead</span> <span class="operator">=</span> <span class="literal">null</span>, rightTail = <span class="literal">null</span>;</span><br><span class="line">            <span class="comment">// 用来存储下一个结点的位置，防止链表断连</span></span><br><span class="line">            <span class="type">ListNode</span> <span class="variable">next</span> <span class="operator">=</span> <span class="literal">null</span>;</span><br><span class="line">            <span class="keyword">while</span> (head != <span class="literal">null</span>) &#123;</span><br><span class="line">                <span class="comment">//更新next的位置</span></span><br><span class="line">                next = head.next;</span><br><span class="line">                <span class="comment">//断连，重新划分该结点属于哪个链表,必须要要，否则可能有成环的情况</span></span><br><span class="line">                head.next = <span class="literal">null</span>;</span><br><span class="line">                <span class="comment">//小于的区域</span></span><br><span class="line">                <span class="keyword">if</span> (head.val &lt; x) &#123;</span><br><span class="line">                    <span class="keyword">if</span> (leftHead == <span class="literal">null</span>) &#123;</span><br><span class="line">                        leftHead = head;</span><br><span class="line">                    &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">                        leftTail.next = head;</span><br><span class="line">                    &#125;</span><br><span class="line">                    leftTail = head;</span><br><span class="line">                &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">                    <span class="keyword">if</span> (rightHead == <span class="literal">null</span>) &#123;</span><br><span class="line">                        rightHead = head;</span><br><span class="line">                    &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">                        rightTail.next = head;</span><br><span class="line">                    &#125;</span><br><span class="line">                    rightTail = head;</span><br><span class="line">                &#125;</span><br><span class="line">                <span class="comment">//更新到原链表的下一个结点</span></span><br><span class="line">                head = next;</span><br><span class="line">            &#125;</span><br><span class="line">            <span class="keyword">if</span> (leftHead == <span class="literal">null</span>) &#123;</span><br><span class="line">                <span class="keyword">return</span> rightHead;</span><br><span class="line">            &#125;</span><br><span class="line">            <span class="comment">// &lt; x的区域有内容，将大于区域的头接到小于区域的尾部</span></span><br><span class="line">            leftTail.next = rightHead;</span><br><span class="line">            <span class="keyword">return</span> leftHead;</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><button type="button" class="tab-to-top" aria-label="scroll to top"><i class="fas fa-arrow-up"></i></button></div><div class="tab-item-content" id="划分代码-2"><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><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"> * Definition for singly-linked list.</span></span><br><span class="line"><span class="comment"> * public class ListNode &#123;</span></span><br><span class="line"><span class="comment"> *     int val;</span></span><br><span class="line"><span class="comment"> *     ListNode next;</span></span><br><span class="line"><span class="comment"> *     ListNode() &#123;&#125;</span></span><br><span class="line"><span class="comment"> *     ListNode(int val) &#123; this.val = val; &#125;</span></span><br><span class="line"><span class="comment"> *     ListNode(int val, ListNode next) &#123; this.val = val; this.next = next; &#125;</span></span><br><span class="line"><span class="comment"> * &#125;</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="keyword">class</span> <span class="title class_">Solution</span> &#123;</span><br><span class="line">    <span class="keyword">public</span> ListNode <span class="title function_">partition</span><span class="params">(ListNode head, <span class="type">int</span> x)</span> &#123;</span><br><span class="line">        ListNode leftHead=<span class="literal">null</span>,leftTail=<span class="literal">null</span>,rightHead=<span class="literal">null</span>,rightTail=<span class="literal">null</span>;</span><br><span class="line">        ListNode next=<span class="literal">null</span>;</span><br><span class="line">       </span><br><span class="line">        <span class="keyword">while</span>(head!=<span class="literal">null</span>)&#123;</span><br><span class="line">            next=head.next;</span><br><span class="line">            head.next=<span class="literal">null</span>;</span><br><span class="line">            <span class="keyword">if</span>(head.val&lt;x)&#123;</span><br><span class="line">                <span class="keyword">if</span>(leftHead==<span class="literal">null</span>)</span><br><span class="line">                    leftHead=head;</span><br><span class="line">                <span class="keyword">else</span></span><br><span class="line">                    leftTail.next=head;</span><br><span class="line">                leftTail=head;</span><br><span class="line">            &#125;</span><br><span class="line">            <span class="keyword">else</span>&#123;</span><br><span class="line">                <span class="keyword">if</span>(rightHead==<span class="literal">null</span>)</span><br><span class="line">                    rightHead=head;</span><br><span class="line">                <span class="keyword">else</span></span><br><span class="line">                    rightTail.next=head;</span><br><span class="line">                rightTail=head;</span><br><span class="line">            &#125;</span><br><span class="line">            head=next;</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">if</span>(leftHead==<span class="literal">null</span>)</span><br><span class="line">            <span class="keyword">return</span> rightHead;</span><br><span class="line">        leftTail.next=rightHead;</span><br><span class="line">        <span class="keyword">return</span> leftHead;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><button type="button" class="tab-to-top" aria-label="scroll to top"><i class="fas fa-arrow-up"></i></button></div></div></div><h1 id="队列和栈"><a href="#队列和栈" class="headerlink" title="队列和栈"></a>队列和栈</h1><ul><li>队列：先进先出</li><li>栈：先进后出<h2 id="使用链表和数组实现"><a href="#使用链表和数组实现" class="headerlink" title="使用链表和数组实现"></a>使用链表和数组实现</h2></li><li><p>使用链表实现队列：</p><ul><li>这里直接使用Java内部提供的双端链表实现的队列<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><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="keyword">public</span> <span class="keyword">static</span> <span class="keyword">class</span> <span class="title class_">Queue1</span> &#123;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// java中的双向链表LinkedList</span></span><br><span class="line">    <span class="comment">// 单向链表就足够了</span></span><br><span class="line">    <span class="keyword">public</span> Queue&lt;Integer&gt; queue = <span class="keyword">new</span> <span class="title class_">LinkedList</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">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> queue.isEmpty();</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 向队列中加入num，加到尾巴</span></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">offer</span><span class="params">(<span class="type">int</span> num)</span> &#123;</span><br><span class="line">        queue.offer(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="type">int</span> <span class="title function_">poll</span><span class="params">()</span> &#123;</span><br><span class="line">        <span class="keyword">return</span> queue.poll();</span><br><span 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="type">int</span> <span class="title function_">peek</span><span class="params">()</span> &#123;</span><br><span class="line">        <span class="keyword">return</span> queue.peek();</span><br><span 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="type">int</span> <span class="title function_">size</span><span class="params">()</span> &#123;</span><br><span class="line">        <span class="keyword">return</span> queue.size();</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></li></ul></li><li><p>使用数组实现队列：</p><ul><li>前提是该队列最大长度是固定的，即数组的长度是固定的</li><li>不要求队列的头部必须待在数组头部，可以待在数组的任意位置</li></ul></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><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">public</span> <span class="keyword">static</span> <span class="keyword">class</span> <span class="title class_">Queue2</span> &#123;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">public</span> <span class="type">int</span>[] queue;</span><br><span class="line">    <span class="keyword">public</span> <span class="type">int</span> l;</span><br><span class="line">    <span class="keyword">public</span> <span class="type">int</span> r;</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_">Queue2</span><span class="params">(<span class="type">int</span> n)</span> &#123;</span><br><span class="line">        queue = <span class="keyword">new</span> <span class="title class_">int</span>[n];</span><br><span class="line">        l = <span class="number">0</span>;</span><br><span class="line">        r = <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">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> l == r;</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_">offer</span><span class="params">(<span class="type">int</span> num)</span> &#123;</span><br><span class="line">        queue[r++] = num;</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_">poll</span><span class="params">()</span> &#123;</span><br><span class="line">        <span class="keyword">return</span> queue[l++];</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="comment">// ?</span></span><br><span class="line">    <span class="comment">// l...r-1 r</span></span><br><span class="line">    <span class="comment">// [l..r)</span></span><br><span class="line">    <span class="keyword">public</span> <span class="type">int</span> <span class="title function_">head</span><span class="params">()</span> &#123;</span><br><span class="line">        <span class="keyword">return</span> queue[l];</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_">tail</span><span class="params">()</span> &#123;</span><br><span class="line">        <span class="keyword">return</span> queue[r - <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> <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> r - l;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><ul><li>使用数组实现栈:<ul><li>链表实现的栈Java内部已经提供了，不过常数时间不太好</li><li>前提依旧是最大长度固定</li></ul></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="keyword">public</span> <span class="keyword">static</span> <span class="keyword">class</span> <span class="title class_">Stack2</span> &#123;</span><br><span class="line"></span><br><span class="line">        <span class="keyword">public</span> <span class="type">int</span>[] stack;</span><br><span class="line">        <span class="keyword">public</span> <span class="type">int</span> size;</span><br><span class="line"></span><br><span class="line">        <span class="comment">// 同时在栈里的元素个数不会超过n</span></span><br><span class="line">        <span class="keyword">public</span> <span class="title function_">Stack2</span><span class="params">(<span class="type">int</span> n)</span> &#123;</span><br><span class="line">            stack = <span class="keyword">new</span> <span class="title class_">int</span>[n];</span><br><span class="line">            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="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="keyword">public</span> <span class="keyword">void</span> <span class="title function_">push</span><span class="params">(<span class="type">int</span> num)</span> &#123;</span><br><span class="line">            stack[size++] = num;</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_">pop</span><span class="params">()</span> &#123;</span><br><span class="line">            <span class="keyword">return</span> stack[--size];</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_">peek</span><span class="params">()</span> &#123;</span><br><span class="line">            <span class="keyword">return</span> stack[size - <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> <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">    &#125;</span><br></pre></td></tr></table></figure><ul><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><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">class</span> <span class="title class_">MyCircularQueue</span> &#123;</span><br><span class="line"></span><br><span class="line">        <span class="keyword">public</span> <span class="type">int</span>[] queue;</span><br><span class="line"></span><br><span class="line">        <span class="keyword">public</span> <span class="type">int</span> l, r, size, limit;</span><br><span class="line"></span><br><span class="line">        <span class="comment">// 同时在队列里的数字个数，不要超过k</span></span><br><span class="line">        <span class="keyword">public</span> <span class="title function_">MyCircularQueue</span><span class="params">(<span class="type">int</span> k)</span> &#123;</span><br><span class="line">            queue = <span class="keyword">new</span> <span class="title class_">int</span>[k];</span><br><span class="line">            l = r = size = <span class="number">0</span>;</span><br><span class="line">            limit = k;</span><br><span class="line">        &#125;</span><br><span class="line"></span><br><span class="line">        <span class="comment">// 如果队列满了，什么也不做，返回false</span></span><br><span class="line">        <span class="comment">// 如果队列没满，加入value，返回true</span></span><br><span class="line">        <span class="keyword">public</span> <span class="type">boolean</span> <span class="title function_">enQueue</span><span class="params">(<span class="type">int</span> value)</span> &#123;</span><br><span class="line">            <span class="keyword">if</span> (isFull()) &#123;</span><br><span class="line">                <span class="keyword">return</span> <span class="literal">false</span>;</span><br><span class="line">            &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">                queue[r] = value;</span><br><span class="line">                <span class="comment">// r++, 结束了，跳回0</span></span><br><span class="line">                r = r == limit - <span class="number">1</span> ? <span class="number">0</span> : (r + <span class="number">1</span>);</span><br><span class="line">                size++;</span><br><span class="line">                <span class="keyword">return</span> <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">// 如果队列空了，什么也不做，返回false</span></span><br><span class="line">        <span class="comment">// 如果队列没空，弹出头部的数字，返回true</span></span><br><span class="line">        <span class="keyword">public</span> <span class="type">boolean</span> <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">return</span> <span class="literal">false</span>;</span><br><span class="line">            &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">                <span class="comment">// l++, 结束了，跳回0</span></span><br><span class="line">                l = l == limit - <span class="number">1</span> ? <span class="number">0</span> : (l + <span class="number">1</span>);</span><br><span class="line">                size--;</span><br><span class="line">                <span class="keyword">return</span> <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">// 返回队列头部的数字（不弹出），如果没有数返回-1</span></span><br><span class="line">        <span class="keyword">public</span> <span class="type">int</span> <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">return</span> -<span class="number">1</span>;</span><br><span class="line">            &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">                <span class="keyword">return</span> queue[l];</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_">Rear</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">return</span> -<span class="number">1</span>;</span><br><span class="line">            &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">                <span class="type">int</span> <span class="variable">last</span> <span class="operator">=</span> r == <span class="number">0</span> ? (limit - <span class="number">1</span>) : (r - <span class="number">1</span>);</span><br><span class="line">                <span class="keyword">return</span> queue[last];</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">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="keyword">public</span> <span class="type">boolean</span> <span class="title function_">isFull</span><span class="params">()</span> &#123;</span><br><span class="line">            <span class="keyword">return</span> size == limit;</span><br><span class="line">        &#125;</span><br><span class="line"></span><br><span class="line">    &#125;</span><br></pre></td></tr></table></figure><h2 id="栈和队列相互实现"><a href="#栈和队列相互实现" class="headerlink" title="栈和队列相互实现"></a>栈和队列相互实现</h2><h3 id="使用栈实现队列"><a href="#使用栈实现队列" class="headerlink" title="使用栈实现队列"></a>使用栈实现队列</h3><ul><li>仅仅需要两个栈即可实现，一个栈叫做in，一个栈叫做out，in栈用来接收数据，out栈用来输出数据，倒数据时严格遵循两点要求<ul><li>out栈为空时，才能倒数据到out栈</li><li>一旦倒数据，in栈必须一下倒完，不能有数据留在in栈中</li></ul></li><li>测试链接 : <a href="https://leetcode.cn/problems/implement-queue-using-stacks/">https://leetcode.cn/problems/implement-queue-using-stacks/</a></li><li><strong><em>实现流程：</em></strong><ul><li>先对两个栈进行初始化，使用泛型时，要记住使用引用数据类型</li><li>接着编写in栈倒数据到out栈的方法，这个方法十分重要，应当实现当out栈为空时，将in栈的数据倒入out栈中，当out栈不为空则不进行操作</li><li>编写入队的push方法时，直接将数据push到in栈中即可，另外要在push方法后调用inToOut()方法，在out栈为空的时候，将in栈的数据倒到out栈中，防止数据一直堆积在in栈中</li><li>编写pop方法时，要先调用inToOut()方法，确保out栈中没数据的时候，及时从in栈中获取，然后返回out栈的栈顶元素</li><li>编写peek方法时，要先调用inToOut()方法，确保out栈中没数据的时候，及时从in栈中获取，然后返回out栈的栈顶元素</li><li>编写empty方法时，即可以判断in栈和out栈是否都为空，也可以先调用inToOut()方法，再判断out栈是否为空</li></ul></li></ul><div class="tabs" id="栈实现队列"><ul class="nav-tabs"><li class="tab active"><button type="button" data-href="#栈实现队列-1">实现代码</button></li><li class="tab"><button type="button" data-href="#栈实现队列-2">测试代码</button></li></ul><div class="tab-contents"><div class="tab-item-content active" id="栈实现队列-1"><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><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">class</span> <span class="title class_">MyQueue</span> &#123;</span><br><span class="line"></span><br><span class="line">        <span class="keyword">public</span> Stack&lt;Integer&gt; in;</span><br><span class="line">        <span class="keyword">public</span> Stack&lt;Integer&gt; out;</span><br><span class="line"></span><br><span class="line">        <span class="keyword">public</span> <span class="title function_">MyQueue</span><span class="params">()</span> &#123;</span><br><span class="line">            in = <span class="keyword">new</span> <span class="title class_">Stack</span>&lt;Integer&gt;();</span><br><span class="line">            out = <span class="keyword">new</span> <span class="title class_">Stack</span>&lt;Integer&gt;();</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_">inToOut</span><span class="params">()</span> &#123;</span><br><span class="line">            <span class="comment">// 只有out栈为空时，才能倒数据</span></span><br><span class="line">            <span class="keyword">if</span> (out.empty()) &#123;</span><br><span class="line">                <span class="keyword">while</span> (!in.empty()) &#123;</span><br><span class="line">                    out.push(in.pop());</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">public</span> <span class="keyword">void</span> <span class="title function_">push</span><span class="params">(<span class="type">int</span> x)</span> &#123;</span><br><span class="line">            <span class="comment">// 当out栈为空时，则将in栈的数据全倒到out栈中</span></span><br><span class="line">            <span class="comment">// 当out栈不为空时，则先存在in栈中</span></span><br><span class="line">            in.push(x);</span><br><span class="line">            inToOut();</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_">pop</span><span class="params">()</span> &#123;</span><br><span class="line">            inToOut();</span><br><span class="line">            <span class="keyword">return</span> out.pop();</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_">peek</span><span class="params">()</span> &#123;</span><br><span class="line">            inToOut();</span><br><span class="line">            <span class="keyword">return</span> out.peek();</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_">empty</span><span class="params">()</span> &#123;</span><br><span class="line">            <span class="keyword">return</span> in.isEmpty() &amp;&amp; out.isEmpty();</span><br><span class="line">        &#125;</span><br><span class="line"></span><br><span class="line">    &#125;</span><br></pre></td></tr></table></figure><button type="button" class="tab-to-top" aria-label="scroll to top"><i class="fas fa-arrow-up"></i></button></div><div class="tab-item-content" id="栈实现队列-2"><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><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">class</span> <span class="title class_">MyQueue</span> &#123;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">public</span> Stack&lt;Integer&gt; in;</span><br><span class="line">    <span class="keyword">public</span> Stack&lt;Integer&gt; out;</span><br><span class="line"></span><br><span class="line"></span><br><span class="line">    <span class="keyword">public</span> <span class="title function_">MyQueue</span><span class="params">()</span> &#123;</span><br><span class="line">        in= <span class="keyword">new</span> <span class="title class_">Stack</span>&lt;Integer&gt;();</span><br><span class="line">        out= <span class="keyword">new</span> <span class="title class_">Stack</span>&lt;Integer&gt;();</span><br><span class="line">        </span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">//  将in栈的数据全部倒到out栈中</span></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">inToOut</span><span class="params">()</span>&#123;</span><br><span class="line">        <span class="keyword">if</span>(out.empty())&#123;</span><br><span class="line">            <span class="keyword">while</span>(!in.empty())&#123;</span><br><span class="line">                out.push(in.pop());</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">public</span> <span class="keyword">void</span> <span class="title function_">push</span><span class="params">(<span class="type">int</span> x)</span> &#123;</span><br><span class="line"></span><br><span class="line">        in.push(x);</span><br><span class="line"><span class="comment">//  如果out栈还有数据就不执行，如果没有数据则推in栈的数据到out栈</span></span><br><span class="line">        inToOut();</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_">pop</span><span class="params">()</span> &#123;</span><br><span class="line">        inToOut();</span><br><span class="line">        <span class="keyword">return</span> out.pop();</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_">peek</span><span class="params">()</span> &#123;</span><br><span class="line">        inToOut();</span><br><span class="line">        <span class="keyword">return</span> out.peek();</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_">empty</span><span class="params">()</span> &#123;</span><br><span class="line">        inToOut();</span><br><span class="line">        <span class="keyword">return</span> out.empty();</span><br><span class="line">    &#125;</span><br><span class="line"></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="comment"> * Your MyQueue object will be instantiated and called as such:</span></span><br><span class="line"><span class="comment"> * MyQueue obj = new MyQueue();</span></span><br><span class="line"><span class="comment"> * obj.push(x);</span></span><br><span class="line"><span class="comment"> * int param_2 = obj.pop();</span></span><br><span class="line"><span class="comment"> * int param_3 = obj.peek();</span></span><br><span class="line"><span class="comment"> * boolean param_4 = obj.empty();</span></span><br><span class="line"><span class="comment"> */</span></span><br></pre></td></tr></table></figure><button type="button" class="tab-to-top" aria-label="scroll to top"><i class="fas fa-arrow-up"></i></button></div></div></div><h3 id="使用队列实现栈"><a href="#使用队列实现栈" class="headerlink" title="使用队列实现栈"></a>使用队列实现栈</h3><ul><li>使用队列实现栈，使用一个队列即可，实际主要变化的是入队操作，使其入队后在队列的次序和栈的次序一致，出队操作则保持栈的先进后出的次序</li><li>测试链接 : <a href="https://leetcode.cn/problems/implement-stack-using-queues/">https://leetcode.cn/problems/implement-stack-using-queues/</a></li><li>入队操作的时间复杂度为O(N),其他操作的时间复杂度均为O(1)</li><li><strong><em>实现原理：</em></strong> 每当元素入队时，记录下当前队列长度，将队列现有数据全部出队，将新元素入队，再把原来的数据重新入队，这样就实现了入队时的栈先进后出的次序</li></ul><div class="tabs" id="队列实现栈"><ul class="nav-tabs"><li class="tab active"><button type="button" data-href="#队列实现栈-1">实现代码</button></li><li class="tab"><button type="button" data-href="#队列实现栈-2">测试代码</button></li></ul><div class="tab-contents"><div class="tab-item-content active" id="队列实现栈-1"><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><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_">MyStack</span> &#123;</span><br><span class="line"></span><br><span class="line">Queue&lt;Integer&gt; queue;</span><br><span class="line"></span><br><span class="line"><span class="keyword">public</span> <span class="title function_">MyStack</span><span class="params">()</span> &#123;</span><br><span class="line">queue = <span class="keyword">new</span> <span class="title class_">LinkedList</span>&lt;Integer&gt;();</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">// O(n)</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">(<span class="type">int</span> x)</span> &#123;</span><br><span class="line"><span class="type">int</span> <span class="variable">n</span> <span class="operator">=</span> queue.size();</span><br><span class="line">queue.offer(x);</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; n; i++) &#123;</span><br><span class="line">queue.offer(queue.poll());</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_">pop</span><span class="params">()</span> &#123;</span><br><span class="line"><span class="keyword">return</span> queue.poll();</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_">top</span><span class="params">()</span> &#123;</span><br><span class="line"><span class="keyword">return</span> queue.peek();</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_">empty</span><span class="params">()</span> &#123;</span><br><span class="line"><span class="keyword">return</span> queue.isEmpty();</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><button type="button" class="tab-to-top" aria-label="scroll to top"><i class="fas fa-arrow-up"></i></button></div><div class="tab-item-content" id="队列实现栈-2"><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><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">class</span> <span class="title class_">MyStack</span> &#123;</span><br><span class="line">    <span class="keyword">public</span> Queue&lt;Integer&gt; queue;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">public</span> <span class="title function_">MyStack</span><span class="params">()</span> &#123;</span><br><span class="line">        queue=<span class="keyword">new</span> <span class="title class_">LinkedList</span>&lt;Integer&gt;();</span><br><span class="line">        </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_">push</span><span class="params">(<span class="type">int</span> x)</span> &#123;</span><br><span class="line">        <span class="type">int</span> n=queue.size();</span><br><span class="line">        queue.offer(x);</span><br><span class="line">        <span class="keyword">for</span>(<span class="type">int</span> i=<span class="number">0</span>;i&lt;n;i++)&#123;</span><br><span class="line">            queue.offer(queue.poll());</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_">pop</span><span class="params">()</span> &#123;</span><br><span class="line">        <span class="keyword">return</span> queue.poll();        </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_">top</span><span class="params">()</span> &#123;</span><br><span class="line">        <span class="keyword">return</span> queue.peek();</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_">empty</span><span class="params">()</span> &#123;</span><br><span class="line">        <span class="keyword">return</span> queue.isEmpty();</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"> * Your MyStack object will be instantiated and called as such:</span></span><br><span class="line"><span class="comment"> * MyStack obj = new MyStack();</span></span><br><span class="line"><span class="comment"> * obj.push(x);</span></span><br><span class="line"><span class="comment"> * int param_2 = obj.pop();</span></span><br><span class="line"><span class="comment"> * int param_3 = obj.top();</span></span><br><span class="line"><span class="comment"> * boolean param_4 = obj.empty();</span></span><br><span class="line"><span class="comment"> */</span></span><br></pre></td></tr></table></figure><button type="button" class="tab-to-top" aria-label="scroll to top"><i class="fas fa-arrow-up"></i></button></div></div></div><h2 id="栈的入门题目——最小栈"><a href="#栈的入门题目——最小栈" class="headerlink" title="栈的入门题目——最小栈"></a>栈的入门题目——最小栈</h2><ul><li><ul><li>栈的入门题目，要求实现一个栈，并且实现一个getmin()方法，要求时间复杂度为O(1)，因此不能依靠遍历获得最小值</li></ul></li><li>测试链接 : <a href="https://leetcode.cn/problems/min-stack/">https://leetcode.cn/problems/min-stack/</a></li><li><strong><em>最小栈实现思路：</em></strong><ul><li>使用两个栈，一个栈用于存储数据，一个栈用于存储最小值</li><li>每次在数据栈中添加元素时，比较新添数据和最小栈的栈顶哪个更小，将更小的数据重新压入最小栈，一直保持数据栈和最小栈的数量一致</li><li>每次删除数据时，同步删除最小栈的栈顶数据</li><li>每次获取最小值时，直接返回最小栈的栈顶数据</li></ul></li><li>因为前面提到的内置栈的常数时间复杂度较慢，所以这里两种方式的栈都记录一下<div class="tabs" id="最小栈"><ul class="nav-tabs"><li class="tab active"><button type="button" data-href="#最小栈-1">测试代码——内置栈</button></li><li class="tab"><button type="button" data-href="#最小栈-2">测试代码——自定义数组栈</button></li></ul><div class="tab-contents"><div class="tab-item-content active" id="最小栈-1"><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><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">class</span> <span class="title class_">MinStack</span> &#123;</span><br><span class="line">    <span class="comment">// 初始化最小栈和数据栈</span></span><br><span class="line">    <span class="keyword">public</span> Stack&lt;Integer&gt; data;</span><br><span class="line">    <span class="keyword">public</span> Stack&lt;Integer&gt; min;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">public</span> <span class="title function_">MinStack</span><span class="params">()</span> &#123;</span><br><span class="line">        data = <span class="keyword">new</span> <span class="title class_">Stack</span>&lt;Integer&gt;();</span><br><span class="line">        min = <span class="keyword">new</span> <span class="title class_">Stack</span>&lt;Integer&gt;();       </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_">push</span><span class="params">(<span class="type">int</span> val)</span> &#123;</span><br><span class="line">        <span class="comment">// 先将数据压入数据栈中</span></span><br><span class="line">        data.push(val);</span><br><span class="line">        <span class="comment">// 如果最小栈为空或者新添的数据小于最小栈的栈顶数据，则将新添的数据重新压入最小栈</span></span><br><span class="line">        <span class="keyword">if</span>(min.isEmpty()||val&lt;min.peek())&#123;</span><br><span class="line">            min.push(val);</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="comment">// 否则，将最小栈的栈顶数据重新压入最小栈</span></span><br><span class="line">        <span class="keyword">else</span></span><br><span class="line">            min.push(min.peek());</span><br><span 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_">pop</span><span class="params">()</span> &#123;</span><br><span class="line">        data.pop();</span><br><span class="line">        min.pop();</span><br><span 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="type">int</span> <span class="title function_">top</span><span class="params">()</span> &#123;</span><br><span class="line">        <span class="keyword">return</span> data.peek();</span><br><span class="line">    &#125;</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_">getMin</span><span class="params">()</span> &#123;</span><br><span class="line">        <span class="keyword">return</span> min.peek();</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"> * Your MinStack object will be instantiated and called as such:</span></span><br><span class="line"><span class="comment"> * MinStack obj = new MinStack();</span></span><br><span class="line"><span class="comment"> * obj.push(val);</span></span><br><span class="line"><span class="comment"> * obj.pop();</span></span><br><span class="line"><span class="comment"> * int param_3 = obj.top();</span></span><br><span class="line"><span class="comment"> * int param_4 = obj.getMin();</span></span><br><span class="line"><span class="comment"> */</span></span><br></pre></td></tr></table></figure><button type="button" class="tab-to-top" aria-label="scroll to top"><i class="fas fa-arrow-up"></i></button></div><div class="tab-item-content" id="最小栈-2"><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><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">class</span> <span class="title class_">MinStack</span> &#123;</span><br><span class="line">    <span class="comment">// 定义一个比较大的数，如果测试题目的测试数据又变大则增大这个最大值即可</span></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">final</span> <span class="type">int</span> Max=<span class="number">10001</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>[] data;</span><br><span class="line">    <span class="keyword">public</span> <span class="type">int</span>[] min;</span><br><span class="line"><span class="comment">// 记录当前的数据量</span></span><br><span class="line">    <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_">MinStack</span><span class="params">()</span>&#123;</span><br><span class="line">        data = <span class="keyword">new</span> <span class="title class_">int</span>[Max];</span><br><span class="line">        min = <span class="keyword">new</span> <span class="title class_">int</span>[Max];</span><br><span class="line">        size=<span class="number">0</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">public</span> <span class="keyword">void</span> <span class="title function_">push</span><span class="params">(<span class="type">int</span> val)</span> &#123;</span><br><span class="line">        data[size]=val;</span><br><span class="line">        <span class="keyword">if</span>(size==<span class="number">0</span>||val&lt;=min[size-<span class="number">1</span>])</span><br><span class="line">            min[size]=val;</span><br><span class="line">        <span class="keyword">else</span></span><br><span class="line">            min[size]=min[size-<span class="number">1</span>];</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="keyword">public</span> <span class="keyword">void</span> <span class="title function_">pop</span><span class="params">()</span> &#123;</span><br><span class="line">        size--;</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_">top</span><span class="params">()</span> &#123;</span><br><span class="line">        <span class="keyword">return</span> data[size-<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> <span class="type">int</span> <span class="title function_">getMin</span><span class="params">()</span> &#123;</span><br><span class="line">        <span class="keyword">return</span> min[size-<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="comment">/**</span></span><br><span class="line"><span class="comment"> * Your MinStack object will be instantiated and called as such:</span></span><br><span class="line"><span class="comment"> * MinStack obj = new MinStack();</span></span><br><span class="line"><span class="comment"> * obj.push(val);</span></span><br><span class="line"><span class="comment"> * obj.pop();</span></span><br><span class="line"><span class="comment"> * int param_3 = obj.top();</span></span><br><span class="line"><span class="comment"> * int param_4 = obj.getMin();</span></span><br><span class="line"><span class="comment"> */</span></span><br></pre></td></tr></table></figure><button type="button" class="tab-to-top" aria-label="scroll to top"><i class="fas fa-arrow-up"></i></button></div></div></div></li></ul><h2 id="双端队列"><a href="#双端队列" class="headerlink" title="双端队列"></a>双端队列</h2><ul><li>双端队列和名字一样，是一个队列，允许在两端进行插入和删除操作，即可以从头部尾部进，也可以从头部尾部出</li><li>用双链表实现只需要特殊化一下插入和删除的逻辑即可，这里主要记一下用固定数组实现双端队列</li><li>测试链接 : <a href="https://leetcode.cn/problems/design-circular-deque/">https://leetcode.cn/problems/design-circular-deque/</a></li></ul><h1 id="二叉树"><a href="#二叉树" class="headerlink" title="二叉树"></a>二叉树</h1><h2 id="二叉树的遍历"><a href="#二叉树的遍历" class="headerlink" title="二叉树的遍历"></a>二叉树的遍历</h2><ul><li>二叉树的遍历，分为前序、中序、后序、层序遍历，这里主要记录二叉树的三序遍历<ul><li>前序遍历：先遍历根节点，再遍历左子树，最后遍历右子树</li><li>中序遍历：先遍历左子树，再遍历根节点，最后遍历右子树</li><li>后序遍历：先遍历左子树，再遍历右子树，最后遍历根节点</li><li>层序遍历：按照层的顺序遍历，即先遍历根节点，再遍历左右子树，最后遍历左右子树的子节点</li></ul></li><li>二叉树的三序遍历有递归和非递归两种方式，任何一种递归的方式都可以转换为非递归方式，因此这里主要记录一下递归方式</li></ul><h3 id="递归遍历"><a href="#递归遍历" class="headerlink" title="递归遍历"></a>递归遍历</h3><ul><li><p>二叉树的遍历实现思路：</p><ul><li>创建一个函数，在函数中，如果当前节点为空则退出</li><li>接着先序遍历则优先打印当前结点的值，接着递归调用该函数，此时传入的值为当前节点的左子树，接着调用函数传入当前结点的右子树</li><li>中序后序的区别仅仅是打印和递归调用的顺序不同</li></ul></li><li><p>递归序：</p><ul><li>1-&gt;2-&gt;4-&gt;4-&gt;4-&gt;2-&gt;5-&gt;5-&gt;5-&gt;2-&gt;</li><li>1-&gt;3-&gt;6-&gt;6-&gt;6-&gt;3-&gt;7-&gt;7-&gt;7-&gt;3-&gt;</li><li>1</li></ul></li><li>递归序即为在递归过程中访问的顺序，不难发现，只要一个结点不为空结点，那么一定会访问三次，而三种遍历方式，控制的就是他是在哪次访问的时候输出当前结点的值</li></ul><div class="img-wrap"><div class="img-bg"><img class="img" src="https://raw.githubusercontent.com/silvan2077/markdown_pic/main/Picgo/20250913184222.png"/></div></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></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">test</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">class</span> <span class="title class_">TreeNode</span>&#123;</span><br><span class="line">        <span class="keyword">public</span> <span class="type">int</span> val;</span><br><span class="line">        <span class="keyword">public</span> TreeNode left;</span><br><span class="line">        <span class="keyword">public</span> TreeNode right;</span><br><span class="line"></span><br><span class="line">        <span class="keyword">public</span> <span class="title function_">TreeNode</span><span class="params">(<span class="type">int</span> x)</span>&#123;</span><br><span class="line">            <span class="built_in">this</span>.val = x;</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">    <span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title function_">preTraversal</span><span class="params">(TreeNode head)</span>&#123;</span><br><span class="line">        <span class="keyword">if</span>(head==<span class="literal">null</span>)&#123;</span><br><span class="line">            <span class="keyword">return</span>;</span><br><span class="line">        &#125;</span><br><span class="line">        System.out.print(head.val+<span class="string">&quot; &quot;</span>);</span><br><span class="line">        preTraversal(head.left);</span><br><span class="line">        preTraversal(head.right);</span><br><span class="line">    &#125;</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_">inTraversal</span><span class="params">(TreeNode head)</span>&#123;</span><br><span class="line">        <span class="keyword">if</span>(head==<span class="literal">null</span>)&#123;</span><br><span class="line">            <span class="keyword">return</span>;</span><br><span class="line">        &#125;</span><br><span class="line">        inTraversal(head.left);</span><br><span class="line">        System.out.print(head.val+<span class="string">&quot; &quot;</span>);</span><br><span class="line">        inTraversal(head.right);</span><br><span 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_">afterTraversal</span><span class="params">(TreeNode head)</span>&#123;</span><br><span class="line">        <span class="keyword">if</span>(head==<span class="literal">null</span>)&#123;</span><br><span class="line">            <span class="keyword">return</span>;</span><br><span class="line">        &#125;</span><br><span class="line">        afterTraversal(head.left);</span><br><span class="line">        afterTraversal(head.right);</span><br><span class="line">        System.out.print(head.val+<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">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">TreeNode</span> <span class="variable">head</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">TreeNode</span>(<span class="number">1</span>);</span><br><span class="line">        head.left = <span class="keyword">new</span> <span class="title class_">TreeNode</span>(<span class="number">2</span>);</span><br><span class="line">        head.right = <span class="keyword">new</span> <span class="title class_">TreeNode</span>(<span class="number">3</span>);</span><br><span class="line">        head.left.left = <span class="keyword">new</span> <span class="title class_">TreeNode</span>(<span class="number">4</span>);</span><br><span class="line">        head.left.right = <span class="keyword">new</span> <span class="title class_">TreeNode</span>(<span class="number">5</span>);</span><br><span class="line">        head.right.left = <span class="keyword">new</span> <span class="title class_">TreeNode</span>(<span class="number">6</span>);</span><br><span class="line">        head.right.right = <span class="keyword">new</span> <span class="title class_">TreeNode</span>(<span class="number">7</span>);</span><br><span class="line">        System.out.println(<span class="string">&quot;先序遍历结果： &quot;</span>);</span><br><span class="line">        preTraversal(head);</span><br><span class="line">        System.out.println();</span><br><span class="line"></span><br><span class="line">        System.out.println(<span class="string">&quot;中序遍历结果： &quot;</span>);</span><br><span class="line">        inTraversal(head);</span><br><span class="line">        System.out.println();</span><br><span class="line">        </span><br><span class="line">        System.out.println(<span class="string">&quot;后序遍历结果： &quot;</span>);</span><br><span class="line">        afterTraversal(head);</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><ul><li>用栈实现二叉树的先序遍历思路：</li><li>实现思路：<ul><li>判断当前节点是否为空，不为空时则将结点压入栈</li><li>接着循环遍历栈，循环条件为栈不为空，循环过程中先弹出一个结点，此时应当记录一下该结点</li><li>输出弹出的那个结点的值</li><li>因为栈是先进后出，所以应当先判断是否有右子树，有则将右子树入栈，无则判断是否有左子树，有则将左子树入栈，无则退出此次循环</li></ul></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></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title function_">preOrder</span><span class="params">(TreeNode head)</span>&#123;</span><br><span class="line"><span class="comment">//       为空就直接返回</span></span><br><span class="line">        <span class="keyword">if</span>(head==<span class="literal">null</span>)&#123;</span><br><span class="line">            <span class="keyword">return</span> ;</span><br><span class="line">        &#125;</span><br><span class="line"><span class="comment">//        将当前结点压入栈</span></span><br><span class="line">        Stack&lt;TreeNode&gt; stack = <span class="keyword">new</span> <span class="title class_">Stack</span>&lt;&gt;();</span><br><span class="line">        stack.push(head);</span><br><span class="line">        <span class="keyword">while</span>(!stack.isEmpty())&#123;</span><br><span class="line"><span class="comment">//            栈顶元素出栈，并记录下该结点</span></span><br><span class="line">            <span class="type">TreeNode</span> <span class="variable">node</span> <span class="operator">=</span> stack.pop();</span><br><span class="line">            System.out.print(node.val+<span class="string">&quot; &quot;</span>);</span><br><span class="line"><span class="comment">//            判断出栈的结点是否有右子树</span></span><br><span class="line">            <span class="keyword">if</span>(node.right!=<span class="literal">null</span>)&#123;</span><br><span class="line">                stack.push(node.right);</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>(node.left!=<span class="literal">null</span>)&#123;</span><br><span class="line">                stack.push(node.left);</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;</span><br><span class="line">        System.out.println();</span><br><span class="line">    &#125;</span><br></pre></td></tr></table></figure><h4 id="中序遍历"><a href="#中序遍历" class="headerlink" title="中序遍历"></a>中序遍历</h4><ul><li>用栈实现二叉树的中序遍历思路：<ul><li>将树的左边界依次进栈，直到左边界全部压入栈中</li><li>接着将栈里元素弹出，打印该结点并将弹出结点的右树重复执行上一个步骤直到栈为空</li><li>测试链接：<a href="https://leetcode.cn/problems/binary-tree-inorder-traversal/">https://leetcode.cn/problems/binary-tree-inorder-traversal/</a> <figure class="highlight lasso"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><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">public</span> static <span class="literal">void</span> inOrder(TreeNode head)&#123;</span><br><span class="line">        <span class="keyword">if</span>(head==<span class="built_in">null</span>)&#123;</span><br><span class="line">            <span class="keyword">return</span> ;</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="built_in">Stack</span>&lt;TreeNode&gt; <span class="built_in">stack</span> = <span class="literal">new</span> <span class="built_in">Stack</span>&lt;&gt;();</span><br><span class="line">        <span class="keyword">while</span>(!<span class="built_in">stack</span>.isEmpty()||head!=<span class="built_in">null</span>)&#123;</span><br><span class="line"><span class="comment">//            将当前结点的左侧子树全部压入栈，之后head为空，但栈不为空，再执行else方法</span></span><br><span class="line">            <span class="keyword">if</span>(head!=<span class="built_in">null</span>)&#123;</span><br><span class="line">                <span class="built_in">stack</span>.push(head);</span><br><span class="line">                head = head.left;</span><br><span class="line">            &#125;</span><br><span class="line">            <span class="keyword">else</span>&#123;</span><br><span class="line"><span class="comment">//                将栈中元素出栈并打印，且如果该结点有右子树的话使其再次执行前一个步骤</span></span><br><span class="line">                head = <span class="built_in">stack</span>.pop();</span><br><span class="line">                System.out.print(head.val+<span class="string">&quot; &quot;</span>);</span><br><span class="line">                head = head.right;</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;</span><br><span class="line">        System.out.println();</span><br><span class="line">    &#125;</span><br></pre></td></tr></table></figure></li></ul></li></ul><h4 id="后序遍历"><a href="#后序遍历" class="headerlink" title="后序遍历"></a>后序遍历</h4><ul><li>后续遍历有两种实现方法，一种是使用两个栈来实现，另一种是使用一个栈加上哨兵记录栈内结点来实现</li><li>非递归的后序遍历事实上和先序遍历一样，只是将左右子树顺序交换一下后再倒过来输出。先序是中-&gt;左-&gt;右，交换位置后是中-&gt;右-&gt;左，再倒过来输出就是后序:左-&gt;右-&gt;中</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></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title function_">postOrder</span><span class="params">(TreeNode head)</span>&#123;</span><br><span class="line">        <span class="keyword">if</span>(head==<span class="literal">null</span>)&#123;</span><br><span class="line">            <span class="keyword">return</span> ;</span><br><span class="line">        &#125;</span><br><span class="line">        Stack&lt;TreeNode&gt; in = <span class="keyword">new</span> <span class="title class_">Stack</span>&lt;&gt;();</span><br><span class="line">        Stack&lt;TreeNode&gt; out = <span class="keyword">new</span> <span class="title class_">Stack</span>&lt;&gt;();</span><br><span class="line">        in.push(head);</span><br><span class="line">        TreeNode node;</span><br><span class="line">        <span class="keyword">while</span>(!in.isEmpty())&#123;</span><br><span class="line">            node = in.pop();</span><br><span class="line">            out.push(node);</span><br><span class="line">            <span class="keyword">if</span>(node.left!=<span class="literal">null</span>)&#123;</span><br><span class="line">                in.push(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">                in.push(node.right);</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">while</span>(!out.isEmpty())&#123;</span><br><span class="line">            System.out.print(out.pop().val+<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></pre></td></tr></table></figure><h4 id="总结"><a href="#总结" class="headerlink" title="总结"></a>总结</h4><ul><li>递归方法遍历二叉树，前面分析了每个结点都会访问三次，所以时间复杂度是O(n),非递归方法每个结点也都会进栈和出栈，时间复杂度也是O(n)</li><li>不管是递归还是非递归，空间复杂度都是O(h)，此处h为<code>树的高度</code></li><li>两个栈实现后序遍历的方法，非常好写且容易理解但不推荐，因为他要一下收集全部的结点，最后逆序输出，额外的空间复杂度为O(N)，很浪费空间。</li></ul><div class="tabs" id="遍历二叉树"><ul class="nav-tabs"><li class="tab active"><button type="button" data-href="#遍历二叉树-1">代码</button></li><li class="tab"><button type="button" data-href="#遍历二叉树-2">输出</button></li></ul><div class="tab-contents"><div class="tab-item-content active" id="遍历二叉树-1"><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><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></pre></td><td class="code"><pre><span class="line"><span class="keyword">import</span> java.util.Stack;</span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">test</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">class</span> <span class="title class_">TreeNode</span>&#123;</span><br><span class="line">        <span class="keyword">public</span> <span class="type">int</span> val;</span><br><span class="line">        <span class="keyword">public</span> TreeNode left;</span><br><span class="line">        <span class="keyword">public</span> TreeNode right;</span><br><span class="line"></span><br><span class="line">        <span class="keyword">public</span> <span class="title function_">TreeNode</span><span class="params">(<span class="type">int</span> x)</span>&#123;</span><br><span class="line">            <span class="built_in">this</span>.val = x;</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">    <span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title function_">preTraversal</span><span class="params">(TreeNode head)</span>&#123;</span><br><span class="line">        <span class="keyword">if</span>(head==<span class="literal">null</span>)&#123;</span><br><span class="line">            <span class="keyword">return</span>;</span><br><span class="line">        &#125;</span><br><span class="line">        System.out.print(head.val+<span class="string">&quot; &quot;</span>);</span><br><span class="line">        preTraversal(head.left);</span><br><span class="line">        preTraversal(head.right);</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_">inTraversal</span><span class="params">(TreeNode head)</span>&#123;</span><br><span class="line">        <span class="keyword">if</span>(head==<span class="literal">null</span>)&#123;</span><br><span class="line">            <span class="keyword">return</span>;</span><br><span class="line">        &#125;</span><br><span class="line">        inTraversal(head.left);</span><br><span class="line">        System.out.print(head.val+<span class="string">&quot; &quot;</span>);</span><br><span class="line">        inTraversal(head.right);</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_">afterTraversal</span><span class="params">(TreeNode head)</span>&#123;</span><br><span class="line">        <span class="keyword">if</span>(head==<span class="literal">null</span>)&#123;</span><br><span class="line">            <span class="keyword">return</span>;</span><br><span class="line">        &#125;</span><br><span class="line">        afterTraversal(head.left);</span><br><span class="line">        afterTraversal(head.right);</span><br><span class="line">        System.out.print(head.val+<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">static</span> <span class="keyword">void</span> <span class="title function_">preOrder</span><span class="params">(TreeNode head)</span>&#123;</span><br><span class="line"><span class="comment">//       为空就直接返回</span></span><br><span class="line">        <span class="keyword">if</span>(head==<span class="literal">null</span>)&#123;</span><br><span class="line">            <span class="keyword">return</span> ;</span><br><span class="line">        &#125;</span><br><span class="line"><span class="comment">//        将当前结点压入栈</span></span><br><span class="line">        Stack&lt;TreeNode&gt; stack = <span class="keyword">new</span> <span class="title class_">Stack</span>&lt;&gt;();</span><br><span class="line">        stack.push(head);</span><br><span class="line">        <span class="keyword">while</span>(!stack.isEmpty())&#123;</span><br><span class="line"><span class="comment">//            栈顶元素出栈，并记录下该结点</span></span><br><span class="line">            <span class="type">TreeNode</span> <span class="variable">node</span> <span class="operator">=</span> stack.pop();</span><br><span class="line">            System.out.print(node.val+<span class="string">&quot; &quot;</span>);</span><br><span class="line"><span class="comment">//            判断出栈的结点是否有右子树</span></span><br><span class="line">            <span class="keyword">if</span>(node.right!=<span class="literal">null</span>)&#123;</span><br><span class="line">                stack.push(node.right);</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>(node.left!=<span class="literal">null</span>)&#123;</span><br><span class="line">                stack.push(node.left);</span><br><span class="line">            &#125;</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="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title function_">inOrder</span><span class="params">(TreeNode head)</span>&#123;</span><br><span class="line">        <span class="keyword">if</span>(head==<span class="literal">null</span>)&#123;</span><br><span class="line">            <span class="keyword">return</span> ;</span><br><span class="line">        &#125;</span><br><span class="line">        Stack&lt;TreeNode&gt; stack = <span class="keyword">new</span> <span class="title class_">Stack</span>&lt;&gt;();</span><br><span class="line">        <span class="keyword">while</span>(!stack.isEmpty()||head!=<span class="literal">null</span>)&#123;</span><br><span class="line"><span class="comment">//            将当前结点的左侧子树全部压入栈，之后head为空，但栈不为空，再执行else方法</span></span><br><span class="line">            <span class="keyword">if</span>(head!=<span class="literal">null</span>)&#123;</span><br><span class="line">                stack.push(head);</span><br><span class="line">                head = head.left;</span><br><span class="line">            &#125;</span><br><span class="line">            <span class="keyword">else</span>&#123;</span><br><span class="line"><span class="comment">//                将栈中元素出栈并打印，且如果该结点有右子树的话使其再次执行前一个步骤</span></span><br><span class="line">                head = stack.pop();</span><br><span class="line">                System.out.print(head.val+<span class="string">&quot; &quot;</span>);</span><br><span class="line">                head = head.right;</span><br><span class="line">            &#125;</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="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title function_">postOrder</span><span class="params">(TreeNode head)</span>&#123;</span><br><span class="line">        <span class="keyword">if</span>(head==<span class="literal">null</span>)&#123;</span><br><span class="line">            <span class="keyword">return</span> ;</span><br><span class="line">        &#125;</span><br><span class="line">        Stack&lt;TreeNode&gt; in = <span class="keyword">new</span> <span class="title class_">Stack</span>&lt;&gt;();</span><br><span class="line">        Stack&lt;TreeNode&gt; out = <span class="keyword">new</span> <span class="title class_">Stack</span>&lt;&gt;();</span><br><span class="line">        in.push(head);</span><br><span class="line">        TreeNode node;</span><br><span class="line">        <span class="keyword">while</span>(!in.isEmpty())&#123;</span><br><span class="line">            node = in.pop();</span><br><span class="line">            out.push(node);</span><br><span class="line">            <span class="keyword">if</span>(node.left!=<span class="literal">null</span>)&#123;</span><br><span class="line">                in.push(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">                in.push(node.right);</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">while</span>(!out.isEmpty())&#123;</span><br><span class="line">            System.out.print(out.pop().val+<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 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">TreeNode</span> <span class="variable">head</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">TreeNode</span>(<span class="number">1</span>);</span><br><span class="line">        head.left = <span class="keyword">new</span> <span class="title class_">TreeNode</span>(<span class="number">2</span>);</span><br><span class="line">        head.right = <span class="keyword">new</span> <span class="title class_">TreeNode</span>(<span class="number">3</span>);</span><br><span class="line">        head.left.left = <span class="keyword">new</span> <span class="title class_">TreeNode</span>(<span class="number">4</span>);</span><br><span class="line">        head.left.right = <span class="keyword">new</span> <span class="title class_">TreeNode</span>(<span class="number">5</span>);</span><br><span class="line">        head.right.left = <span class="keyword">new</span> <span class="title class_">TreeNode</span>(<span class="number">6</span>);</span><br><span class="line">        head.right.right = <span class="keyword">new</span> <span class="title class_">TreeNode</span>(<span class="number">7</span>);</span><br><span class="line">        System.out.println(<span class="string">&quot;递归先序遍历结果： &quot;</span>);</span><br><span class="line">        preTraversal(head);</span><br><span class="line">        System.out.println();</span><br><span class="line"></span><br><span class="line">        System.out.println(<span class="string">&quot;递归中序遍历结果： &quot;</span>);</span><br><span class="line">        inTraversal(head);</span><br><span class="line">        System.out.println();</span><br><span class="line"></span><br><span class="line">        System.out.println(<span class="string">&quot;递归后序遍历结果： &quot;</span>);</span><br><span class="line">        afterTraversal(head);</span><br><span class="line">        System.out.println();</span><br><span class="line"></span><br><span class="line">        System.out.println(<span class="string">&quot;非递归先序遍历结果： &quot;</span>);</span><br><span class="line">        preOrder(head);</span><br><span class="line">        System.out.println(<span class="string">&quot;非递归中序遍历结果： &quot;</span>);</span><br><span class="line">        inOrder(head);</span><br><span class="line">        System.out.println(<span class="string">&quot;非递归后序遍历结果： &quot;</span>);</span><br><span class="line">        postOrder(head);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><button type="button" class="tab-to-top" aria-label="scroll to top"><i class="fas fa-arrow-up"></i></button></div><div class="tab-item-content" id="遍历二叉树-2"><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><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><br><span class="line"><span class="number">1</span> <span class="number">2</span> <span class="number">4</span> <span class="number">5</span> <span class="number">3</span> <span class="number">6</span> <span class="number">7</span> </span><br><span class="line">中序遍历结果： </span><br><span class="line"><span class="number">4</span> <span class="number">2</span> <span class="number">5</span> <span class="number">1</span> <span class="number">6</span> <span class="number">3</span> <span class="number">7</span> </span><br><span class="line">后序遍历结果： </span><br><span class="line"><span class="number">4</span> <span class="number">5</span> <span class="number">2</span> <span class="number">6</span> <span class="number">7</span> <span class="number">3</span> <span class="number">1</span> </span><br><span class="line">非递归先序遍历结果： </span><br><span class="line"><span class="number">1</span> <span class="number">2</span> <span class="number">4</span> <span class="number">5</span> <span class="number">3</span> <span class="number">6</span> <span class="number">7</span> </span><br><span class="line">非递归中序遍历结果： </span><br><span class="line"><span class="number">4</span> <span class="number">2</span> <span class="number">5</span> <span class="number">1</span> <span class="number">6</span> <span class="number">3</span> <span class="number">7</span> </span><br><span class="line">非递归后序遍历结果： </span><br><span class="line"><span class="number">4</span> <span class="number">5</span> <span class="number">2</span> <span class="number">6</span> <span class="number">7</span> <span class="number">3</span> <span class="number">1</span> </span><br></pre></td></tr></table></figure><button type="button" class="tab-to-top" aria-label="scroll to top"><i class="fas fa-arrow-up"></i></button></div></div></div><h1 id="递归和master公式"><a href="#递归和master公式" class="headerlink" title="递归和master公式"></a>递归和master公式</h1><h2 id="递归"><a href="#递归" class="headerlink" title="递归"></a>递归</h2><ul><li>递归的底层是利用<code>系统栈</code>实现的，<strong>递归的实现原理</strong>：每次递归调用都会将当前调用的参数保存在系统栈中，当递归调用返回时，系统栈中的参数会依次出栈，并返回给调用者。</li><li><strong>任何的递归都可以用非递归实现</strong>，但非递归实现不一定比递归快，改为非递归的好处是不用系统栈空间，系统栈空间相比内存栈空间更宝贵。</li><li>递归该非递归的必要性：<ul><li>工程上几乎一定要改，除非确定数据量再大，递归也一定不深，例如归并排序，快速排序，线段树等应用</li><li>算法题中能通过就不用改。</li></ul></li></ul><h2 id="Master公式"><a href="#Master公式" class="headerlink" title="Master公式"></a>Master公式</h2><ul><li>所有<code>子问题规模相同</code>的递归才能用Master公式：T(n) = a*T(n/b) + O(N<sup>c</sup>)<ul><li>a：子问题的数量</li><li>n/b：每个子问题的规模</li><li>O(N<sup>c</sup>)：除了子问题调用外的时间复杂度</li></ul></li><li>如果log(b,a) &lt; c, 复杂度为O(N<sup>c</sup>)</li><li>如果log(b,a) &gt; c, 复杂度为O(n^log(b,a))</li><li>如果log(b,a) = c, 复杂度为O(n^c*log(n))</li></ul><h1 id="归并算法"><a href="#归并算法" class="headerlink" title="归并算法"></a>归并算法</h1><h2 id="归并排序"><a href="#归并排序" class="headerlink" title="归并排序"></a>归并排序</h2><ul><li>归并排序的核心过程是：做部分排好序、右部分排好序，利用<code>merge</code>过程让左右整体有序</li><li>merge过程：谁小拷贝谁，直到两边的数字全部拷贝完，最后将排好序的数组拷贝回原数组</li><li>归并排序有递归实现和非递归实现</li><li>时间复杂度为O(n*logn)</li><li>需要辅助数组，空间复杂度为O(n)</li><li><p>归并排序为什么速度更快？因为比较行为没有浪费</p></li><li><p><strong><em>归并思路：</em></strong></p><ul><li>定义两个指针，分别指向两个有序数组的起始位置</li><li>定义一个辅助数组，用于保存排序后的结果</li><li>判断两个指针指向的元素大小，将较小的元素拷贝到辅助数组中，并移动指针，直到某一个数组的指针移动到末尾</li><li>将剩余的元素拷贝到辅助数组中</li><li>将辅助数组拷贝回原数组<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><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">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title function_">merge</span><span class="params">(<span class="type">int</span> l, <span class="type">int</span> m, <span class="type">int</span> r)</span> &#123;</span><br><span class="line"><span class="comment">//        i记录当前操作了几个数</span></span><br><span class="line">        <span class="type">int</span> <span class="variable">i</span> <span class="operator">=</span> l;</span><br><span class="line"><span class="comment">//        a为指向左侧数组的指针</span></span><br><span class="line">        <span class="type">int</span> <span class="variable">a</span> <span class="operator">=</span> l;</span><br><span class="line"><span class="comment">//        b为指向右侧数组的指针</span></span><br><span class="line">        <span class="type">int</span> <span class="variable">b</span> <span class="operator">=</span> m + <span class="number">1</span>;</span><br><span class="line"><span class="comment">//        如果左侧数组和右侧数组都有数</span></span><br><span class="line">        <span class="keyword">while</span> (a &lt;= m &amp;&amp; b &lt;= r) &#123;</span><br><span class="line"><span class="comment">//            左右指针判断谁小拷贝谁，并单增</span></span><br><span class="line">            help[i++] = arr[a] &lt;= arr[b] ? arr[a++] : arr[b++];</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="comment">// 左侧指针、右侧指针，必有一个越界、另一个不越界，将不越界的那个继续添加到辅助数组中</span></span><br><span class="line">        <span class="keyword">while</span> (a &lt;= m) </span><br><span class="line">            help[i++] = arr[a++];</span><br><span class="line">        <span class="keyword">while</span> (b &lt;= r) </span><br><span class="line">            help[i++] = arr[b++];</span><br><span class="line">        <span class="keyword">for</span> (i = l; i &lt;= r; i++) </span><br><span class="line">            arr[i] = help[i];</span><br><span class="line">    &#125;</span><br></pre></td></tr></table></figure></li></ul></li></ul><h3 id="递归实现"><a href="#递归实现" class="headerlink" title="递归实现"></a>递归实现</h3><ul><li><strong><em>递归思路：</em></strong><ul><li>首先将数组定义在全局域中，方便递归调用</li><li>传入两个参数，分别为需要排序的数组的起始位置和结束位置</li><li>判断这两个参数是否相等，如果相等则直接返回，因为一个数天然有序</li><li>算出中间位置，将数组分为左右两个部分，分别对左右两个部分进行递归调用，进行排序</li><li>归并两个数组，使数组有序</li></ul></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></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title function_">mergeSort1</span><span class="params">(<span class="type">int</span> l, <span class="type">int</span> r)</span> &#123;</span><br><span class="line"><span class="comment">//        说明只有一个元素，天然有序</span></span><br><span class="line">        <span class="keyword">if</span> (l == r) &#123;</span><br><span class="line">            <span class="keyword">return</span>;</span><br><span class="line">        &#125;</span><br><span class="line"><span class="comment">//        找到数组的中间值</span></span><br><span class="line">        <span class="type">int</span> <span class="variable">m</span> <span class="operator">=</span> (l + r) / <span class="number">2</span>;</span><br><span class="line"><span class="comment">//        对左侧进行排序</span></span><br><span class="line">        mergeSort1(l, m);</span><br><span class="line"><span class="comment">//        对右侧进行排序</span></span><br><span class="line">        mergeSort1(m + <span class="number">1</span>, r);</span><br><span class="line"><span class="comment">//        合并左右两侧数组（实际排序的方法）</span></span><br><span class="line">        merge(l, m, r);</span><br><span class="line">    &#125;</span><br></pre></td></tr></table></figure><h3 id="非递归实现"><a href="#非递归实现" class="headerlink" title="非递归实现"></a>非递归实现</h3><ul><li><strong><em>非递归思路</em></strong><ul><li><strong>定数组长度</strong>:假设数组有 <code>n</code> 个元素。</li><li><strong>设置子数组的初始大小</strong>:从 <code>1</code> 开始（每次把相邻的两个“长度为1的数组”合并），然后依次翻倍：<ul><li>第一次合并长度为 <code>1</code> 的小段</li><li>第二次合并长度为 <code>2</code> 的小段</li><li>第三次合并长度为 <code>4</code> 的小段</li><li>…直到子数组大小 ≥ <code>n</code>，排序完成。</li></ul></li><li><strong>合并过程</strong><ul><li>每一轮从数组的起始位置开始，以当前子数组大小 <code>size</code> 为单位，把<strong>相邻的两段有序子数组</strong>进行合并。</li><li>合并时要小心边界：如果最后一段不足 <code>size</code>，就直接并到前一段；如果整个区间不足两段，保持不变。<details class="folding-tag" blue><summary> 举例： </summary>              <div class='content'>              <p>​    数组 <code>[5, 2, 8, 6, 3, 7, 4, 1]</code></p><p>​    第一轮（<code>size = 1</code>）：把 <code>[5]</code> 和 <code>[2]</code> 合并 → <code>[2, 5]</code>，然后 <code>[8]</code> 和 <code>[6]</code> → <code>[6, 8]</code>，依次类推。</p><p>​    第二轮（<code>size = 2</code>）：把 <code>[2, 5]</code> 和 <code>[6, 8]</code> 合并 → <code>[2, 5, 6, 8]</code>，再处理后面几段。</p><p>​    第三轮（<code>size = 4</code>）：继续合并更大的有序段。</p><p>​    最终得到完全有序的数组。</p>              </div>            </details><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><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">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title function_">mergeSort2</span><span class="params">()</span> &#123;</span><br><span class="line">        <span class="comment">// 一共发生O(logn)次</span></span><br><span class="line">        <span class="keyword">for</span> (<span class="type">int</span> l, m, r, step = <span class="number">1</span>; step &lt; n; step &lt;&lt;= <span class="number">1</span>) &#123;</span><br><span class="line">            <span class="comment">// 内部分组merge，时间复杂度O(n)</span></span><br><span class="line">            l = <span class="number">0</span>;</span><br><span class="line">            <span class="keyword">while</span> (l &lt; n) &#123;</span><br><span class="line">                m = l + step - <span class="number">1</span>;</span><br><span class="line">                <span class="comment">// 说明右侧数组不存在，直接跳出</span></span><br><span class="line">                <span class="keyword">if</span> (m + <span class="number">1</span> &gt;= n) &#123;</span><br><span class="line">                    <span class="keyword">break</span>;</span><br><span class="line">                &#125;</span><br><span class="line">                <span class="comment">// 当右侧数组不满够step时，将r设置为n-1，有多少排多少</span></span><br><span class="line">                r = Math.min(l + (step &lt;&lt; <span class="number">1</span>) - <span class="number">1</span>, n - <span class="number">1</span>);</span><br><span class="line">                merge(l, m, r);</span><br><span class="line">                l = r + <span class="number">1</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="总结-1"><a href="#总结-1" class="headerlink" title="总结"></a>总结</h3><div class="note warning no-icon flat"><ul><li>递归版归并排序的思路是<code>先拆分再合并</code>，</li><li>非递归实现则是反过来——<code>从最小的子数组开始逐步合并，直到整个数组有序</code>。</li></ul></div></li></ul></li></ul></li><li>递归实现：<ul><li>优点是代码简洁直观，可读性好</li><li>缺点是递归会消耗一定的栈空间，遇到特别大的数组时可能会超出栈空间限制</li></ul></li><li>非递归实现：<ul><li>优点是避免栈空间溢出的问题，不依赖系统调用栈，内存更可控</li><li>缺点是代码复杂度高，且逻辑不如递归直观，程序可读性更差</li></ul></li><li>选择建议：<ul><li>学习、普通场景、小规模排序 → 推荐 递归实现，简单直接。</li><li>工程实践、大规模数据、内存受限场景 → 推荐 非递归实现，更稳健。<h2 id="归并分治"><a href="#归并分治" class="headerlink" title="归并分治"></a>归并分治</h2></li></ul></li></ul><h1 id="随机快速排序"><a href="#随机快速排序" class="headerlink" title="随机快速排序"></a>随机快速排序</h1><ul><li>随机快排的核心思想是<code>递归分治</code>，非递归快排能够实现但不常见<ul><li>先把数组<code>划分</code>为两部分</li><li>再分别对这两部分递归排序</li><li>最终合并</li></ul></li></ul><div class="note waring no-icon flat"><p>要排序数组 <code>[5, 2, 8, 6, 3]</code>：</p><ol><li>随机选一个枢轴，比如随机选到 <code>6</code></li><li>划分：<ul><li>小于等于 6 的放左边 → <code>[5, 2, 3]</code></li><li>大于 6 的放右边 → <code>[8]</code></li><li>枢轴 6 在中间</li><li>得到 <code>[5, 2, 3] | 6 | [8]</code></li></ul></li><li>递归排序 <code>[5, 2, 3]</code> → 得到 <code>[2, 3, 5]</code></li></ol></div><ul><li>排序思路：<ul><li>在数组中随机选择一个下标，将下标对应的数记为标准x，将数组分为两部分，小于标准数，大于标准数</li><li>划分为两部分后，记录下这两部分的边界位置，小于x的便捷位置为first，大于x的边界位置为last</li><li>使用临时变量left和right分别记录下边界位置，此时left和right分别指向小于x的边界位置和大于x的边界位置，left和right之前的数均为x，left之前的数都小于x，right之后的数都大于x</li><li>递归调用 函数，对小于x的边界位置和大于x的边界位置进行排序</li></ul></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></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title function_">quickSort</span><span class="params">(<span class="type">int</span> l, <span class="type">int</span> r)</span> &#123;</span><br><span class="line">    <span class="comment">//此时数组不需要排序，直接返回</span></span><br><span class="line">    <span class="keyword">if</span> (l &gt;= r) &#123;</span><br><span class="line">        <span class="keyword">return</span>;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="comment">// 随机生成一个下标，并保存该下标的值</span></span><br><span class="line">    <span class="type">int</span> <span class="variable">x</span> <span class="operator">=</span> arr[l + (<span class="type">int</span>) (Math.random() * (r - l + <span class="number">1</span>))];</span><br><span class="line"><span class="comment">//        根据x值进行对两边分治</span></span><br><span class="line">    partition(l, r, x);</span><br><span class="line">    <span class="comment">// 为了防止底层的递归过程覆盖全局变量</span></span><br><span class="line">    <span class="comment">// 这里用临时变量记录first、last</span></span><br><span class="line">    <span class="type">int</span> <span class="variable">left</span> <span class="operator">=</span> first;</span><br><span class="line">    <span class="type">int</span> <span class="variable">right</span> <span class="operator">=</span> last;</span><br><span class="line"><span class="comment">//        不断对两部分进行细分、排序</span></span><br><span class="line">    quickSort(l, left - <span class="number">1</span>);</span><br><span class="line">    quickSort(right + <span class="number">1</span>, r);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><ul><li>划分思路：<ul><li>已知数组中一定有x这个数</li><li>设置两个边界，左边界为left，右边界为right，左边界作为小于x的边界值，右边界作为大于x的边界值</li><li>同时用变量i来记录当前操作的下标</li><li>设置循环，结束条件为i超出数组的边界</li><li>判断当前arr[i]与x的大小关系<ul><li>arr[i] &amp;lt x,则将arr[i]和arr[left]交换位置，left++，i++</li><li>arr[i] &amp;gt x,则将arr[i]和arr[right]交换位置，right—</li><li>arr[i] == x,则i++，继续判断下一个数</li></ul></li></ul></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></pre></td><td class="code"><pre><span class="line"><span class="comment">// 已知arr[l....r]范围上一定有x这个值</span></span><br><span class="line">    <span class="comment">// 划分数组 &lt;x放左边，==x放中间，&gt;x放右边</span></span><br><span class="line">    <span class="comment">// 把全局变量first, last，更新成==x区域的左右边界</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_">partition</span><span class="params">(<span class="type">int</span> l, <span class="type">int</span> r, <span class="type">int</span> x)</span> &#123;</span><br><span class="line">        first = l;</span><br><span class="line">        last = r;</span><br><span class="line">        <span class="type">int</span> <span class="variable">i</span> <span class="operator">=</span> l;</span><br><span class="line">        <span class="keyword">while</span> (i &lt;= last) &#123;</span><br><span class="line"><span class="comment">//            相等则直接跳过，判断下一个</span></span><br><span class="line">            <span class="keyword">if</span> (arr[i] == x) &#123;</span><br><span class="line">                i++;</span><br><span class="line">            &#125; <span class="keyword">else</span> <span class="keyword">if</span> (arr[i] &lt; x) &#123;</span><br><span class="line"><span class="comment">//                交换位置，并扩大左侧边界</span></span><br><span class="line">                swap(first++, i++);</span><br><span class="line">            &#125; <span class="keyword">else</span> &#123;</span><br><span class="line"><span class="comment">//                交换位置，并扩大右侧边界，因为交换后的值还没有判断他的大小，所以i不需要增加</span></span><br><span class="line">                swap(i, last--);</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>测试链接 :<a href="https://www.luogu.com.cn/problem/P1177"> https://www.luogu.com.cn/problem/P1177</a><br><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><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></pre></td><td class="code"><pre><span class="line"></span><br><span class="line"><span class="comment">// Main函数中是输入输出处理效率很高的写法</span></span><br><span class="line"><span class="keyword">import</span> java.io.BufferedReader;</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.InputStreamReader;</span><br><span class="line"><span class="keyword">import</span> java.io.OutputStreamWriter;</span><br><span class="line"><span class="keyword">import</span> java.io.PrintWriter;</span><br><span class="line"><span class="keyword">import</span> java.io.StreamTokenizer;</span><br><span class="line"></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">Main</span> &#123;</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="variable">MAXN</span> <span class="operator">=</span> <span class="number">100001</span>;</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>[] arr = <span class="keyword">new</span> <span class="title class_">int</span>[MAXN];</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> n;</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> IOException &#123;</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>(<span class="keyword">new</span> <span class="title class_">InputStreamReader</span>(System.in));</span><br><span class="line">        <span class="type">StreamTokenizer</span> <span class="variable">in</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">StreamTokenizer</span>(br);</span><br><span class="line">        <span class="type">PrintWriter</span> <span class="variable">out</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">PrintWriter</span>(<span class="keyword">new</span> <span class="title class_">OutputStreamWriter</span>(System.out));</span><br><span class="line">        in.nextToken();</span><br><span class="line">        n = (<span class="type">int</span>) in.nval;</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; n; i++) &#123;</span><br><span class="line">            in.nextToken();</span><br><span class="line">            arr[i] = (<span class="type">int</span>) in.nval;</span><br><span class="line">        &#125;</span><br><span class="line">        quickSort(<span class="number">0</span>, n - <span class="number">1</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; n - <span class="number">1</span>; i++) &#123;</span><br><span class="line">            out.print(arr[i] + <span class="string">&quot; &quot;</span>);</span><br><span class="line">        &#125;</span><br><span class="line">        out.println(arr[n - <span class="number">1</span>]);</span><br><span class="line">        out.flush();</span><br><span class="line">        out.close();</span><br><span class="line">        br.close();</span><br><span class="line">    &#125;</span><br><span class="line"></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_">swap</span><span class="params">(<span class="type">int</span> i, <span class="type">int</span> j)</span> &#123;</span><br><span class="line">        <span class="type">int</span> <span class="variable">tmp</span> <span class="operator">=</span> arr[i];</span><br><span class="line">        arr[i] = arr[j];</span><br><span class="line">        arr[j] = tmp;</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_">quickSort</span><span class="params">(<span class="type">int</span> l, <span class="type">int</span> r)</span> &#123;</span><br><span class="line">        <span class="comment">//此时数组不需要排序，直接返回</span></span><br><span class="line">        <span class="keyword">if</span> (l &gt;= r) &#123;</span><br><span class="line">            <span class="keyword">return</span>;</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="comment">// 随机生成一个下标，并保存该下标的值</span></span><br><span class="line">        <span class="type">int</span> <span class="variable">x</span> <span class="operator">=</span> arr[l + (<span class="type">int</span>) (Math.random() * (r - l + <span class="number">1</span>))];</span><br><span class="line"><span class="comment">//        根据x值进行对两边分治</span></span><br><span class="line">        partition(l, r, x);</span><br><span class="line">        <span class="comment">// 为了防止底层的递归过程覆盖全局变量</span></span><br><span class="line">        <span class="comment">// 这里用临时变量记录first、last</span></span><br><span class="line">        <span class="type">int</span> <span class="variable">left</span> <span class="operator">=</span> first;</span><br><span class="line">        <span class="type">int</span> <span class="variable">right</span> <span class="operator">=</span> last;</span><br><span class="line"><span class="comment">//        不断对两部分进行细分、排序</span></span><br><span class="line">        quickSort(l, left - <span class="number">1</span>);</span><br><span class="line">        quickSort(right + <span class="number">1</span>, r);</span><br><span 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="type">int</span> first, last;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 已知arr[l....r]范围上一定有x这个值</span></span><br><span class="line">    <span class="comment">// 划分数组 &lt;x放左边，==x放中间，&gt;x放右边</span></span><br><span class="line">    <span class="comment">// 把全局变量first, last，更新成==x区域的左右边界</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_">partition</span><span class="params">(<span class="type">int</span> l, <span class="type">int</span> r, <span class="type">int</span> x)</span> &#123;</span><br><span class="line">        first = l;</span><br><span class="line">        last = r;</span><br><span class="line">        <span class="type">int</span> <span class="variable">i</span> <span class="operator">=</span> l;</span><br><span class="line">        <span class="keyword">while</span> (i &lt;= last) &#123;</span><br><span class="line"><span class="comment">//            相等则直接跳过，判断下一个</span></span><br><span class="line">            <span class="keyword">if</span> (arr[i] == x) &#123;</span><br><span class="line">                i++;</span><br><span class="line">            &#125; <span class="keyword">else</span> <span class="keyword">if</span> (arr[i] &lt; x) &#123;</span><br><span class="line"><span class="comment">//                交换位置，并扩大左侧边界</span></span><br><span class="line">                swap(first++, i++);</span><br><span class="line">            &#125; <span class="keyword">else</span> &#123;</span><br><span class="line"><span class="comment">//                交换位置，并扩大右侧边界，因为交换后的值还没有判断他的大小，所以i不需要增加</span></span><br><span class="line">                swap(i, last--);</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">&#125;</span><br></pre></td></tr></table></figure></p><h1 id="随机选择算法"><a href="#随机选择算法" class="headerlink" title="随机选择算法"></a>随机选择算法</h1><ul><li><code>随机选择算法（Randomized Select）</code>，一般是指 <code>快速选择算法（QuickSelect）</code> 的随机化版本，用来在无序数组中找第 k 大的元素。</li><li>这里的是第k大的元素，不是第k大的不同的元素，因此元素是可以重复数的</li><li><p>时间复杂度为O(n)</p></li><li><p>实现思路：</p><ul><li>随机选择一个数作为基准数来划分数组，根据这个基准数将数组分为3个部分，小于基准数，等于基准数，大于基准数</li><li>根据划分的结果，来决定继续在左边还是右边递归查找</li><li>只需要处理一边的子数组，所以时间复杂度相比快排更小</li></ul></li></ul><h1 id="堆结构和堆排序"><a href="#堆结构和堆排序" class="headerlink" title="堆结构和堆排序"></a>堆结构和堆排序</h1>]]></content>
    
    
      
      
    <summary type="html">&lt;h1 id=&quot;从社会实验到算法&quot;&gt;&lt;a href=&quot;#从社会实验到算法&quot; class=&quot;headerlink&quot; title=&quot;从社会实验到算法&quot;&gt;&lt;/a&gt;从社会实验到算法&lt;/h1&gt;&lt;div class=&quot;note no-icon flat&quot;&gt;&lt;p&gt;一开始有100个人，每个人都有</summary>
      
    
    
    
    
    <category term="数据结构" scheme="https://silvan.chat/tags/%E6%95%B0%E6%8D%AE%E7%BB%93%E6%9E%84/"/>
    
  </entry>
  
  <entry>
    <title>hexo快速入门</title>
    <link href="https://silvan.chat/2025/10/28/%E5%BF%AB%E9%80%9F%E5%85%A5%E9%97%A8/"/>
    <id>https://silvan.chat/2025/10/28/%E5%BF%AB%E9%80%9F%E5%85%A5%E9%97%A8/</id>
    <published>2025-10-28T11:42:57.683Z</published>
    <updated>2025-10-28T11:42:57.684Z</updated>
    
    <content type="html"><![CDATA[<div class="hbe hbe-container" id="hexo-blog-encrypt" data-wpm="抱歉, 这个密码看着不太对, 请再试试." data-whm="抱歉, 这个文章不能被校验, 不过您还是能看看解密后的内容.">  <script id="hbeData" type="hbeData" data-hmacdigest="7b8e640fb2a8cf9ba72a27250b49683cb2e6f199459a3eac4f03a5213b07ce9b">21f8e907b6ea59635716f91a3e5cdbabeb4ebcb616b9ba916bb634df7fbc679d4289cbc78c600dae3f4664089079a9c23d90138913459646b21c250401293659682ec80be238ca7068773edeafe041c9ab5f17b6b06abed3a2a97966c1abe4e8573dc66dc8728724eb070fa591b307dabf0b684320ee7bfc9e11abdf22ad66191fcb4699ad24093124a902bb87fff91e6b30f5b37837c05c5e92187fdf10688a3314fca0baf3e1926e3be4fa43e1e8bba694979e378e10031764c37225046f51b8a9a9bf48e4fea92d710ecdb5a4e471e54495440b8a0965db31a91b8b9b5fd957c4a6e016f2126241c5884773e4441013c276df214f70bec00a37e8da0b813e42363ab8fc6d558a10203c5ffdfecb1c54dba29d9f60cb507a5e5d552df230c887d71425ae3b3be9b5ce8ef51604902df1bf458ca65ba5a993c3d92c501014f92f3374543b37f78b4e247d805216f5c2c8293af05ffbeb4d9dd903307005687e5e56a3e6882a030fd1e968aafc75a49c04871d27439d9d46c7ec6a177c7b4ca0a4074cd2b80f81a52ace08b452f317b3f5add7aa9991412cfbf68fd4346142ad9fba36c3905353979a62e09c84d0050c6f1cbdcfba36b4af267a19e3f9745058035670a4033586f3bacbdd666a95c17025bfe9709c5e4cd8a0dd986bde39e4c9b198d17f40a9633a72e6da6000a2cb410ba653e3aef8688fae5d7cd709df60cc5a15983e46d7a826225ac1e63153881175260c4bcaabdeb5c9af166b95fa712f9a5644a62b7f4de5fceebfdff7b4e8b5cfea6e42398713b91061932d964ffd35a4760118001174f58da42cf5408290d54fef9b5530a7da08b5e47aa790f6f18eb13b0ebaa6e685fc77ac0f43e5085725eaa50f871d8df1792503681f893b6df3bc6b1722e22b0cbefd1b944b281527bbb7dd4e3b245fe396cd72f1d3f09cdc3cfd4f0edd74487fa0b75678c4ceabf00c4ac1fd198ea2ec431962d8cec7ebcbacb79a75ac5ed6db33d79fdf4f7ef7b0475b5434c6535cd62c573226eaec141d21218eab4b67817c65997b4d88ee2ecdf499b564db84575be2733054d6cac9028b26c5252670538a89dbe306f6fc9af668f32b26eefb8d73d085234105d931d406a63593d9234abd91377c863eba827d27c94931f09f0e4d4a20b83eaa72a2eafc5959cd38563e2fcea0d18f0651bd1f68a8e97e66dad98d320458129bdf82cdd745f47c875f755c153571c50bf13cea28b324d2210f8f100abcde77c4f88618330bd5fc64f02b098475a77b5ff989ad64b6cd35e778c467ec1b91fdfb18cc79c2f624128167a4deb17865ac6636fa86c99197dfb3df2954de58c651491256a2c21b18d7cea0ddbbc4ca444e9f26d851f80068b4cefc05032db595740b02885abbaa3adb963943d4a85480605f28f9fc952c6628c70db1c3cba56bcd21e9f89daee3f4076b045aa1c84ccb5db66b62daa3a319a7c9e05c86334659fb239c7870452ee1db7b2104c3973b28f6932779aaa5c50812086eece6be0201146b8169e96c68452c9b092e3d64db6ba18769e6bfbda0e5d6c9f5c99ef0d29a50b38f2789ce87a9eeb941410f1091e096be4233eb316c2b69191ff5f3b7ac82bd2e1ca5e93b904014bcb8028363f7027338c493ef9b7272467bfb4b8483c330f5bc057faf62915273c3b8e5c6652456bb43c501225dce248867bba1868af5e0a96c3b7f8cb02a9e9bf7b37c505c921686bc07c49b0600218f3277ccde68c87ddf23b765190dc7f8946d3fe957f89e25a7b198e56793044034c6a8b75056466b180f3282ab8bdc1e24858d44644329f4937fbb2a0b8095994441bde7ccc2e400d60c43e54c2eabc3686a954de66e1b8efececcd5a9f27c653362f5a32b32d5eb9a0849413e56b2f068bd131bac384f3c36be4f9081c156f9e0e517fc8172544337f01e753f4003c338d0651aed1c3633daefb0db19ec95624d39819b6d59e122f802036e8187f978cc8a85c8d47ae2f7b8b07742e54442b2369f7461557357bdfd77ae7cc043e3ff74ba6ec32f2296bc897189a83a6d36cb3a0c72751019df7c2faa77925c45d5300eece71f86c6c49c5b59c988655566b4c247f51203e7fe98b84de13ee33f52dc3a9e55ccb9b4d859a31e851c45a69bfda28b17804e64c9ca21199962a9cba40291b3ce21b2bab251cb7a196d326c77b9eb60c526923eb3909f9f429a05b270231511a9e8f1b388cc0e3570fbb5bf0d93dd31ae6077ccc195ff3042ecf1503b8985c77b48d21b96dd3c58b85fde8a91668c44c1adfeca9ee37f9cc28ad741f1a3fe6a74ccb4ac96594734261b5962999ff31eb07196105425ee43dcf4a9f9ca461e073bfab71cf7df3fa5384519e6a372aa159bf937f01c3510b6c8a415bcab1d6f048a3aebeea948d24382ac95dc41aebb1a7499e420c3396e3e809ef9c7d7ee519716bb89402b5c6e344f3cc0ed38d40fa13963611d50b0e765e97380bc181a3c6c7595312ad56cea7c823dfaa8816389cd0f41002082e5424466908c4eefea80ce4a47e56ebacb495fd6174d034f221f8066a9ef5f83fd1a33f2926c6e3e8391014017731e0d77b0c65414e7eb932249b51790aae8f91955db741ee3c801b26fe00bcb81c810c09a44a4614894add1edf9bda6f3c379b99fde4459435bbb5998305174583503134cfe10a77ce3ac7329013b73ce66d5bf15a8a37df7a43133eabe259a969a8950ae2c1aaea74a4bbe7cded2134cf6c729eb335aa07394c2edf7e4951bdcba5a37da19a9c8c0bf67ddb3e5d3942c42cdca060538f88c19abe6b38eace7e315a6ab2dd5e08b94701fcf679c7aafe307a735225795d163a8e66df83bbac80cf02e1694f1e7d0ad149ee48f29aa7e56e66cd3d7660178ab884aeb33243a686f8c51f5549785103953a125023b06e30333a8d80ec4d172023e863ed2c65b9137399e3713f3ed3ed0b8ceedbb28a81fb5330929b36f34553bb6892fe1fbeb27f6a75b7b907a2cdea8a65713d7386287198ddf21af3e48674033bec24c001292d62749bad84b37f3dc12de81ca4cf4c9f7a13c65733da05ab31700dcd23bf5ea94e066c394236758c68165513f9ef2b32826831d027554074445b7f8ae96976eac3ad9c6ad30b64c406614eba9349cd55fd30e5276651d41dd3ab7224b2be5cecec6a30dc15c5ca0ac07e246315e5684cce220df18c157594b80f30f1d61bce2681786c8667ba7b602fb2787e0f4227f7348ec08b0a5aeb6927bc719ca85eb6b33b74f6f8b8d96badb025c9c9f1f9e0fd1854e8a1e6aa5d0f42942968eaaa343d912045bcbbc67f72a0c32bfbcf6463ca034f0646da83ba68fba67824b0312791568975592b5e3d7298f5fc0f20f9e82672b75ffeeb82b28e2c7f43efaa1b1f1843c6fd0333095cea8f99e09f09fb6a0dc3ff0a8fd2bd03498a1a0f96f00b46a6640b691447fcf5e6cdd55df4c8cf44adf8e4dfbe67d9a958ea5559d9e5b91daa34dc6af6e1943a9861ddf662d5d7801610d6acc7602a7ded21d78c2a4923985582c26e89f4fd427e38ff8b440580b4b92cb72e86502d1bcd7485e9a9001734b276f577742a95554f3a987c9e6d90f07723898061d93b9b94736f6ce6cab51950a102cc1cad08cf4e8b2221ecda1ad9cd548e7f9882bcc206fea1e12f81e1faceb67cdf2dbfe718c1d37500109f6a30638612e32b903fa0c734cea507097abe429c82b92d706deeee240a14a19c5511da38f07ee0ee6d1ae21888d559455c14da9726ebce74b669227c4de2ddc1c456d0e49dc93e6dc92514ea825262939dd9f70ddc6ca4b758a85a044bd0299b24a626afa0963d3d9dd0ff921d6de500141e86e88a66a183a7e09cd6d52647f35cf2e71f2e0b9f644ccdd974bb8c89fe711187f4ebc601e3c2dd004bf27af52fb4a17357adbfa7687e0c232e9c27e2e6f4fdf5f48a237b80b38570d7ea503365bcf9f3acc9b40871645eafc4cf5e1342165de38508a49ce5f31d196a30d8e2715e31914a15cde0430af633e38420664b721f7c42d487a16090e72cfc1ed377ab29670a4b4810d95d1e0895f5a0a796890ad60f150cbb9d101a3d0ed129e8cdb0e7b7cf3f5af23e1d842fd4c4c4548f69d7c22f889b4f78a7cbc08abdde59c386c34d25e66c40d0e437de17b832d10ea12a5fd5cb6030a3e11ca7f0cf810623acb39c63e2b2e0140fc9f85cdc8d7681795cc5075e6eaa4598c31293d2315d80c8db344421e6ed2e3b3d4232c710182d115681ae6c4c9086bdeaef9494130a7d0c2eb08f1d8b82b8f889ed24dfe12bbe19b08deeae7ed7a8c4f0e1db2654044be6bff522441d9e675667881110ff5e92010087cd0ff452103173542caaef370774eae014104d59df9ba74991fe2b52aae5eb45080034a80aabdc9852e5c9702bb5066a592c2f6ff2670fe55af25821c3534034852545b6592281fdd303453383e119905c84e5130cc369067b55c46177a2bef7b706ebd0ffb62fec6b933497d8dcc45feb37d10cd3bf279839f69e2f35e77c54353a872aa99aaf386b6df2d149a0a35be51e435d1adbce288b47d2743983dfcc5bd87f44b0ee17fbd56f96980308b5bf13772a3dc39865c4386c0ba311289e92a67af5bcfc1424deac96387f40f21d2c687fe04b0d90ef47124c082e188d88b637603e54734c85ff4c63ff43ae4ae9ac51094bbf68e59856cfbf611eb1f5ed113f829f43b73c0a0b29af36095cec8a32996ab13d68045d9ab55e6e6c022d118cfe6d1870952a7bef1791d26be909aab5e9517918ff89c668d2434b1bcd1789c595dbd324e85b3c998b467578c077eb51bb8a6a11b719eaa46a894fb2e3073bf4358a11b8b3bc442910ce840cd10351340ce47b883c1637adf237248b77f494e3f69e2bdd97527dd5c50b1ec63ac1cb28d727c08728ccb76c4b5f8162aa59bb10f48f603c3a8ee7b7cdbddc59261fe1dd044aedfc9257b63650e3fa486d600341582e81ab020bd74174a3f866d3392f786b4aad29294e527b5cf3829e223346f536340c86e2f722abf53e7f20d04f4824f6b02641d387e98bcf143bed68e3f4129ccdbd5f07f09608661dea0b424bc2692df61e0af330e484d1517ee3fcb73d7ab096d8831ec8d381d87f9f8c71746607e1cbc6ddfdf2d14dbeb321c932c6512bc12fbf262f533341dce8da685fb1ec99acbfa426ab737a514597e0062ef0cc7360c74ca0d0285e2b02473cf6e3bbfec0dce80a7394160e5f20d9f00d222467868d448d50f0b86e1ad7b3f9056986e6992199d4979a89d957771d2d26f29697750c0c6ee4616388dfd107bfee0c90550dced4469581a11233b9caf2280ca9051fdafaabd8acae15de7c2cb56dd3d4f1492105825e118ad438b52de8062a531aaf0bd8a6e8dfcda16444d6eadffc6b7323eb4ee0df7f3a46eb318358fb16f5265261ef445df1938c34d10df6adaca4e72399ca936b4d8536e88c0f7bec3fc2493ab65d1a12e0e409edd54d8c338987d474b478a4259eb8966da817b2b537976845692859fd031c72e11eeaa1481855b3a5f6693ee8c7b47db4d6d2549735e732473a66c9fe068fbe6c8b22543db8de37e0497956a6f398b916693cfb36acca5c884555112c3d01c17c938ca21f85564ef4d1ba9671dcec423ca3714a2204b76c50f743ffce6ba6bf2173354abf3085fe8e57429ae28d9ab9814d221302d2ec6b7e8c3d453dce3f4c3c2c4381098fba8abb3619dfaacce2c5c40cf247178ea4cc9208957eb2282fce63011dcb9f154445b6ac0577c3000f0776e86a078447254f55a18cae7eb95f58c6041d33974bab95af922a357eabef65b44e111246a69eeccd4c9217ebeb1e2469ce43b327c5e4cbf01cdf2cd25c75fb15922120d6427f1f8ca349d4164f7f63a0e99dfe0097f417bccbf44b0e57e2400f6d8655d7be1f5945608f4be4051d061b63d7f64588885b0dd3920ad2fcb3ecc0865439787118a5132c4160765c36fea21504a907a9e77b5eeae2fffadd9f42ed19e61f1d79deaf4303f36a357887b18bb6b68c3221b4565c6fdeea0ef9d51169630658980b87753d603a6e830904b9e87ba23b936ed7f58551197a8804c72b59e94f15dca0460403f6c85dd39fcabc0a1d143343c03f9a8aab36bd24345d40a2e6371ae223445b54c08cae66cf5161b97b5c270d497cf27e3ed07c1e72b60945b7d642e70eea223a51cb2287584e8e8eed86bdc96172597ed69f58aebf5723ad69fc1707a1f1af2701889ea7793648a471e830d1d005e5b2e09185d22f477268f02f0e2509fe711588dae37f6ce15f379eaef076b3dc0c4c3a5dfd680ee1439509155a0a559279c3fabc777b31cb58c7f68288447df0cf6c253148b684e1b769b1ca5987e05b91f44597ad275871cc7efbfdcf4017ad8bf5ad538cc9eebf861130bcaed53be07422aa857aeb08e62d609460c5e2a278b8d9684a36a34de7bdb41f4095246dc72230dd169176e0d7b421d49d4dd81c01db7573cd679f0d59ff859a6588d7b96bcda94db6d071e11c31f7aca92575288e8e5f67dce0bc91ba7305bfb6d71fc08622fece934aa5353e8d63a56b2d9217af9fa90dece6dbacd9e270692842b9f927618831254035833ed0ac7a4dd1262cc772d4c449134efa0d10fd5288a6c7047afce2ea194f705be905d8ca13de9e798bec7b646f98dc639118167de5f589bab497781d53c4c343fd32b39a08266b78c17284bf876062dd9f216df3cd3dea9e5e6b114a7d993d05c3b6ac7f2ac1c0039b9d945a47b7398925e6a0e3b94fce463182f49fd5a3591b2112c58e9148ea635cbc2f45868e27bed18e7888d8024249414d295e6b427a1ed8593ad46b8998ee8af4ca4a1d6545d7eb9a46c5f5702f12a154ca21b78aaed2dc1773983aecc554f3422b23ee0e1054dba5cae9495faf5a30940ccccaae257c3d062d438046a23162bafea08b54f9923734f106930de59eb1a36ba811df1d19d3afa50c32be12802b1f896e306774b4c765a537fca1364638a055b1080842835552be28ed46c1f9db857ab3c9dbadc1e650f46e3f9357196c130e2c2fac0a584f46c27d62ac551d0723de6b1eed5ec63bf9aa40d4a7feb58bbd61530a7bb3dfd1f978e7b66cc10b730334bbda945c2fcf11b02b5209e221e223f3b83832f2c6a362870f818b8fc0b0e9d0bb7697fd7a5ca88f709062f72ca03df013e1c9f9dd5541ac570d0b4ed7eaa4aa6f2820f2b68c0027a1d9829731d8b8c73e577223b511b230384c4d078eec0bfda571c2d90c5c2cff40d6c9f9ea112075eaab061ebd4e38386802b96ecf4bce9cfdcd7e382aca554d483da5ad80b43e3451d7e8284d1b1db80ee6bb7168c1f5ca5309bb8c940b3a9449996ceca6a063fb05ce66dbfa640e63d6fa3cbf2f219d863a8e5ec1ac16c233b7de50c1a2e117f6b2fd7c03025a98fd3dccc40de8f9c9678c542c35b557cb3f5a2d252c714a5bfc9d81f0d987cd2a3a74284a4890c23110e32d6df18c8f659d6d56c22a39f846f15ea5a528336e2cf1e85c81e3450f564238fa32c2f5303c1b3e179e820cc622884f54747fd3c789fa07009565684723ef23413d47fe5cd0a384c99e28cae1edce155d37f33e3a50937506a8a90ffeea89fac557f7e3d1060a25b5d05a773b72254a21de5c0f069b6fc4e67e13e2d64f7b6a43d57acb993c4bc986541b30de250d564c31fba5374f68386f25a3afd8b00a07f28cfef2ebae35c4782215e54c8e1c3c0db4c5dae56398c30fa8af1ccb9245e5477dd6b67567c51869c4355cf041aac5ef7b46ba3426c2d835a58982094c26b11ab8ea2ae63e218163f4b83fe50fe8e3cd2c49341307789d82afd68e80c318c575ff7c817a8280b2c12b1c1e22bd3269ff71c2d1dc751b8c395bbc7f7d5ee29b509c9df5ee25d2d0b48531342988612d70d23bf46ae6deaa620a76faa21edb942f963f8ce4fe529eeafc121b4e8a75660d17846309a04d1794211ddce4fce90a626d03ca46d1f23f8063a3c5b5668d6f857ddf0e726636f877d1bc38b9b1f0231db792be5f419157157d94e780da679dcba75f1802d8825c18c928996b60ef2ffd9766fa98a1e84cdea6c26a0e15ea810d58117d820004fc818983a5dc531a64a9cc94e37963ebb4559b5917eccb256f0a67c69d90de1d3a31329784f3038fa201e4cd1f20b47911e3bc12197b900cde73821034e16df081e16761abebf30b11adb6a93d306d91a33937fceefb7e4795212761c4a64f7ee294d32a8f0f7affa461f9df7a60026532da86f027eb94bcecf4a7bb9c318f850af3dd4905cd8f17cc0c1222292711537f9dd8e76f01795741f7bdbc1f1abf5a8881eca2c593cf3030c27210d43b3923ca45735bfe32aa057a6f9bc54b10b6a85eaf954e45f2895034543d9b99029491fac40f899664ac044957a8c0010333771f65a03610c38358346de365c69f6d424e4f2062cd1d0c6d63b9e862316e2e4461e45299f6b068826c05aa2f95f82d5a0bab8e5b40f9e7e6c5681dca741926cc415718dec2680d376479f9b97dfe5d80c2b78b524f02d60f2b945523f2b19cb8b078efe4d05a573d6161caa32cffe9dfe3230966def0961a4f4762a67fe5c30c309efced2d4ffb2cacfd83bce35f90bfc714198c49cd0db2713dd7244b8135b27126e5c8c18e9a26179ba30918921bbd38c494edf8dad06f3f928de54abdb193b3f3ab496a7e14904bc27866a2f43a5d654e0fa0b172916baddc0163e68d18972828b1509d94cfed9f7475d9b634977d370e41b734ebc4ed5b8f86961ac23858e0f5063ba22f08ab2b8c83f18889c0e02c9dba1552ee992754e12056f622a2f5fa7cf2bc7531d1855bd66a6717772f8a3bb5351d275bcb650e168c1012a005aa6c1a0e764a3614fdbf29023ddc206448d9f78a0e957b10de4ed32c5b2d903d5a5ec66f99a1e1373bc814cae468391da7558dd9de2c60927b70919cc5fade001818db1b7ad5caee8d54da33530c886acbcc8a363fa40ee624916fbeb45b5880507c43b08bdcb2f9d9a9eec99fc6c51634499118195294e4027e9627eaa4e47a76abf0f905a5260fa54ed7f8450d9eb3a7f99af3c5df8e35b8979bed32f9b912e6428e7e8aefd58bbc5d98f3f5a15c230137f7ccc3a5ad7674c64d41da77c0ab73fa21bdf96c28947642c9ae86fadad7cf4d653fb7be770a73e61fddd15103f3213e5ba99d97e207b596179af2c9772f3f8a0a6e4e66645acaa190ea9c259057512f37fa36151cf712995dfab9606687d4e6927b1e547edb43d8b9eaf0638c9f0a01e79d411a47f0a72c6ff84501d5b3c71ce042b1c026939f1c22bda22bd5dc1ff9b16561469d39833b92d954081e3e7fadb298cdeebf3d01988c582699c7de2aaec85f374b40e1b0f0985cbf7d86541562c4b54f2615c9f98e6f487a94c5479f122ce9c325385defe3d9930f44355d1fdb51f921bdc9dc32969c11a1e508038cba45dc0471310a9551b614a7c8e1954e1cdeeb59eac70adcec3e513948ed1034bc54fb6f64187a28fb04defb53796217d688a858d1f0ab8195838b1c342de6895d62a656451b5068d65f146b59aca394dbf87f334238ee8b26348acc3a9f831695a8c5da3e3647ca6c29aa93a7364f6b0c8aaeed17570f9a6ad8d90e96f7f0565b3e06ea5f42fc6cfdc4e3312cc594ac15a1ccbe8d51aae7c1117451a8dfe81d1ed782fb2f18ec1b7ccbc3e72e989161b5304a2e6bdceab0bdf9b4c2cb76ba5dcf99ec2997b46196a31c56a35d246acd75dc3184092037febfbe38e5d2d7342078def2ec0b8bddac64d3e17554943fd7fe56ef71d6d092961f221acf6ec13e4dc02b65faddcb3b470dbdd321816aa6109250b92fdbd013c7d5c6c06b0487dae9f85a0200cedc3234100494522eb69e92cc8ee9857eb040ba6e7136bb2e140a90430f3cda72b3db888edd1f74ddfac669d33f580c1304ebb6de4a6b5053d0b7deb01d7733f89c889de86d654062dca222f51fc327585a96a94c2e28a3a3585ed45a9a74d56b928281c2db633f67a7476e698443ae84fc44ec499673819c90dbd308792e446895eb20de95a6288f82812eb7d02f6f22d5128ca267379c905a98f2d0bc3897282e02d12722287236840be2c3750381a8c1a1f39291cb4bcbc072b189c276b03aa66faacfe297f9f8d4aabdba85cb3f2832e1efb0f99e8a9330b2211290dece4f484c81818069cb2d00182c910b932b1d697f4001134bc7cdeef1893d0e8ef5f0b8c112cdeae8da15e5710c7b7b8948eca117b713d742e91074100907beb901cce0854774e7172fef87785c7b2c7d6e9f0504ef105d7d4eac58b843216e1cd1cb655f078a7f7fd19fc8da9de018109335d8154458c29159df16e041a631e9f713a6ffcd436260755cc8099f292c790c946c43da65b9908a0732177cfe0405dd2ef35fa89b6e9925f4252f3d1caf99a9b1a4861284c833c50e74e7fe25763086e86e46503b1c7076c2d68c061c4b47a2202a54b7f7c19f2588d77f83fb7c780b18084ef08e89547d5b22dfefbe6a37e23b14da0744e67022f3f1cd7f328d2958e4ec21e7e771d37457e1eb8a8a0bc30582b3b011c5aa992fc8d85f722ea2a3743f32ab5a865ac089384aea046c85e7f503c2c3de391672af8f5495500d311bb3186d41c29d982dbec54919d2c43ca27d3a6fbe395664a3bb1ea6e740d68e4c22c62e1267d0b4084ec27fbfde9fcf0ed51b2ab5cb27a48fea147c84d94c6c9f1a3c68d30c317ba73d055866e4855c4fc7a19a5217e7940ee1a0690b89ce89d463a7830c65d4e7d8191fb8ed6d71657b0165073a1ee2d9ca1ad4b3169e44fa6a8c62b327ab8ea0598301b9d4162992983aaddbbef98191c9d61a64b18306307288f7a54cff9f23cae9d91c1bead223316ad1c438976f9ad352373b856e2c1fb46d4f5ea3ad3bc3ebbc9e1d478e7273bfaf6ab89df3057b47669c5700dc108e29db057d71903aac37e6eaf71b8d478fdf7ab07046eb9433aaddfbb8630e87514adc7060f4da49dff66c0dbe551a33ed8befc8bac80b3e069dbc8fad003a27adbbf6f15df094923fb87ed7e117dead4bfb273df8837bb954ad5ff4a95b758973c3d8c1412ad8b5fd4a4b46f161dbb4b491bad77e23db2f3a8c3e14b077093a39d276f0590ec15645ead38fafefd54c23a0fc74f20f6badc87cd4c29ba999d30806bcf1473752fda0f94d53dbc89f2d9c714a5a4012d6327a24bb8fe0fd30352dee80621becb593215ab048c444d00acda35ee0324bead781b334298de230a6214026762ec5f8a4211222568ab8185db18c5362e9a8bb997ce1a658b87086f605a381d5454a5bbfe9756db6229ca924eaef4eda3d70803ce1f172a13de2a3e4d876926152b67778ec7a5456d5e5cd5ccc0f0f76448b20ddffd3424a50b227fe97e16a5a57d4d756811a87fdfae67068a877b24b5ab7e0da4355950e66020103d994c83b5cd1ff2118ff2a3ba20b693f1c087179b0ad37aa4a103d28ddce36af04921f591db9946472efd272353c3e48b4100d775cd5612be4229fbfb0ac36d8a6294dfca05e4692f5af59b369904e638a945d4ee2267aa2176977e1841efde92e4def7fcdbf6f5e48a5fa0f3d3e5cc7f230db01a11f73903b0864900c6c7d5e01d9cefabcc604a1bb9d3cb514f3b193d9b5c36168ad9a8bded80d1fb95a9ffff66a81b9fab0b1a88ab67e8a86ed30871218bcfbd053431b398f768607178a15a4f7503c659e7c22fb7500653cf7d59fd352991aceaa3a77057e40a0524478bb50f87706e7502e7062d653c5212c7225b497945e4e5714d2fe1cb788a6e8a7319bfee8a6db1f45f160b9f2e930e0f309074246431a6b2e5f80e5fe9af3ec461e32f3e9c9f181e6a413a50b50f2e8d1b39378a396b3b4e2d800890cdab6645380a9760240f7e0bee1ff05945378a9632b7456eaef3889158cd4943a3b545ea0cd8eee0b9765eac5410786ea3c698e8f183d319b0017f45e363a90e841fce70312c30a0856beb6c867e0e4299c2db53f52c2d398359dc889173d34b069cd63581c231ecc096513c3053f1e4b6364e0a9ee9f7c4e94e75e78e896e38dd1e3998b286d7f546e241796c4c2024516767013bce37e3d2b8e19986612e0b823e253e5097f47b7aee7b79935f60dc8fd0d2a6ad7fabeebead7f136637b924e5c1086fa0758eaad850ba0e84bf3c02cb10701ae5c84dc63b72d9ab867b6e4909fd53ff60804a216ae451403838081385cf3249554402dbd11673bc7790032f6d422bd93e04fa6e3edeb00ec033c95b1c15b78013f79ca5f8a1bb3ca9eae9a33485df43aa0b7229471ea9eddbb0626a509a3a6c9c89aa02a987f39a9a12d645e2dc216623ccf1e343bc8699e80db10c76816098a2189acb710f1c3f126222ecd2ee4cc261ba968674e1fec85b48b3ab77bb4803871598b88f3b459fd564e37de3553eedf0a9d595cef3457e638b31ea3291186d2d6ea7acae5bbb78b23e907bfbc131bb453d53600c4fb5b4bca8ea58b80d6c058b722747b7b2601684e68f04c484e46f2a3572dd6609456d4f633c851a3fd1aa70d799ba03192f117574e42e8b41976fa25e9105fab594d82e5ad06751ffac8314066347d48d48e1d035d9b391d85c278a4c5b96b58944b1de63e02c6e2f536753ebc571f050153b0811eeac2b72372d55634fb86e21b3252c4f89455670e5112fb3aeef4cf19568379bab2a3b329f5f9bb245c6700239e55e4fc3c241cfc26c9470224f6b84ec37c534e99531dfbcc4ecc41309c0012b7d041aae6b03fcc94245c2d851bf606530190f16bb8744ef659e7213f360dd8c3d152906538baca851cb8be7d250595bba52e4e8e1d7bcf5868a68a13bfd2f1832be6a0cbfe9f92f6f5c381740ed1fb7049a13f887fc924456a06bff519bf53ce169e8e67310be609cd8bd483600ee394ae586dee690a3cff75bedcbea178a5384c6fbd49bd9aac3b27561d284c6050700fa6cf1f8a5995d875600eae4ed03d837d4d606a85fe673c3540d8f533b747fbe68c3992249611b6f95ed4ecd6078c50543c68b8b143a088eb69eae7b7b42e1338684d302c72b9ca686fd9605a5e4f8c9a156dbab3412b153eee0756d92bfab1c33beed32964431d8455e7a1e92ca26075c9aaec2aa42714099e6021f21e5f04f3a7b3256e7877c2ec1a76f03638e5807133984e16123a149e2dd8e442f7d1198cea548fd78bb7715892e1e7dce63949e8fef2b15a8d0e7e148c717129d34a8383622bfbb240cbf097a5ac603372c70b60b0926be661aa1b43201665038925c4badef544f52231fbfe57936d77c06ba9e91d5eead74dca2d34ed6d4c243cecddef1a11f53cc2e8bc13df4f935664fe31302ab3bfe8dc39d75af802de860c76c3c71a3ef263658291b0fdfc11ba1af320fb89d12a6616ab4f7875fa5e287933b63fccd09f4812a8d6e883e4772883d19b2641a72a5a937481e5b749b307be40dfd82794d57f19d80af68946b29b354438aee1e795b669072bf1237e7dc6723fd9f0fe3187a2bb0db44b88629d53eab4290a7967e3e6fb73b9379cbeac7704045ec61946567d83787ef58c7e16c8844f82bf8bcf1586c1513d77b4d9dfba01b854d9d1b6cebf8c5b18e8dea2de7c6593586a606463436efb655211013e974e6a72465ecca3ed8ce87e4a07fabf902cfd24dd39bfdbcd1d1080bf7a3df62ca79541e750c04861c252487ec8bd7d212f2e3df6dcc67af42390af801ef36a3d4e4a0ecd8bd03fe730b1798c8c8fa8b9d05ec8fa99f832b64138fed8cfa9be01009597429addc6a1ee9bbf9f751d5f18d6c330b70b5a3412e4792f354d9159d5041a384b1432ea97c4c65393399381d7cee61c94ac81a0713a42c7d9755dabf204e18b6e2887d4f3a53cca517e6ad5eeeb8fc8fc3237dc5b7f510ae9d31324ae5ad3db5352243eab00aaaa5de30325e8f2c8f539a2255f7013c2b27409cb5b8107a062623f51856b6eab2e67903ff1c0bfef61623dfd4ac64d24c3640cf2356d46e14a342e35b231025b99b5bd17c9be5952ac0b4e261411af2e6853fc9061790856ded4f7ca50a1d451e5c39ed84bd0b282df053e2bb2a701dc006b9d181a2caf038e3d6437ccc71cdb988a3f0f02775f9d6ac1d775560cc588b0b450a3ab56d776bcc45b088e7becf961c9baab9142733448575deac15c51a1c96ed42e5328bf375dbb2fe974a30083ef3f543843b8c00f2e07583608165ab090b5ee56384a16c858b4d91181264a1b862c154d81086be548d5130d1a0f7d9d573f1ead902639fcce405c32735e421b43211cdfa3d632806be5045a0b54862c4defed613bbb42863c7c55b8bde9a54322a707586f55a27841a1d241db15898c9c8a6fbc6e49d6dee2a69a1f8906f067dc053866c4e87c0354ee67741e84fa43cc81fb7e4203bf88607e7c56deb9e159b0556d2248521fe045e3f50e06c378003859f1ca5df004d394c156ed9f1a589aae1d5ea88480efac55989f6ecff1da79fb069e09bfcde6eb3a22d44f0f06dc50b425e08d40225037d9a3989632a3b997b748cdb9b07f4076328294fdef9ee14c026f0962d368b8c6477d2eac7c5c814f02b9ba55d1779e0230223466d5083f192691651828caaf5b941eba7cb4cc27ae120a8cb88f28b87768ced9b2b86cbf1df9192283ab481dbe3e4c66e4574ce95b42d52936371274f64e7e3bb31f3c8822925b4a250cfbf35941fa5597c339e4c9ce71890e4f7e91ab86970ac73a3adc1850725346f65beb530d0641a5fba0f539dd43817209c6786ac5ce7b31d36247a308050b9420e17b618b574b2741bccf4c594c38e401a6192280f257c5e184cf06f4e8fe20eb860b06e8a7b4691bcb82fadb6724447dafa72874dca22bd0ac0ffe9689f499d253c54d53a2823f664f788f632271e4c28b924de76ba6843b754ee846ab8de3445fa2a99ba45f3cc2fab91473c2e44657245de353469c02e0de787926341f41dfa1be5339122cb0d52f5ecea51174d33e63363676ea47bfb207925880bc66fb46fd8e90d73b0484d0c5ad3976b30b9c354a7ab81563d619cdfd3c52985bcbc3d277f427b48a6b1682897997b9d27d3e5a364d939f35af192567e70b0afb6474fda6b30206ddeeb0104aa011de1becdfff6923245c1d1b725b91f081b6750054d705cbddfd6a4e27d58a53c09aaf5d6e46759d6bcc09d8c8c20e951dc15914b8aa97a3e4391e81ec0c2d3931d4c6fa3c33431c72ebcfdc031e5673f084242fb823294de131bf80fa0fe6c835d35144a869236c7efee2e5941a677d7c3deb751f39eeed0ee230173e3a820ad7c1db51f46071c04d09da7670b91eb28c7f38d685c680f6dd1eb56764a383c92a0a8280098eedde3c2a7d32af758884afdc213f0acace3b752784a171edad6822688684e5a3555e7616de6bf7400a339a199c75d63d0865a97a6e2308384ba4b9983ad4f1e54360cfa599fbec1c06345904e730fa08bb0defd4cd30450e539c774d8a30acdcc24188dc715537138c8914ce99bf9fcaa241362fd28645743ede639d6d0ef35103df72b0de6bfd409b27986a831b1b2891ad3cd74721252d3f36261b418dfc785c306201848b82a78849f8ad14aaf2033bf015dd1cdd5af55b64598b11839bd3aa13be56ace80f06db2b837f6f8bd95c802fd6e55f8bc7ed9d61d672d92ce32c933350cc53036879a9295ad1c0ae34428ddf5ac94f211a9795b450df20d3dc5d9018163da25a1eb2ba78d8b494cd45867400bf4e81409227c5dff7bc6aff15c0ed029972888c8925ccc447a59d7127d14a571ebf320194373a0a2c089218e96df6131888b77977c99e6676989764a7fb10aabbaabf46f15111b9b17ccedd533aa2479826cb07a806a013a4b4e3b39cff3faabfd8c1debe99edbd9bd0f31c581537bc2838c61db52c4dc99ef29d0270d98901d6117bc0779a04cf69b05ea0da6fe30fd8ffe33a517cf25d3631fde3302da05b367d76d7f8f4dfc1156bc06e1ba5cd13f821f047816962ada90074e3e34f5bf5eedfd69c7ff17ffaf349d09928e1ff6a5f27e99a5af57ac7504033153a3f8363b9b6d0f4c55c8adc03e6366ca138bbb0fb74a7974684dcb438392e3dcd092b5867d1a5e0d990629b55e03d4d197b0faab6973a58c5586b156a6bea1e5b37f7cf2f82bf053bacdabb82d4e37da2a976913a4c050dda2a275027782ce7912009e1e430ee6e002d9693c792e75136c6132c07846a03682f60cfab94ec75a8f29eb9f3fdeb3bd01826e7756d4fceb173325b58da9049c4be63c0d733ca0c97f498361fafc60db934eed89a5eddd8b9f41f918e5bff62031245aea099a3463012c806c61925596ddc85cb19e1aa90d2986cde6f986f070c69cbe45e6f0a6bd54f0c595a9252c0f7b8e9d9121e584e5648e298d1ea8440ab2407f191d9cb1bc0202c28fb5edd686fbf46b70accca0bb590f9d0d21a9bf99f6ea85c2c426ef8336f582adc50c0da8b69d3b2f142702b2bd22547f3e32ce3780c66822bff60d1f6bc0a86e08ed81523abfca6e6a827c932cc39d404dc99259c324ac766db46db1ada47a0a7fd6b691b58fba6fe7a8aca02bd43903f39b51b6e3874c137261bb290e57648915bd4ce15191c9cad22b99c7b35754cd398ea230df05e1d21bff62a135f4c81aa57c71dffe7c74bda49830d1f0e3aadcbc8a3f9213432aac2de9ba48b27926b976cdbcbe522ad45ffdbf812363ca89a5e7b68b2b8264c01ef6b1f2ca95d8a31d729bc6f9f9dbca2146c9504fd1f5fad2964824412df2ea6ccd65c3999cfacaaea01184b21e3ee45a0313caad2030daf250622bf8c1893da2978c4bd32a45314e994a877a33b46d5e38bc53542d7bbc0cd74d488ac9627752e57743e176487a6444152b1cec3439f8f90edff3b1dc84474ab01dd2bd8a05d66c7ffe710964fd8fed9d7d6519c102ecdd20c8703074f275afe4a3c4d3dfc453dfe33eedfe67f187ef29aa44130b7ef4abcc6637ab326ea19d6d648572b7b8a00e9518e52e4b3d7e8e48a38f88ac5c08c7474a8acaff54d4818570fcd4402cfa3d02c0ac05bee474b6dee28e33e9b9017478dee12c056d53db3211ac141b524350b6d5bf1f511a9467f18cbc45d594352ca25d8619c6b1d6829be9935ba45759f6d359c1156c65d7c5a1376edf49ced85594453b52246aa3fad05c5b2473a74de3f102f72dbbaacc0eb2dc52e0f127b65a85d51bfbfd3f3aa5ca7c8a9bb75105a0dabe6923cec868afb0dcb3c2fe7184035503f350bc440b85e87c44ccd47f1638c7f214bacfb9bef3b3dbca5aaf0d4934e478f7347fea845b2c48a084138a3af0f30e828433c6520ff1de9b9a355803f4dfed7d337b3df1ddba8e8d023a277c5fa846f1e810b57076c02ecae4b01cb2bb82e96fe09ee79fd827ca8a55b6f029354ba0a98e50c58caa65dc4b1321dc43ed92f047bd346882d44b9b46fc51baee9fa9087d9ede45998ce4ac6efd1aa405d8dad28f9b93e0ef2a4b681b8d19cd3cc8f4bb858a1a83c768b1a7dc8a5541058d05a844570a49baaf61c8a6a22b1a4c76a7625cf72e84e610b2c22ccc6fad3658e223022bf0f1a805ad6593e134d706d0a74a1bea9d17396a6b9ba0a3e5b47d873987ef291c6000673812679e9f436d3ba307f4878cc947667069f05af2b07e4b8585c95ff1b6d5b36ee4105dee3059eba984422a795f4cc8a05a5cbf0cd3c26da5a2786ac062da776b05b53ac2f693f774523875074dd48dbdffc2344949be0d53be72cad2bfa67f366bbffd08c5632cfb2018b45ac8963e5a4509fd1c6ad521925d8c9fec45fbb623a1f5865f45f2898b094314a3daea743cf26b89db4c8834fadeaf97d554b4c59476900e05b8004a9e4e7af6462e3288fcbf95483a0501f4e429da6f204602ea34352947f73a6cd116984fc838785ebd04201add166083224fe05328e2b92095110fa413e5efbc25be4c60e923194509e4d0eee117e7f03e2f7e95b9b57ab42f4048e58259a6f0a42aa0da801467d37aa4d9f4a0476cc13f4e83b927a8e79b964c0c9d365857e6696ab0875122d76ff19c91e7913422dde95ac4df4b77cad255f03b8800f52118f3c9b353e20249a7a84cf57586ca683be864e415c7c6d1d8fd986d8c457697a25527a2b2788a92760a6323aaed17a28d430dd36f821c223e3a55930af520d3099416ae855d3208e1e866d838be2042e3c698358117c3615565c87c15db66279ce7caf09eb77efed018f4325e83894c6aa1669f751dc620367966cff6f637e1bb3d1236a5e89120b624359893f803a9174f0ee05e48686d8483dfa053656febbe46a90eefdcb86c7b4f0d7fd45d03453149eda115c96dcb2c11a0f9ad2ef37ff31ec1c44ae378e13f91e507c460135821fbf6e4916fcda7338124af557a1d67634117a6bbfa240028cd9bb200643621847e894d6be51d9c2a198639b0dde1352153f4bb5fd4552ce0299d4a03d74cdf2531e2fc4bdb2754afae3f89e0c1b07877685fde4ca5c3e5e753a5afd74520f40439bc317529ef82fcf9521fb1207b3b70e8a034c72621747a9a7ab8f63fc1d0a22e126f2a11b70f530bee92e2b6457ec53156b6732c79e260e39c97745d8d6b4889d993510a6ee4b58b44057240b1d41dc2ae65cf25360c3999581b5d232350a1a66ba22530ff2df71bbe8b8a8412578ccf3a0d6e0c52321dacd66232ddc8162ab886ee0c05c09b28bea2eb4bed9c1b774dcfad0e1acf4a03b0e47bf2e4d002ae00d12eb8edd8ee861f11700d929370aa9e33bcfa9d7426b5cc507b7ce23c2d8b5e36f53f1af62b82aadcb6d1e5dcd55c3e26077f10548e54a35db0d73cb67a9770c20c427d9998277fab088c76bcdaf0a10adb1b559bfc503467a2074539767181035f096ed6e265a9faa4cef10a1b962b29878b513fb29bfdb127177723d40c0ed0fbeac6241abe0f9723813a8527c06f64e911bfff58e58b47ea35eb5622ef18642b0af9a27d5daecf26238836caf8a9d62c9f31f78a905b1a81d6bfa1d363c2cf0cbc9dc6e47a7e67b05dbe223f858fe938aa8e3303e5cb2fa90fbdbd58535b796ab57e6ef534e5f1f032be1903437e2ac35a1a08f314df77896f6dd2c0e5f1671c86e4b42ba562d7f8d162140c90882b68ada83874149cb2ef3202ff7c956bd338f959c52368d71223427ef9149861b709f95d0d76f4dfa9c43286e8b1ea760b5dff3f91e10dc929b1f2ba95cda0b49ddb855c1ae807f0b49446bdb941d9f6b65f3a5554764fbd728b0c9d4135eb9753b3309c278933d4571a587fb856ce1f75800692237809b1f05ea28fd2ac055e44e36396c0d07400d9841a37acfc89bf9d6abe34aea356bdaaa9d3081e0686c78b5d619f171fcd2718da71e8d396fc146a85b8bd216b948405cf2e9c9855fe515ce7c347909f030ca79b17f1712f01d71ac7b5b1cd4270972ac4aea2b41f03f01b037af70f248bfe0fd4af8b48f7e59ea2c9b2ca3446b24a60f124f8022b6afd643b62ac02fd074450af467474627017c37ef1ba69a49a2a0316b7ccab0eb4d9721ec76329838e0fce75b3a1161c8bc0d823942a8f007c4809557d6e60d79bc1464d72d35674a17170d6434d4379f57cf37452e8ca282b27056ab1ae22e6329bed7f270810ff6679e7fe3bccf1d87fcb608bfc33eda65c4a352d33e8fb54115e73243cc72efbc795f970dba3ed8c76aac71d903c39c750d9993f63eb4e65eea5cd68758c69ba53e8be38265cbc4854b2caf2a049b8ae522f935aac3945d6ed01c1e74fb0d0618b7a65dfb54b8c37d009386917373964d23a20f498496e92ee89017a7f24236ac4ddbdd2afdb17a190d0e0b1b9085fc841d26e326f18483abd5118a8434ff6d3ccf19e8f70391f5ee0616cee48f6cd2f73ffa53ec842a3f6dc782d0974e9423f418e399fb8eb28ace6e3f30413f34b62c318ce30ca43878559a13f093af919df7a57789371cf4670d2b41cdc61cff739031daa59dd3f7114afa8d40f2550e6ae4b8480e923dc0fa6fbe37c7fa8405af825b95795db154b3d0595d5c475fba6d8f8c9a1e54810e4085f9a43f186c239a00d917102b9f687d629fe20c7760ae9211160241c81a83b677c6c6d6759a1a14a28b0d9b7cc997cc81bc4a7662e79e65ad269668ab1903cb716027fc954e12e4ae435d9b4466170206ba30c772a212c3396f70053e1617b190af00e37b77c60b1f1e072f0b8396b06385509369a7a5ff637590f08faeecc2d93e2fc3f3a4a75d2db99e5a1bb6e21294bc5debcda11318cd0d9c6bb2cfe6cb87272c2fe572ea8aee8a14947aa2591a6783902add1314c8bd488eefc6fe64a529647045a40999ad67ecf31e6a88365304e70da561bcd11e308b8c59b582c5d6ac78daa3e3890c193bae070939f49e4ef41021e3b1db04f59db1901a041f9a754257dde9bcf426d5beb7dfd1e50755503e64182cfd2216844cb824a7095521bda028f864f59e9036e30cea4f5c1848114da06c03b42a7c5116bda6ff9203e087f84c84dae124cc288c3e83cc0df2d575833bc1623f5ca89d597ab22f8e222d7643c284753c5b02268bc17807c6bc3c58638dfba1a511ab69b4e08870cf1bcfef42aae899b26d16f67cc787f0857552ffe03dea83befbfeb18644bdd63aedebc6d2e754e6e31d87f820cf0ae2c453b1044fede4703fc31b0e703bcd9286a270ad71d817add403821c11838de0a7b8a31f59cd58026e37a40b8fdea0dc6b154d896773615cc03b484114864e134e7aeb2e9a0dc0feeb531449dd35c2e4efa3d049e3620b2d2304bf8480c6af21d801e928a5537fd85762ec8da154a955d60aa24de2baa3f394b4275e0525c1935fe061a52246b0946fc95cc8bd7142ba3da9c7d26de88f458f97845046d141f50ef4e8de2d2218a163985441636d536a551f329d45cb1a961e17a9b82fd7167b01d94e9a88a5a242c2fdc77db28b47e8649759994dfaacc064e89e31983c4c3153b2c052b6af64fbd924fe0d49854730a16c5f32efc6fdd57b6b0d6924bfdd05e35878f9db00ac792194d59aaa60cb2df788ff5127ebf26253ac331eb75802c9506b5f544e34c4dd8843f843f03a961640cb2e57ecee7f6812ebe575342b7f28ae21d6a4c93a7849300ad021c37c2962c18c2bf10099408339b886a86d2e0daa94239eb52970004c730a20d0389b5edbf8f6f25a96f7033f457ef63e004a2712cca6e1d478c668acd415b26faf13fd915cd32c31a410f2567654ae55aac00c354fd4c4d192046120d5d992cce45f9b442399d77862092688b02f22d61d8353a61975fe6261c97b4ae4dd0ba59db2065a0913719413708d690588dee77d02ec2370dfebde0037494c38ac1fa1b613548d207fe262c082bb7ee45f8b5a25ad708bdfc775fc919d004ef7d0a98c036e539b26ed2de66e7d71b5c75708abd0320895bb7b3f182d060f1dcbebbd72acc409e183f72a7b78c9212e67d81f5e74adbbf7ea1ae639a47fe64b3589741b291450f57a3c6050e1007e51f333798384c586026cd8cb915cc1fdae4e01437b141b3d4387feaddaf5697709a1b04b99ba1c64262819552855ba4c590edf8ea3ab255c5ff8e825a5a1e791e703fbcecb7e51acd4ddc4961b6af9acffed89a0fa3b4b860301eb9742c076c93173cdcb2a8851b46a0b3906b6cc7ff56428a6f651e98b72a0330730779ec545d4b023da01d9c0f504c2ea4adec4a22a11342ba152f41c72be26c2f9014cb405599a7eb544bb55376b1cf06267cf70759aa5cd6ededcfeb46b78d9bf1fec00398cea41a10ce083a5620aa6fc1363f32fc5e088a168fb34eae3fed7a066e6474a2ed16b2ef44ec642e52f7ade662664ca25d9a2229d05c8d4fc09c8bb5d5c395b9fcc339cd0f6e5abc84194e5da469ffe78880837ec8c867e6545871aea0af2610bc1bface92ab774755d90b1529895ad139963149aa599943f970b1ccfe818aff81565c19ac9465afe4d87cda9ec373d6a90afada9b899c25dd36f40df91af0520a1d24620ac8e5400baf4ae71b54d4f9ed9d3e11b4ba0ddd66e7cbab3bbad7752213492a7986f211ba3d4958f23fcf0876965f92081124cfa6228b488106b7a1e612f484b3bdb327f3220409a03b7bfe125b6c6e5f267d6e5f49c8e3eb97bd15f8d89bf6af97c6cb5249273daa36f8efbc6e82efbe15f4485ca68a6b1f599706e55c0088916e529c47345e768669756819b734dbc64a347f4220b59b3d17a780d6fc486c5700effba3409deb9c3127be74581844361ba79cd102b0ca11662f15b301b7a092fba1b6a9d2cb7debcc95683458185ac960f5e359acf538f7fd50ae5680d97dd13c74aab4c52c3056a432ecbc7b047985e91e667db2b21fff00b7fdc5a747cac47d90d6789925bda74aae338553d83bde96dab8f016022c459ab25a090d60fcbe203d5e716f9fe6b717a6f32eb6d26a4e29a09923000dd9cba2704f2977bd26c6e88c740378d379f10db653b8c4a0fd2aca55c00df6d627b346cea3e6d4f6fcbe54ad3357975ab27f2a338c5a2d49f1eeb8adea51d53a71a7b411487870b8bb68c333c2606d838186de76c52d114f45aa4d0b59820aa9ece5a635be4cc4b875c3e1f6a3a5c6529a8b0cff33d48f33a0e388f1c760c8a7d16311099bf1effcdb45212ab6b49b9a637b978d372d3eec5aae920e0aebf23012cbe129268ca10579e66c604513f6b2d2dcabd5c8f8cc476df8b62466330cc77d118aacdd13118ef75a8c90752a4c2b91c1c989f02324158db93837633bf0c2f98f8d4d2ccc799a966edd14b3eb4ed7a3c4305847e70e907773dbc4f8179d223b7b37ba5a6623b4556d1a82dc50f4e055373e59523c7ab70e5c857f5916f0c6370e59c04e87dafa998574a16c2c8abd19bba9b5ddd3c04d30512509bb5bd4f8dd2f0cd3b1c0df98abdabb0b6aab9c0485f8a624764e8aee7942102242bccd73a10fdb379081df1d1b29b71f493d7bff9e92fde2ed8ec7a9e99ec4914be953d244168c310732ddd36e090e22cd180f2145f8dfca72e53305d69d64f2343056ff83cac718b52e1d8d26bdfa7c6d1ed870cb006d6e66c76cdcd0fce7ae4b5e9a1264068db8061429ca0d0c5d9031a2658bbb5fb4cd98363823d59e19dad62bbc1cf55f1748e54b7ccc2065888d2bd002d757e2cf0616b5ad2f52c33c469b0fd1ea310a0272dd5cb19f0f6c80c663cb7bd5286cb645b39bec6efa893070bcce4166a3637ffee162cadbf388e3fa3c76d4819fa211fa20283a5dba5e3434ff8033f8e46f92f3282ec1eadadb96adda1b7b771fa6ace7858af6bed5ff3be4822b792c9bc13572f0c4936dd5867abb256e61a10b39f80e91fd50d7984e5cc380d582c8bfce0124c70652a9b0193a1441a409303b808e0e315eeb8da18dddb0d746ccd380609dc39df5ed7929a9ec63182483c7dc91429ac31d56bacda4792493b7a3052305f18ea10ba069201b3688a235bc21b886bdae916f5e06bdc812d3030a6469622a798464da8121d1e9b9cf208567ef003293942ae154bac2e5ccaaaee77803b2aa71f41bb7750b71f6c67c42290b527a540cfbe816b4e3e0039505c1d8be963bbb52dde59554e2bfffcfb4217f4ac4bb33ac377c1ee292521f0ded283a9f4b0bc760ce7a225fa4b86c131a692d3638abf96a386492fdda0d06ad780c3ea70642de00d6fb64bdeb67c77599c026518849df3a5de0bc483f27506101501d814434ba33f65517708a2ac6d51e927b4b1c19432a7278a267fa5ad4ee81b427208f0cd00b120b294eb721393fa1b5b21290977cda76a61bfcbe1e7bf669cd95dd48b3bc3b2faff91829309e5df3fe9a9805c5ff84ca5509af64be2eb1ae182805f456db030a216042ed3e8b47d1d8f24494b3a097ee9c31b7b61c02d1d4bb27ffefc85e7d216050ce50754df2d3be5de4231ec162d53a74483da53142eb0f8cf1631f80c528fcff9a091bd54c0e38d55214ff02bce8f88bcaaf2103cf634e45ffd5818236f7c6e9ff6b766bdc5574fbb8c0981a12c5010f99287c430e00e2ac10029a725f46bb2f3fc516ef2526af94895676bb7fa58ef7f5e9a6f9e0d28ec70e2b93562dc999733130bfa15bff7413b6244683229dcc3e2c5bfa85703dec74d946f403c5ecbd8ac2a3017bc372a534452430353d114a71236b5ceac54eb7bd8d60e2b654b0237cb86bc3c51f24e105d2c7b1705ca75c36f26fd845713f1eb799040f438e5b3f0fa84152a9bffb9d83c8fd715c6c09e16efa74c81b5ccd1dad23005f82c6179fcb36e76fe0fbbffb9074d7c04999be1e675bccb01d6d16f249d236e0970b59711f909cd8368f42ea81769bda1dc103a489f31dcfca4f5afd432741aed220ef1e6f2635d9ceb90ec28864d84586e8026c9f96bf7c1a87de6ad07f3fc2b7a84a3b68640d756f664d0dcfd314909fa1fbd3c6f6e28bf4f266681f6094248d011193c1b9f1a4ac4cbd2e33ec3c3fb28cae0ae3dff493776629b33c0c78d778abdcf1c0efb4b8d3ede9686cd0ef333f62eb5d9ea04c2e65b2a6b1445fdfa8ada285eb113aaf9baa7750e48501f44a96f313ff023abfca548ae5d75dc70909a8c6f97935c5c9b778c03c2eb264d13d4ec359ba5e3d43435a9f65b9a77b7e9541159844210fe7919d096735cbc09507e7b05f16613a29a512714bb9004460beb6b8ee7de87bb07041ecd4b3fd8f3e5a794b26be53c81eabccb69851ce5ab40fe817274445cd5fc61b01ac6656de9855516d7b0cb9af56e289f6e5e04c12e755a91e04bdc4350eb46cc71e996ba9151e56e9dc84d93b83c23ec4e5862a347cbd094111173780a5a6dcd30e55c77acda1d1492a2896e3b156f54f9c93d19e8cd90ecfc118589817921629f04933b9888956cdd806cc09c0dec3ae418f143a970e1f211bb42e6efdf2b88a44a324277709b13578e46bdb4e8b6ed72d4d12e013a9c2cceec22a0d7f7995ba7e5b0f525e76c6831748161b9596eec6936dfeda6f004f160abcbb8f874adcf58e3605d2701e477318f1dbbeeb08af4e4f68c165cf9014f94605c8d81acf3a296cc50307fb349c0956981dfa7d02e5435e1fae4be0efb11c311be475623d8233f1f6fc2c2c0356a84622d7901ba5bdf8f933f8af5ce49b6437dafd19b9df3ac5c789e125ab3eb1c7d82160f4dc34cd20da2d61bb5310913c33da7651d1fb177f705b96689a6d9a2870b433fecd2c143864f89d282d120a45f24e304902124e799632130e655e58245b3b22fa59d61bc59947a6fabb7e59b54d2bf532011bd644c7be0b4572dc9f310088c4d51f0dcb4152db63b33dda12cc1ba345dc7ad44c4b64ce3bde7f3c046f361d88e96968dd597745fed507137f5bec4c85bd8e02bdc8dfebe9e19f3d015fac379427757857de74187042a7f65cf2682336c80871db4173b1e264a4628705946baa8a4f9b294e4364116f0ceb01a6eb62023b1c62d525d8cdc7de45baf5cc1d8186f0ba34ee1760e2d0827287e35ded18ff657834b8b93bc7f2b8f2796594ac7eeb5dae70cb02e729ec1e341d8809d3b44c0ec30699e7cb90a998d785442a07cba0bc1d011c76070d44f950768f1ae5c138322d93ed82e07b8a47c01108a2ed702e59939a950ca058bfaeb926be644d0c2cf7953ceff7fb2b05f12ad1a020b4b6ba303350520bf7b69155ec39a8aff01c123ee2aa44ab5141c2bd6fedac50ac894169c95ff8f7fff47cf0d7587dccc78e19234a83f5580fc7ab8dcb62e61ca68283c74d47dd26728cdeb2a97d40bedd6b31bac7ed36bd42680cd81bb8ca92e03598b1e0af6505b7071b3d45825996a7dce73d225a66419b40766940ec1001634ab8b0a1ccc24861d0c46180fc903b3d730f77b49452fcd7cba64fde21575364a77bfd2e46bafeb070dd76c47e3d4df27bd2913c097ee7eb28fd8d9b87b32b246de05507533c178fbda6a1968bffa12d48c5bc9ec762d89b30c2c634d048560d4f4db6427a0e0e259c2ed7184386c5789fa8499bddfaf6dc507a2745f02a168ac95b1aa561511bf19eee012af91922d87fcff6f259df58f1d8d33718be61bac3cd09474b7af5bd56144fe04ebef8546770cd8e130e42c22c8e418b2d6932e60b4d2041941a9bd85b50e5c3744f386f3e4393cf310bb6ff65b279db43996b642ef8a148aef8e784165a03bf9eafa32ce74e1d165d02ed85ba438c0a3548542a709d3a372c3cd0116448509d76945589378d9e7601ea52d5646131808890883e09c071a385c33fcbab10aa53c353f51684ed60979b828aa90d729e0ac9fe34f6ae7934e61c45d7986376ba3e20ff0bde5c9edb7548d8dbd611efaa8646874767b8d57102730fabc24208146dec225edbf7edb245de24e3e0175a3e82b81097dd5d989d6d2abe9e7036c1e3ab2b637597d68f2248fc2a4853068c02a9acfe081a79179b91a758f7cffe112dd908c9417e88c6f9334539ee38594b8ad2f4351edb8a65719a755b06074946661a8db174faeadc3d30870ee6e2dcd645ba776b0821a67d6fc0ecc9b530e06d7e139bfbf20e770f533fd994e43b3621840d833eef35b0acbb6dfc365b1039562fe15d8d2cdd636ccd3c9ef5895a74ecc7336c3f3a7677b27ff694d2f2ebc2645052d0c4034ca4770a58586655888da9a9847df09f496e602f7a325c7605f6962d19e0fc1536c1e6d61b631320a648a48bf4e72bd01dc351e9bf07d4d52b20126bf848c817393f82806db4c0191a8c7061f9db25c62a91904e31cbde0a1f217eac1aa3887846bf05aa1da69ed7a71811f9592e6d8f91a090de96bcfde0412c41ded480f2ad75afbe60fe4195716b1bea726173d52861da12842f25e69f482ec84f0b427513c024475e02172ba139fbc1b5582c4204fbf37694405fe8f7d44867baa5544d8732a6f282cbcf0c38d51b5efbff2479826ab34cd8761fc4c61e82ebda344b7b2371f940ae5ee79bbdf162c4d88ed4e33924e24f9239b3d913d9ea5b2e184f67855b247c39020734e62be20cf61ab9c94612a3c74eedef6213a6cb44212360f4848e0f8097ab055bd0d7eaaa215c01b7465e1451f06cba18c17c5c38565ca4a8e6dedccc2eb00423cac3316f41bbbb3e87b2f78b4cd8e02d6d1442b77e8b3db93ce5311f1785041dd8997f93bf51a9ea3829b6977c92a580cc7b5087b89e6a839218eecda92d67aec4560ca28daf08139f1f257da6ba6f535d20c9344d8ebc98e121915c78b317befa485f93ca60dcd6b4f0582eeb68dfc5aaeb394545a4f45324ecd114af8086d2cba0eb98e62150647cd3150496d66c061257fcf6f110d1251909579d0187eb6f114e05bc1c8aa38e6d4cc2a3e109f3c7dae6cf2537c8db7ec7b56aa257dbe93abc8cb120547bf414e83f748bbd1ba28e74b3891254353e66d607a66eeffb7ba6e23bb00e8f9bbc7bd982f4bdf9c5ea40f2bfbf17988e20a7bd76caf3dfc1332ffec7283649f8311618912d70298658e55962101e7151a2a4cce856e45b1ac7105497d3974c7a3e56933e8edb288511baec66d6a7c44f93914d3ea7a29f08cdd3218c475923366ff553d358c45c05cf37875a53231eee0c6e38c5d2e4ee51b8d8a6a229deba7c96d359f515226125f58e7faa39dd9dd06fb9eb4349be81b44bac4dc9eeb5bc33d58249b4bf35bb7bae5abbf66cabaf176dcb41f3fb006b24e93e69f50e0d76c10d90df1db6e26f80f9a5dca04db7fbdcc2d41dc526e8bd82757cf73b130ce7b83ea53303dca319870238a0d5222a773f0ac126c23e7c12090b9e7de06afaa2250d8169f4bb9c01030fbae2c0a8030d1624ebbf78fa7991411c428c1ec8ae3d3bdc9296d9ab33c5f06c91d3a42567bb34298a6e8549330ebf5e8409dd8786cb29673b74513278c1c64607f191689e190391b30f1e361a3caa88677902de631f00d20f548ee4a7942f88f0dc0f31de01f3eb3445831cd7a1fb43d2063834b16547dc8ee0462daa075acdef8aa7d06d6e618e488c426156c2bb973a93dd2fb8ce992136bac7a4b85f806441e784ae6463dfad413d867923fd51a895e321bb128df6b124796082056e2d7166dfbbbac2358bdd486c6cbe4e699a6461abd987188e7caa330f638faf630301a672430088fb6c540043b4682aeecc9d0cf811a53b00ef832552dbcf231035b97f267b93612aa883c950929504601b09de66cef8046f9062152083431b2dcf2aa140db4e0d73a9d4883d5f30bd4726f765e7992224d29b6c31bb55b6f542a03bb17384a26b82122c5437be67cece3b68895d6aed7e6bcc67c315bb36909139cadfaaf95d7c1c8ea9cd930deb71b90b2953251f5a91d6de8d7c69837006241b735fd8a2d7fec9749475b548db0c5abbd044b095a9e1d75e0bbfdbae460350d0f972795528250aa6437ec690ac25f5450cf42d2108f96f67278f45db457516ed7dfcc7a98099220b8f5d11e7a85e0e005b06fd46441b5dcb09284092505b82131c43866f62faf7bf996005c78adde48ec838cfd005bec91f1e772306c84364193b45e1cfb5d4ff5619c7251ade63690e37721d88cc1a06456071ff59d3bd7c0c2d705837acbcec615c7124f0d91414d75c389ccb3c75b3b6a8c20e81aab629b4ab28a0c16d6d7f05c531e90270f216cab90555258a5a2452155d09949b20f25e20bdc8adf09597799d3f7a26fca06aff3a4be9aa283ca35d2a6212031736d9afeba39f355974b345cca8109fd2b279f610aaea1d211458f08f6b201347dd7d74101d4bf2bc713c01ee6a52139a4e5ecd95b986eae52a6b6c76c92e28c1660bae9875155dfc0fd35269e83242bb37d554c42da2ed446b22fd0755ebbd9e675d4ecf7d35bc1a15abbe6270f280bef83aa3bd2ea0f7a357ca1889b126b2532706b1d1952cc1f2c3d369f8166b5fb49cbf64ada0099db7a3c3ee00e99db35f3b3af89bf8643869d0eef5b6a29c36b41f24fcc95e237ba171b0008d179abb0d7179f8123af1874b736108e9e74dc1a075a2c73356a37904d5114e1376086088ea6c14e4aada9501dbf5ef87bc6a0ff8b4e1f57b1b0f9b2517bdc0e35a8472e6eb424deee78661deddc55077305ca8be139fe8aff4667032f29a99bca8d29bd8edf24681a777738948211426b2ae10bd40b3b9884ea9765c5b07051cb30cdba6c9839aca0f0073a883d9846f1fe13bb9c44bb78d5d54388849ac7b8c8e86792781ce8ad3787fb9faf9da01b5dac75ca38abf1c8468db670de9650d0eb925e585bc277a2e69b9f7bc31d500ae0a2bf348be0368d1726dc8999dccaa1eaba4d801765e115deed78957df8554225b59df585aba0a6d3be5b996ed1596e9aaf97cd73e06b91e5edfbb7282989dde26eebd16cca81173b3e2a76e499af64a0f09cfa9e628d7c9f27356588c66f389fba760416e0914e6bc9a0d905490165650e6a24f2fbda75ca1c23e9f56deffa5da365ffca31042f26f55a069cf8e9530530d35435e2fc8315b1772839648f80927982e5f685ef53125d3041902355191aec1bc3a1d1fcf931831c93b05d4da77bbe13f40c0115e486988e84603e36ba83f7f5dc7cb4768bb2e71b1e7b3d48fecbeb5240176e334f3b4de44fb45b759b6942dd26d837bc7e20dd277b6ecdf2f324039add1f0f7e87167bfe3d4b8704bdfef0a07c78dc61b8e34d93477088abef41976f2f4c963ba3190ee088b9073ad29f21b3ceb9b4e036a7469cf9ecd7de23e9871c520d70f0b3b8e1c5cf6c2ad69af016ba33591dfb80b137bb7a90db035de1f2bae39f672f2599b8fa9077c6a1a4cb2f94d3bef67ddb7a0533b34bf96fe258344d648bd37cb1d0bb3b746c6b9a5165e647cea692bc0ba697ffbf908d8e795dcba2a3d6b8f24555720cafaef0fbd0050e823d5205a2d47597d614d8872816fe92af584cd7b0e0e99a986835d66fefd4c89ec3f713e8e40c59ba484fa50eef0b65660f5f459307d9331e11c1ee17b4f6f9a32f0202ad2a15a0266363b63bf8ca0e0b4716f9b03ca89d451762722dd6e4bc4fe2456ac3b3af017b8d76b18b5202393ce635f652e5d0117af85a0beddc7bead220dd4b46d57244ad5af2d9dbe1e5f9e965abe65c9c59d24728c059ee07c989131ab9daeabe059452579245f0c839b0a485cc0911e62eaa0ea7610e7c83fc3a0758f69fdc92e551f2274883a05d802f2dd92bce83a64be64eaa28cb3d00d6ab6d5a27b8274afdad77b769845d26deb97ce840ffb2ce6ac7594042991d6deb86e05130e25b79af01e1bc7938d30090c65eef5c0ca1ffe95963f85eb2ae0c7655e8e3616f86a0c904b137ada062b69bed8a31bc34325fc0652e577f53dc3928bdbc98eeca3a1ef56d802ebeb0606b7223c9827e262bc2143a0c362e828afa014f6d3397b7488005c4905c612f6ede29ebe2afd82e38d30d09458d11a7a2cd4e18cf9c6916e078e5f15fa3f5c9311d5963d7c14ab0ca28030ef9a0831973713e70af9719b01fedf6ba77d6404bbf693f28a661132c27182d3c83dbcf80855317ce45dbd504889095a326abd1f95925e5c7933a679bbe9d1d82aa2cb91901390a56a30ff078ef79a9376a16f15455636c05e4cbae944b6c5266c0708e9d74b20aa6a523bb2fa2ddfb0d5cc4a416c15858a1edc6c2f303e8d8088f8f1c103f11f59e63b9ba69f5e334da650d7fbd6a19d978eed24194ccbab53493cdf6effa0783011587455c6fa3562d4d25820edbe15860c4cba6fa3a515b0d041e8e7e67d704f54731f1fee2286f972895909d3686370e99b065cd32f4cb87624a501f3eea837e787eb3a28796799a1a10269145cdc69521aadda5b20a41999ef934781a8d2c5f8f2717b4bcfb91a1e7d8c757a7e8452faece83dfb8b2c2172523e5f8b16dc24056f73c7cace109a1482d580dca72420271c8d08375db4924aa08da86fea3a93347ed88ba578a3c7cddd008180479b149d10940cde93b8ddae3354d77c5823a28343a4d1df60374d2057d1c74254cade407548f70af2af000107b0e763fafb28db71704a4310d3bde2e33276dd545bafe5da59a43b2836b4bfdb31e6bad2820c77aa59f4746dd4332283dca46fb0e32de17814c317f1ef2dd2a084b3374588a189013f63b6569c73027f0e39daf4439886499528fcc653dc103de5e737edc1743a0a6504546a85934c3e3ffcf3315092473eecb124295779a055207ab4da87b1e356b95f1d38a0855ba80feec1584d7818a8d336894b6084e38ef57c9bceb64b161b43996e055f2c3f8c330b823b360000ba563295705d794a57583411ce848af3ed52b7e2e9cc3ec5ef22cd37288cddd58a2603d8bf2e893e6482463cf280acffcefc9de76d6f04ab7f8b017f46723bc68c4a043f1dd928e8429911e57b904770e156409747ef45eda8223ba9242c38a0bcd57e45900c0e271277b4f4ca6cb53835d6535c20bf971f877e40df17053ebd6d98adc0859d139426a5bc5aa3b9a52bdaac37c4df454048b6cecea8eef0c292522c1a4b484dfd2d6efc908d974ae2091328e7eb829686c6573034cb12819df6bbf0ddd4bd7b15a4e0eb5d6ababc4641e7a210a4454cbea2ca4ba42bf6bb5efc0d476cd4736838615df83b8fa2dfa1d54ed7c0df3b4842540dfe633597764acd23e0c5f5d8b750d181ec2fafc88c543e5040fcd6184225f37e0af621139ef079de79f4037e13ac00b19d071f29d65b3f09aafd4e230f6712bd99eaad191946adc7180a76a0e7bfa4fb47ce09008a39f7a693c77e6e4da55468e85537280984900e5db1b7a7d4a2ab73c76473c79cd6e8bc405a6862a98c2d18b4eba2b212c8a8b20d4cd03b9b721c6dd095ae2ba49599de6726033015a5ef1de477d6ca073f45419f24c6036b0153848b1eb63955d2766ddcb56673539ce442a3e82e9b4d2f30c1e7ba7afdac91abf7cee2521af6b1e02c8e2624fbee7106518d0a5c745b8767b7de10131a78c19bbd00c897533d38a327dd7eaddb6660e51f9af4fc4575c9b45e3864202ed1f476c9390ce106c19959bd8e2aea0f3fecf1f9b50f0ea26dad4a5778bc235d5ca6628ad26824e699adf9ee133358f753762cb10a26f7d809081d178ead2371cfad407afb48fcab1534a33cced7a4f8d51ba6bd429846a6260f6754b0230d1190529330f5e473c71277e0a03c913de65b48ac3fb31191a3006904ecf3b972e9410f6d96328406a3831318fb28e38a005b4f8a128e4f67faa32230f5e92adf4088f0b4c03fdd4413fb92f8f6a8a6240ee6b4f6b9f213b9745908285134a8e51158a6f7f7ace71b83938593a57c998c08c490c4b3352db25abb750a5e3a7cfe1d3f0a9ecde2c23cfa980a86c7bb4f561b1f3a1a19c21067d9c70865779ea2851850c223f54deb2bd2e1ab0723b52adf92bb6a7d2b347e0f8f07253e8575dc4c924d6c1c9358f183d31f6990c15cebbefe77818aef4d48b96234a8c068eff086c8648860e32e8094dc733e431309d4d60960b90f02225e5c9ff5d56d965890b44998369b964965a554d9e8a74738a91261319fffcc0356536a3968b6434aa616354387505dc98c4d78b85d93b968285cf1393c7ee0ab2a06a3df86d09c3f3b72b0fe31ad039be1f79d4a19f8456a8390b96d8eacd3f2b3d66d7236b14ed6d0a8dc704906ba339dcd90b5c300cf77b405a4abdd7f3cce8068b2fabddbd277c3fd49713ce848122dc1c17387dd4a8d93acd7a6df04aa30a4473bd920f9ea901d8df0ff1affbe0cce080840b82330ff86f647c1134cf6b30fddcc69c45c501b1b1256598e4c11c0efcd34d82eab6e1a094c4662079f0909e48085d5d92d5e4e8e6b648d531fc417b4ba0db1969e68f6aa8775cf242dae650fe9d368dfbcfdb2ff16ba97a0cd0cbd202ee48b3e72c30091b6081da219e9396500126c3d2d5df49ebe51f588bc1e9b97daf0ace0444c32427ff20b9f7848e4f0ec7d038e243d607ef276f0968c7d3a083eff46680a81d3b6e6e1ab1c58e4703ac494f8e620005e194f7bec409aaa1c1034931c971d80c7a5c39b98f103ca8078668352d7003c6ac93b91c3ff9fffbda15783d9f6aaf3a4db0ce4f6193b58d1aba45c7d1f7da9c414b218d39a2c7ea4f5ff560ec5b6774935d9492d10d61e2ffdfdfd0901a20510b03fea8c125b4577c1b89c2c5a142496c427bf8abfbc531a558d6453b25246fd429b6964205a0ed9211b801f9597a43374167567a1a896d3fef7cd4dd9b3945216e0f1830e56c9634077889a0bd7cf913a484c600e557ec80be7f048961e59968c7e2b39c5e352245778323e51dac6ef665e1ea245565cfcbd4d6dfb5f365e87342fa40cf94ce8dc023c8b327e05c867e3630b0f1fb17030ae66e4e2178d96e9c3161084b1ec583f48034398052c1b4066e3e68c469b284c0cc75a4c760bcc41db0fd4abf5767e6bee5123d3eb3f8812b96922196ea68b9ae5be911158a2254b32e41ef2cfc59e3cae2bc8980994a1d706206e1408f2db0f560c6a8020ab306beb6060fdf7c0f75ca4a248645659a3a8eaeaeb74981ff68a048daad4a3df27802659c76966d3de6e84cd3334bf605dbd61a5f8e40d6c75e63218fdc5baae16408e10f7921ab3f2e0189441fe04a78cf4c4de19acd1a6780263da5620257a5cda202d357c61304df4587ff897f25c9a198f7d8e70574303cc06e210698775944da4b2a75897ef93bfb45d0626e0398b54888efdcaf681adb926a59a48837e7ace728533a3b7b31353ddd77e7a1c916d6688fa882810e619b060498f53c7b95aca4e5298c118208466a571b5b2ede7e65430b779853e9babeb99e30fd3ca0bd75f03213003ce4d79410da9acee5b19df12a98ef4faa6d1677c91cb3184427359e32326e99694eb00e2f4f3dea94384b38e425bfbc989fac099212b422bb4efe6c278d157353410e866d5c369c8452dae8b52c0b99e2cb4e927488e10178ca4c5233735d2f26ba09abca793b9f862d7505bd6fd06e0a072f2ce3bf7f31c6e507ac257fa6331fb698390cb0b16358b0da71d450e4fde73711bf70a66c48d35e705acea5c917a7396631be8b5abc421a0ac17d30a82a38d806b621a1a1405b9755c01dba39951af00028101b805bc4c24f08648c190402e1a9a5a2e8c9c331274d5dd4716db070f986b8ab55d6e34c5d196f892bb614ed7ee1b5de6b5b0a8048c9a8d78dc8486d71d12127ef0903870077bf4fb81af71f4aeadb387505f0174d684006eaa548dd68ce70c79733c7b89b6e3d1008d09b9f1266914ed2962a55610dcaef864b2bdb45009b4c65e1ed6adee3d4132e5886eb88d05c32be2c214031b480da2773bd84d7a430eb205b977eaf4c08992229f3c17c8f1be0005118aa9633a60884d723b65d878fb26a4f7e056f3fa720bb4d3f8694b48041714c402b7846047bee0951f4b2caba241cfee2cfddc4738be4754bc7106b43560cc4d010beb1a40c51d7f198759ea1d1d5b25f2e11801723adb01e4721b3f2bcbabcc266700d9c1ea51d0ce45ec94c328ed5a2487751278adbf137db8a1da4019a767c0ecb232f061d705f94ce271ba7ae95a2eac3e19495c601aff83d788d7995ff39b27f10183c247949e49187a3aedddd06a984c4d7653ef0197c132a09d7697d163a8ce032f0e93096ba544ab5a9b6b0264a0d3da827660582ba453539008b3b99d323fda26cb2df8a3bad03866ac557aa6b59c7154b69b3105ba42925526edfdda3e3ab83d9cd29fcbce92c7f4c7607d8a36b1569adb0105fe0442fce3f7ecfb3c79e11dfa054bc8dc1a13f43f36206d02e0dd7b6140b7f2ce9b5ff18c86bebffa33f01691298599ff4b58570171eece86ed344c8c453ea6dda8971ee4532c0a37064af3ddf857eb6ba19199918b7a5c194dd23f6bdb6c63189d8e9459024871c50ad85b8714de8d6809ff11792e4e22128a3750edbd5d428b2b815a7f68e201bd8513e15afbf6a7d61b0b391090d0333dbedf89faaad4ba1886baf6e3544800b91aefcc35c9d82502ca938e54d599492ef0b6447e797f3db0cd1aa43265d3d4473b9513a8573e528bba26da79b372cd5fb43ae3003ff0247cd0848e17c1506301e755590365290265e9103551b6c653f1eb746a64b53a2ea1bb9c90af8eb3a3a625b6d880c28f10226ef719e7c18292ac4ee2aff386d9b30f02b20c4a6d0dd6274fdce351f35d6f3f1ec7524a9e0def9ed9e7aa9736137a7a2102a1015f84b756beaf99bd73b3610adcf7dbde9aad29c5037636cd4149544d8035a2201561735cca54be448a5ce03e13294d30af531e6644abf595f6dc27109d0fd158e7797fe26ad8b8f778767b540654b31e09d8d3bcef747aa8ca2a8685bf56404c185de62bbe27a99fb759e376ddaca6f0a3a1527981897d965d4f7ddab1c038373bff17c2b08ea6744c4ad291b74efd7c546f88cf6224d6a07d737bc952342bca0febfeba737fb44b2fe548eefd39d9d503d59f25f006e7a332042a3cdc844103ac4d6e89a191da75c978a98a340335750bc0fdffce7c77a5065bd31b305d233a6092d4f6f7dc2111e286461962b39c6b6f552575762da826a6e03acba9f9c6317bdc172e69bceb63fd759b76d7017550f0259d6f8b70dc7ad0ebe4791c78a88d2ead0146c07fa604db0a5c114b5cb78da6c956bffcfa545f35dac2cd1e55256b171bf3ab981ee53b90ec7362270eb8a485267a73739cadcd1971b91e1ebe6d1d790b748e1a80f2c8b63ff9cf2004542ee61c06a1065ecea7ca36f740da306351aaa4cdaef345369f3d1db701934c47e8f92c4695b9da624d3321e49333401d3fbb17f69b0cd3ab1fd1e69212a1221049bfb2454775822fd1cf5339681ab432698b34cc7e8fbbb8e1c842f7df7c0e20bda5471b4c2651af41079a6b047a7e3b6cc5267f5b30136ce7dd2655706f75e76bb474f652c0cdd3c91f44cc9931b35b5f1f4d93d3f7abe461315eff5c13cc36b5d8e700eb31d9d11be9f700587561033fabafb99e39e7f388bd4a0a5aab09041abf1b23a81b5c8bdeab26a192343c5fe587901e728b0a28cb4ae0cbcd317ef8e12c45a429f7d5229cd0bd93f02cf1fe1c1485607aa60a4c059a1c1bd4f6aa4f99b21f91d1641ae5acb6184b7158d0eeab320469bfe6b2064b76cd52dd148aac249bc77ec8f47021afa0ca6f8237ff04cbe6945b5fbcf99d372977e94dfd349dbc4f2fd80f38d0c8c86b363d31fd503f3406abd41039e64d9a68558401d2eb757846dcb15a83f2f9fa24020ac6ed26c764b80eef274a98154101652e4475dd55a2695527a79818f05e2afa4db5fa9f5c04024cd7c3556671f9bb0dcd485fd1a6c222c46e5db506bc273139a1cd4e898aabe055eeb28e33e5e7f5f372f318a3959c9f54a30e1d8b75b6bac95a6b6f91f6a231526407ae9d66c034a9bbb1a5415dfd2853323d2da976b1326b2a374787b3b9738f6a84ccac96e8c3507a6be1602a645a00a5a488f055fdcf352764a053fb547454c6cf2cb90e3fb9c35d3793def0647866ecc956af36c1f38e254a647d42e4fcce8e1c48254fc48783ae824671dd09062b4e48d711fb5ab8ca03593246bd204da4fb1ce5d22f9a0da18acff7d904a0c944aba4d1a881115b813e3b422f6aa250e6775e628887474b2fbccf1bd40987b33111658d359cd5fb594a937e7877aada97a2515f806b5152608e0c9d9df96ec7861d7af0edc2db489edf1c14e52118b76f6dccc5eb677696c8d20600398345b0350f24dca5654ed3f7eb8ff4acbccb54e944bd650ef13dde4a5fc0e1dabd8856c1556ce452df1b14a0cd591638813764c1dc38fdd05c1c95c43be0a13b8aaf02cf3a0d90e40cfdfae88f6aa01c6817324c6c5036d71928708d34a5be8610874cdc2bcae382e7561a68002be26b835fe4983b4619487b2d6f54318f2793f0c2aff3c3a7d45d0d37a2e691e1a3ae93ca57c849bf6dfcbfc2cf64cb20c82daebe2d6a98f170afc9785e55f94735132be38748c6060a2f74a4738a4f7a1e6b1e5a7c0cd50c97d613c24d94a05cc752f96e1899bd899483035a1b0cf48ad0418bb960ef5f664c35dcfc71142f85ab2548995a0930fbd3439a13da9f2e0fbfb7b0ac1a0770701a79929bc0c6c145c37115df7664cacf206a975fe32df213a86a92666623429d96be38fb4cec07e383602739f025405b2fb768e100b6bb5ecf87c4145d87609a5769112bc024cbf394e36017ac84894f159d4accb16fe8241e2d0b8289bdc41ae6670a8b86263b6eebee77f8396a29db7d408d29513b7c114cdae02df5b3a8f244790de1679a5353deaafef002970dd5aa3e1a6dc04be60a123a93a0f5cd43e96693ac00dd305b6304d12a32c48ff8ec7cdf4c8824e2139f2b0137d09ee31743f41cbd4a7f1a02c826b62247b5ba426687a03127f235ba2a2d6f04d9c6debdf7d9154696afa6e75152588528e2f02edc11b8806f29c41b7ba84f751abd0b4e2675ddda8e9f74d8daa47e9804721a792cbcf29e6c04ca0cccb3f3a8f1ea5824d1a2735f1da4520d6fe31280afb9acf0ea495ff7f601de43dbb13a88bc44dfb11a65e107f34cbceb05b6cfcbe64ab728b70d37082f7ef2911860402e1bb085d8403c84457ba2b57bc732c6f36ed462e84973415e422391bf0e000d7c0f9b9ad76756c8c26ebc0d3d50ebe853a5c490c48942c2954e0aa0ce58c1304243a1602c2d8cb3929da678b3837f73dca2472cbaabda2c330d20efeef56cd8c771749d9f9e3978cb4af12e1a14427040a8cf05ea29c6952ac652c58d50378dcee8dad3d3d6d7e706ee243780973f8476e704dea1437662ea00a80ab0aaa0f09a84533d90c1b6f5564e49f45cac7bbf33c4e85a799cfa40fa58fa8b00c10536a61ae6096a25da6337d11d00d003f69a9adef5ef7a19a1f7fc92b1a7e3c7a32f807b5b0ec1f45279f20369d9554e90d2a4fb7fe0bca6a7cdda4b1212d0dc469a460e41e80026198ebedb38655ebb8ae6e1ba2823014d763e9b4a5a2f2847b0e6b4a68861fe517a09670b881c65c8b2d10c9a1b3a253d789fa10c5d7bc7d6f4ad66c91981695babac0114ff74677c958aefd72eb75b75b757a6ebd4adeb62c3b7a793b0a801fb5f6ac0946738380a3b14a08c185303429641a821174ce819d236859657e61fd64ca58ad69c09b2671332a9466d4dcd916bfcf4265f9a2ab7ae420564326f850973a794bacc340f06ae9b3079880f34a79f2f7f7095dfc2530e9f95f5c4fcf8e36ac4432d6d621147b8558f5104c51f3f46632fbb43c602c2daa8cada0d9a80ed90373db03796101692ab2ef49243185ce7a703d2345fb1534f32e028d8418a830fbef9aa1323a2c65ab8b13cd5088bf04e8f746b54f0733e1ba87d1a2d852ef367f3feb7ed5448da1cc4aa6e93a808af96e41a8c52f6cc87dc7ea0bbc7f9d67f258e404ecd9aa00277c917aae330f0305fea58db4977915e5f97d5da2da052c63870c27ccfc759c8cfeac60cb4da3f138ff7db4a5220fe689c309cd1d0f8153e9b18857c883af491ceba796d891b580e895e6b754caa7fd3a401d42d508c1553538bdf9dc2036efc190abd458c8a861d2a5ee651ffb2bc05e4b1378dce48f1b156f52a2a7aa684d4150780b202271129d8591f50fce317bf3947f1b7cc0df3ab60bb0eff65deb63ca7fc362d83471edf11dd6e9c66f5f3dd02f552f8c64326c67c5519c3b9fac1bfb9616c3d0980053fce10fdfdb09871325047bf37d21d0d07e94d37c549acd04dd13d8a7156b1777139e30b85ff7e65a8d250bd8ca87e6df67f5c1a9b81ff6f3a64a8f57e0c4224b01e66b6e7feffb6b8425a047da613bdcf58f8d174addc8cad534f6d7e626c9b8b2da312202eca4c697f90e6b919e5baf95f4cae3fac72dcedc97074cff5425988e673f61df6257956e2171218fc0126686db3cff273a9106c57908f5185afe38d5165ea508821ca3074122aec9342514aa4b354f59d2849a77aba5b5b1c352dc7c4a1e14587992475b378afb4c4d063284f2bfe15c3b96780df2533db3de44176e151ec70e89f4104a111281cacdf5df56641fc7ad7318156278f2c27ad50f37ef417ad50ccc76f3a5a91734a43cbf7ecaabbd3a8dc8a5af8d383463859c2209837024a10df9fed555abd6dab8b1e7a258e594cb57c9e35ce09f9b6e98adebeab48ab5f19e8e1b9b557eb09875317d21a249d82b1fea30fcfc3454cb668f3f1bd6fc8a11c2a414cfbc9d8d124716e4f3b4ac0ad3ddd3b67bf79763486198f44646e5e3ee231631d707673d92b28428f231bc6df7d7005665f17975e16db2ba22ce03fc73b171a75cd6369b85ad7687a87dd2805eb565b7fb34bddb350de285bb3451ef800ac91209360c6290f51466d606b6d65cbaffeb2ef7783aa8031364f27b364607746cf3e7b5603d254a47f15ce8dd521f221137ec3720a3749792165a21a6cb629cf8038440a4bde91fe1d09248653ef626ef9819216b671695c60bb73a66b951578a7ca41c1c35abdf9a71e225ba7cd0350b88fbf330f334c271534b73304348ff679b08ebb28d69b2231a500db9deaf93d7764714ed0eb68f7dd05d46f97383007ef26df4d0002a38141b665ed4df771faf6b15998d3a0f09147b4068a25e9c26e0201d6f20b68c8f95fefb403112095e1ee2aaed61169bfd9e6c7451ebae743ce87976ce540e93d0772963ae36e63f539687ce231f670b91c45db944a71a82f7a569a869b7416e545177d8aa8bb91b21239e2f954469ddee825047d98e97f8619795cefd7b81b75279cd9901c2f6f7764298428a3745dc639b4a2e7d6af6a91afa9c1e61fc6a4057e56950590e8430e5a7d8b1a8d550ff4ff4cb326fa75e16339f5a50fa57b2fff53671e62269aca8cb6e009f63cc78e0c4fe990a475c14446631d5bb38879ec29cb22d31d3957b6fcf3c8e6bc97cf14f96b5d370096b79b698ec0838fcb6fa47d01f51cef76d3f02d020a358bef73d39e88183399efb9444393421546a0445beaa5cfacef766e432d6030ff03c49c7f00143832c8a376067f9db08d73367bbf8ea66e34899141e77b9978461c3cba04079fe6716f08680c6a1fdec79393338c79f801d732e1ca24f11591557b58f5cb1cf7c0f39d8747939b1095ed9f70265ac55d609f5220e2502673316566002b29f83b503eb1de4d32111e5378cf330b32a8ef202dfe68b3b9a125dda76d25d0e77d5ea84aaf4a5d013998e4fd46028233a648a832dc63c403f419a6a0d6db27839872b0a4ae8e2008c2c5bf7383307b76cf490e2e02c32da6c26dac627319a19b4448a2859c54de3cd95c2b782f07a3abad872abed3707806cc6e75d515320bc264edc17c7fc15935e3c7394ef343b6f5645a2fbe887cfefad48415df2f0a694ac6a5f17eb84ec22563b7c468578357897e116c777a235bd30c926a5d1a3c0d3d0641fee1d373a355081759e15e1e55736729e3ae20f454356b6b3f758a87a72b000635aa946473dcafe1c7cfdd27e177286e844a06097ea11fcbc6a07759a6ac3ce726f1a2cfc74717fdd94a6d988b2f920b98d9d58e4eaf5859cc27f37d552b986715b0a1f0381dcd56007bb85f13d50fc16f2ab577d65b07d15e22c13c8c9ce051b2d5fda7567c31f842f55bd4984bcd5bc9b968ca4f0225e1baaf828acc6647d02a6df58712ebbaa0e5650b507eed1d69dca1a8850c412240f885454a2d747e13c961f7c42ef23f80573dddbd301ce7c09634481e5b7fb24c52ac95354f3cd6251882c1698d66b85989015cf13112c0fafd90c00a2763edef9e6e9eec028ed844dec20cf65e251d93d61ac15d8c1a2f939b1e066fd075550b5396d9a68fa3d9d3d3929c96b4bde4e9f0193353bafc8b63f531c476504e599e324f5d663779a4af7740de867b017970bd833543d15bd98bd11bcd40c50f879c4861eeb26bbf240d7833319dc59024f663c1cf0d5753b606e5c99557fd10a5c0e6ce83a9132ecb87aa3c59e8f4c9845dc471642909a1bb6701f7cb78d2e86be295ca17b1b62913ccdecf05189537815ecbfd962b0660efb1d78b1ee25ba82ec1d79ed3cc51fb804ae4983e4135cd370eee087e0bbba6dbce899411368d5a798ffaa9376a7c4f72ed6d3813371f2360b844c01aade75f90308e52c1940b02d45f635d025701d88c393cf4f4a0e891b0dd9d03c08f73be8339034d9a038e2756b9dc50c52f2526743cc9027e15893922bb4bf72619311655ceab8ce33a46f4e8ea56f43ca2d3ec0756471f46ffe970e7aa6e452bd73917b3d693081c4320b5097d391576b8187ebb01697741a41dc69890cd4f54955fe5451eba61ed717ee5639f99cf307aea9658fa976683d44b2e40d3606e3a4eb61285c6b195d2c0f1829fe4080db5b556127c4752c876b97575e0e885732eccc8cff58c4468f4f78a30b327a2702d6ab02f3e6bd4d294a4cb54edd5dd5585a3e8496e4edd1296edd0b2dad07e5d194b468cfccd89627a55c74e1a61145d9ce4c4107ffd72a1bdfd497193556ad5b3c9fab38270e6708017cc9f8d0c7f6c724498ec7b017394c42ce14da1c1f643d869fe8cb0067d47e2605fce4a071fa61c274c5016b9b649126581e4229b43326ae6b0e3d0acc2ac5bf326a1373795f97473a92f4e959f69360b1ba22520880dc5b2cbeb42b135b71f7093278a5e9e16a75758b96aff53c3b79af9be51db959912a2d521ac36d8e5346d70ae7898ba858f7c369c60b6addc5640946c6937c7480b7a6415e7dbe9bc482831a0b3099f588bef3da02413586951fa6d31e229480b9262433ab41c317acf487e7285fdca95e8ee5e9003303b0eb5e0aa2ec060c559c5192f4c6b23c03c9246e89dfbbf842e15510fed538abf9277d7303985ab296b7f0b6b33a2ee4b0ff6f108c3e58e720e6635e27cf8eccb1582e65a9123bc44a6c9520515415fb57083436ee9b7c5696461d3b87fad819f60a9773fff4aef938a4321030b33cd0ba87e16fa810c6325fd865a503cc348646f20aa3339e68db8351fe3e1d5d8c776b6671dc51c9cc094979c8b0c918e6a22f9ff3e3f736e24f64c34c720e41cb9018687ee3c23084993882f52bb970705fc8a529515f0785611f99d26f54553d74eab93d7109413d5e650cdc849bcba15642773ab1abd4c13206453d5d3554407a625b502aa56c2a41bbdeacc55e4d84a71ded4db5f31a72ea8f0207d63a646bc9c6f8f9298f09f5aa6a70dfd10d7f2c896115bd35ad3cb96f0e8cfc00c84cc2318e79b542019632b4c943f51228efd6f35025b8b62bcd4fd48d17cdeded66409fc5b99dfac8d27fe1d7825eac5d3a17cc70b6659f070f2b1962e1be748d927e94921619ef68db45e2b76e84bc1a3923e37dad5e00bc8519bb20966dbc7195f23c2f129734ce18fe26bf842acc2e551e4b698f674f6c9ecfa5eb55e3b91ee7c456df1dd6cd7788c975e03bdca37d53a3d1b229a1e436d9491cbd91087bac34361f4f71d819bd20df40376559ef7fe8c59c13dbe4aaa055707b1e279bae93cb0d166465b3304d22ccf7e9a2dece446d93d2176d01f1ee890a7bd5e0972b775ace24205cf59377c25c512d7f8bf9a6f6fa34e985eef9ffc9971e045ca325c4654f6178f18e601b044cf621c4aad73487117e0e46ad3e71ad4c068db5522b556442a3a81bdf6f5ae700c2615b83a29f40d733eb72619b60eeec8f8beaea90b7355acf741f344a731259681e3180a8483217f9d7887d5829815141bb522438b14f66900a7762928ff57e19732ecf8c02bd089bf77f6b2af9a2fd575b8ddf02596c206546ae45b53ecb551301d3604f43fa3fb22f50a2da6b43a162a02ce4d02c2c9828656b68b8bc3e982fe7270ced5fa3444af17311bd94acd013865290195cd633293fd4bf5618567a52e91bb245670d2d4fa1fab6bc7d3e7be092e342ea5eef30bb07d4b735b755c9ac43df0e305e8a738696dab18b9efa342f71a26a9f1bf6b58f041d7b1b4297ce83d71432fcf12af56cf0c88e9da216105c1f1b9c1a9567faa134d0d51e008850815d80e1e6da7083630df293b0d8d61557441029da453fca66d0788a641811938a3e419ba50ddb7a3e3fc148e996e6198315d6ea38d7ab949725b80ee0fba29cfa7a730d88408e1aaae183958ec7b39d603d6dc0cd28b947ff167484036a02f59de48dc1a613e350f05def4aead64a0d696bf81b0b0d70810c1641b6f0188baf9f433f62ff96b38946cf9901a5700b97d3f647556732e6c6d294ab34b4deb2667c564b1a8a45476184ee4dac29c52208341024a8848bca3e960019f53fed6c481b81b33833da1b9df7f1f6475135483beba51571cc077af16274bd7d8785f22f6af81dc3800f65f839053e970566e8cd9eb1061ea56b09fd74c2b550bbfe783d50e89582be4eae1bfd64cdac1a3d9f5761e4f7d08abb0a1f624ed939d1b1b7c7ed8c47929f0faa82f665171144e12cd8a4e0f213472f5aa4d9ebb51df70c2393a1db8ca66fb24820f9e652c8bbd876d2934beb14365e33f0770bb43b49ae068f554571e9f2148050043f2cbb3b42fd7045ad1cc87b36133f7b1c75afd80b9743e18556dff9b20f73c00f81a839b4a3d1a4a2c8e87312f523bec9986e7adca55d442dc4bcee6238fea2420d0fa026c1c17c1886e1b936f8af7d4b7f890452ce06d5388cfac4e37347a5aa0f12cf9d0464a3d478e8d821c154de02f519f2f0a79a5d1de605fd5a904a5e50ce6d788276a6474c6de64605e095ce6308534701f3a6c4e279c5c63d91dc7a28e011518f52d3bd0853b831748850e013529d3476259f4b5fdfb4cfe4be0b4efbe5c5d9bc92be2b0f66c9e487f480d02ff788ecc8a988c5a2db927d0ca974386f72d3987ae0f486f9629203b7c9c9fee338fa543cf5d84e3fdbf484a0946c43675064579d2978a6e90e57b0e18576ce15b4f4bc64429c41fa03a03e70ea056006633fe78bb198533ad6706123dfe021023ed58e9bbaf445b47dc4a2f3edb07d4d6b9eecaf44c2c719a39f0b5e0b90255831445c8f1dd31c4133b12ba3cc728e51690c38bdfe2fc1eb87b56f823f85fa9bac541e3c88180248f60c4ce08d4c1387604b3e198602fd1f5ad290e70f7f8bdb6dbc1a8551aed93ca0d647398daa50ba9d6e7523a0e2e1486d0ce00d00efb9b68a389b52412b5a6309e4a411452659f960f2b36e50d4146fd29c6663c5b19faf2a72740dd1e9876040f60791019a49b47adc9c0ae5786684964e8cd129525e00da01aa5c5c5bd1802a6fa2ee140ab8fc1693d6f976e530e9cbbb64660ab1da6cc0e4f4d0401579d31ced4cf8d092064fcc16fccfa158b3c5d86bbaec2581485f153976a3f4817a28a50cf8db5a3832f66b474de6be3df003f28159a30a156017ee0621102d952dc7982ceecfa4225b9fe4a7f9c91140221e6a7edaf83f731828926e28ce54a0f6c3fb915110b0f4eab0322b4e07c271c22ce38d2f698574e1934fbfb8c01a8c44b9c35babdb0f3f8e9e0e80a17abfda9812155f4d74e6b63e161ef73a87919b2336f9a322392caf0f656df3f6509b68e47c2fdfa5f267e8c4dbd7b868789820ad1944acedbf3db2b2c55e104b6392a5be71daa006e71f4c1cede93fd43f01f483d84312bd9c494867c86f7d930d977aabe1e39cb6112e0dfd840d960666a4391b1bca115e5d997d9f4db42cf54a7171c8d3195eaf2bb2c685829ea6bc33f39c9044f5ff0bc8e105145e9296913ae84db2988e120a975e44c7b3db508528aee34dd29d863909a5c53d03fd77ba78d6c89821738a6036333d688127478b581770c4fd8af463b0263e21c26c9043bbfa0b2d648eb2bfb85045fa355291e2c0d536eafd77870c50344ee5e9bc6dfc0cd22142d9aa6b2f42d27c679740699025dea47ae043051e31f272ce075f4051951ea607c6f8f2e3861ec31e0f39700042ebf2dfd9263ae586b6d9eab47ee624bf8aa7a5f9b43363c7b1189c935968abaefcf79fbd1650139d64024f59cc1dfaeaec5c3502ecb4a790dd42e06fa1d8531734b72163b965d73b13bca3831b34df8da7dca76f5493d825e737a6eae95c2e37146fb6091f1ecea1f37d7870032cf87d41085beaff656681424abc374cbc66173680d0c627858699537d36bb858a06d2c103ac4f25eb22244aee3684f7c4e394b59019908f9a806f55c593dc7d2e1d976443d81e874f631d686e46e7ba3a900b4ef6fe121cc82bb07bd85800b594f49ec818f5e165c76d9335c1936242b4d4b33627f8cda19a63040e7497ee3441ba151fe4d827a7b6acc88dbe8835af9f27784989411a0f45f290acbb7711d122a46e8c1429b98a38b2c350f4c78b72916487e57c15e3dbc5edc302c7f0bcb521519366e76e08d78720e0bccfa270462f578fc897d8a4b2313882c10a1fe84cfdaee07ba85837460a737d28a1e67847620968aa897a202d09145c61e931691202f4b1e74c53bc8aaac2172f2c3336c6d681d94b605af7bb16f50fb17d7773a3d91c649d43c07eb0b9c65e6f377bc2e9de1a7c6412bde06a99358c7ea8b4fe0e098684e9d3ebd2ef7cf312543d64cfa2d91a2b641308728133a0c039a6444efcebabbef92c7f2748ca4cdeef50d5d94dc2f84d9116b30507f627c1ec4a2cec57c69c7eb95dbdd05daf91b17ceaebc3bd7b9e3f40bd56cbfcacfd57966a9300d909905e17ca772b916ec25cf91286a53f90fa6e47518ef42d73b0bce610ab748a6445493e6801d9dc8b3e709c334aaf196c7732c2b2493af7473141b1be69e072fce8954c0a5c10663130d687340b8c1b6f0efb31f53d58449b4ad9b2cd7f3a86718545ff21f3acb12a8f81ea500a8f5254d940d653aa841ac45ad5eb806f286f48d54b77584499cdd0c9fc0e0f7b18e2e4b6a4c584bb75f3e12972a6fba17e3abc72d31abc02938f0e4e47734118df4ce460590d80afde73c7ad9795a8e9e0e271ea6816a01b77ae3a6e41880cadb52b057ce0b030728eedd331460cd2e6de3cc2cd9b4fa1a028f40bab960084f7c45b08382f138b24277132c707f763d8160051ba773640809443f4ada46817cc66c7de74b5fa571111f39b2236a3ed45b5f3bc6b8b4a33220e17f301f73cc01b406090bc9280779911e21da0f3e3223371535b2fcb32a9ec40ff3c316848a9ab9d9868261ba4754f6db7f24c0c59802175914b87dcfa653ffd10f588aed6f0fa5269acae7e82e4b11600919d4a9d2ef98c7d959d21b3ccc70138df7f606dc72c97cc78854966ecc4ac2429f6e9fc005a75d816d4d5e82547581345165ede9698fac897807107e1ea8c65c4b5502528be874631d23c7a54566d4f65dbf74bb8b81c9442272d0e9ee6b005ebdcc7235fb91c871d847d3722ee9d299dd5f86c87cccd7536c82998780fc85de241ac9fd352bdacdadf98f9b3c79bf531b45b163b8ce24b448f1fc876610d2eb19d6a9efd97aa20dedcc845747961684049d411db62dc172318b28dddd743a96b0a28da3cab1cd2ee324d449cd001a48697481ad4d3434cfa06826676a4a63830563403c656640e189d70ba063002ebe52d378f9f2a4ec8cf5d8f92731d60d68d5280e535d57be0c2a185244a2fa40ceb2343af547edc162ca3c02bb2bd8922c0047c5cb5232a9eb922de95094241f0d4c23d582da8e1f72dbcfac581efd5f8c0ec8067dcb87be9e74409b75f8f5ee2b8e94d5d06fc5680b3353ff0117d82c785e41363db41037231c6daed71bd237a82656526d209d3ed2a5ec36d237eabbc78c276ef4c88c5feca354dbfb5a94fc2f77e3eae7ca816a4c995391959ef555475a8f9fa8af4831d72be98993166eef50f55c7e8e66604d875016447a602282a21f17acbf96f15ad7785f399977a925dfb179a3615d9010fa5aa40579c625251d992e7db851ea63d4eee7c2659d12bde63b0a686ac2c5b9d392dc05400162eaa3bae92987dce121e1d8298c4f4c75eb68c8a2803200ebd558d67d004a7aa6e43f9f03d72b3044163e30427f740f0de483c84aa482289bac79387dd8ca045c1534b11b13cbaa7db015dc53d2512f5a99d67aaefe3c2eac9df915b665659408fca06060535cec099e6f6a4ef918c3fd463b7914d77afa497ba7813da3c8bc4cc32474a0e19a81fbcc392f0f37da91756c6ee6f322cfa498dab8a197f97d5758649a8dc4952c3e5a7e6a35dd2951c28fbddf15ce4ffd84eb66a19fcb575fc617efef6a25baee957960f789c831cba2d75943668828612e887c52e3189470d247924b6ed13f5b6486fe61f354331f6da0df303d71a936b9c20e24b5797da101c0224f1f58904a2dfee0c998717b2bed81f6e44d9400ad6007d2545d5bf1faafdd1e6b0d4612c541b48c41602873e2e244134699859aeacc2a724b747682c36e04161d65b3e6f96a4dea62f3ef78813c58f90236bdfa06eaa54309ab5a02035e5974031fcb3746af77af92273b2b34616f1afaa77a1ab933736c9eabdfc2fde7a61e6ec559af82911be1f36286d29e382ad0e78e7ff3ab49b46b0c777432613647368edf06287a14c8ae158e7bfb210d57a87b38fed74363fbd52ed79ddde65ec99dec19faf3ca8660ac69c93d62e6c839d58bfbe09b218736c6c22238a9d75c8222f0a985530458f8941878dab093b936282676acd4d0ddf895e3e09598156334afb5f2032277faba1a0dc0d40d3cf3a8e9b8357734291065bcdff54b090d41afa1b0a70f9c5d34c3f3da24dd92d919b0413ddc68f09ce8b147e0e93f196cb78e1383070e8aec3b2cead6416f838814db215bbfe2bb17fbda99f5712351760d5fbf4ad1d472d11800e53d55a885f405df790b36d6fcad5c7dd244c7f41a914c1bd1501b3d8a7dc9fe799508ab83ab57774715777d6afbab7699c8ac0df841aed084392ecb211db9ae84cd826d7fc81b1f098f9bcacce3d8eb676edd7e28d5fe0043a115f2238589f58f69f8e28f4376dceaa6dc49bf5ef2348d30a24d88643c3e6a756aa9188fba775c494b82624578d75aee310def157dcd4debb1d5bbe84f532f95c3f364e839543262d6baec5f3e5cd45fcd349ceff4480210be24a7bf25999f20e44102dc318357e10cc7321ecd5c4a36b4c68a7d977cea67a674479c4eaf7ac9fca163e0bc03d81c8ab115d16992ec8be6e39a448468d44be5eb1699091204640a920674b5326f06359007329f7a026c3b7aabe33a76f775f1f71659542bf145ba7e8ce2c32c5820200f043db29376b68c183626b2c4696f455630e6e1629cc3b9787bf8250997325200d7aa1bbf69e8337c85245493edec6701dc0a6a1dee2237751f3b4abbdf7934ed9c68ea61ad4e41ccf9d839958a458c5a80945e56e14b84cdd5a05e7910ce1e594c29afb2497825e59dd8233b657c91b6f4d9c0ee58ed2e891f065daaa5c5f79d705d05a2c4c75463e9df9198bec4b611c78b9612e594059d1e3ab266351a2936d27b3944199466e498bc74a8728c693810c9b61639a2e2288f8f7762af2b1722d7175717889b06223285d26e1134e5ef7358c8f0a3f2943bf79417879d080dd182a1bbee297a7aeba7a501aaee6322669d434670d76d482e327f9342c416c1d91c4d79492516bfa0d30ce122eced03aaf077a75cb58b66bc12c27b3168e7a0d7c3cff08a545d11a5d3d834570e0d879f2a1cdc549d6d697a55992d22574d7e626ba3b0b7268579f6a2c5cea5ba476c7497117d180224dbf7264868f7f9660769e6cba257329ddf67638398094cd1674404e5c0897959d296079b958250934b96d4a96fbca1eb6eb7aed09a6a946588bf27d31bb30a39d7d43da59dd1b0ed3365de93f9f8bc7fe36bf867bbf1a0d00c6aab4d6bc17a6dc2847dfebc8bc6d88c5dd0603b2910dbf1bf53440c4046934f1bc7cc76e2305be0bcc456c8f48354a6f9293328bb4c0c97f80e0e3f45655ea1c134613ef0616557afada9a404babd6cc62c70875a4c3003be47608f09a101f80838b907dfa1554c32b9741eb88e26d8f6a0bdc2517774e0e3160339392620d0d84878e6dfa7ee9bc9f1f26e6a000bb4bb714c19e58388379fec134ca99e00e44daf203cc2300dbe6dc6727953070eaebe4a4219c9a191849fa36fc6c42572c85abfb0e1ca479cd3873014bf6e3e5387bfe73211a976c2097868dec187870f2fef908de1e9cdf9224ca4e81a8e2f54763e30d31a69ac6653714079899e39e621f947427435cc4c76c4cb09abe93d203766b0eadea09259ab7ebfa22dd751decdedde5a4afbc3b1d22538757681e8b555393cbacdffd0f9632d4a3be33b7752cfa84ba088a8e1ddfe173ab0c41f58009b5ca7b81b81a5250fd469538d4db3c4a000e0193a4d692f52cbe138f858992bcedeb1ead7c30cfbf7caf6c6cc59782849ee47c09eb03f7b15de8ab4b2af5e3f12a754c998fc1ad13c6b344247a83b4b68cf24a274ac43156dfb1091a7af2a56cbc0e784eedc3b9e2d765687e3e467d0477f87013159780aaeea354fc9d6591897665295825ddbff6c1de49b50578d06312ca3d16acbe20d2bea1525f7b0b3373296b4f19e5deff4ac1606c1eceb1a44944f0c146356c44f7e02aa684b4d881b772959b9e5fd7d501d6276606808dc70ead2421367cabd28f34eb873bb0395c7c161521dec71f0d563b9c5d2bea3b39c004855119d81899f65106db93d9fc44c6791013afdeb230d84cfe5db4ec0cd6bb336d2571e9ce8ab3f95e6cca8c3a36a6dc07396385621ed5784ff3bd76cadb7ced059fa4aed697ccd2fc7b428f6bd31d7ade4079655792d50cfd6f253000c5fdbccfb8bcdefa6e8102248d8e934cffa98df8acbf90e9206a4ded27fe4ef9ba7ef53b71a4948d26a1a7133f44497dd458b62c6da008ca535ba3db1a4fa4ec7c34a3531375190ac403e617bb7a1015983ae7713d95c93ae57cc150dda1d612c6eca45888b02c4d31c4e6bc6e72744e10da73da93ecd198c2dc8eea00984cccb2be82f7dfe54f6a163aec53117606b70d16db4665aad1047fa0c76715f7714cf186101c85453775b6380a3aeedc58c11d871d976b430f593c45ea6cf0654ca6a4644de1cb79e98b0157f88fc363a73e38a4d7e1cb3029fbccece63f699368fa7472a82d8f4123349e1a0b7d4e3f5c47b4fa04f118a75979fe123ecd94c6ba7a13148eae097b2d0bcf98f3ed647ab0ab30597e26e15dc09a255b0324346035503957423003dc83432c7847e71163d124a870c67438deb7aedbcab06afa03c9f851ab39fc18533798f4b9dcf25e2592e0c5a4d67cc735a0597181e1178823dceca04e494270e67e6e0e347189327bff760ef22ac8d40125ad9afb1b66b4ee990811ec12ef60bb1a06fb48dd4b4845362aa30e067c2ae96814f9ed858ca4c305623d921a07df7f18d119b7f3f0e5fb3665e3f6bc123422fe25a2a637c849cb19ddf0d113ce806655d6396af3d607bb89a2bf03e55aa118042793975f041e4ccc25b3626a2707092d3e52e16df9d9a494a81b93b9602ab8a6bd0250d0d8c2500da32a6cdc1bbb6a0d45b3a0b508a610c4119442d7301b6958528510617e29409d83775e44e02ecf49cc34d094966dd99c94542bfb94b60bb0c47b40bfbc4c0c01deaae741c02bd672d7be97c4d7de98b4b8fe059025a3fc4316a213d877ee45cc1664044488298ea7de7af43c99904fff4fea00aabeb0787fc4392f67daef61931795ab4a565ef433e786eb3c1544b91d7ba097bd69cd6cd8a5be35696aec6dd395c158abd56ef9b3f94c15f8d9be3fbc79439eb5b2f542c4035a02f7dd3bd7935102cc607dad10fe38eeafcb381edd9109077e4c79fa59b405aee879715e9c1df95d338ca924b714e485f1d182edcba712da7080cbb6e5473d9986f07acbb26579ee4108624c77681557dbc8f91dedc43ba4b17468c5dfe4c540bedab66789f4efad9ab94b9bcb1a100e1227238b84d0cdd182f7fd3463a0a59298b24612eb229fb7c0de2838e424d2fa88d15d93188dad950acac8a29c0356c02a7b32f56342e4e968e01831e81b81bdafcc289e4f9e0ba6ed05517673383b3b38656deb4d5d1cfe8c3bb54ce0db08b7c789f3d45e64e5ebec0895864627ea6c55520f8b3c4d15fd174ed96ba646b92315f011fa4385c81194f2cabf73fab4ec6ae0387dbeca8da852f490234a5b81f7577a2576ec4a889b3b9779809e5da1a23ae97f9fc8a8e56d928b987ed325471a9891a8103ab31fc9ddcd9551945e9c47d4eb1ed8ce066f40db00e91cd0cfb6c5c6def5d59de00570800cc4005f6133465ecc88f2b1db12e198db74ca2d54e224115def48202c437683f8060c9bbe9dcf005387dae8a651a6572f75cca629a846de91c45d1672d969256e3aeb54cb6bfaca83124ec7e75f3dfddca1bf78cb73a5ce7c86f0e63fac4481d183e16721506e12bdcc3ca58facd4e431d9267e410bfca4b08dc27ef887d62940904f76b7152d6f44fff32acc7d7f883ebd51c7765f6dbfea37bc48de73bdbcb269abd383f6349f5f9c59fc7d308cf7ebc760e0d377a02882c9680e24d13a55ac23429bd14a17e46911232eecad286432d2c1dace6e73c27d9753cec0f673bd03cec925b3a990a092224001373f14eff6c9240cabab52330791c11d976fcf324dc192ab9ef4f44e9b49b418d0c4c57ad290e3dc9cb881ee8bd293168803af8da0016a2f9e77316af15a0920b6c435bf53fd80b103a041571359c851c27aacbad3dce6a8ae26790e8603e320d0225c97c748a31e145f48692f1b2bde02b4c3e03d4c51648495c04fc58dde56bfd63867fa6117c5a4d67ff0a1c6c094738604e57fd6748d8ed742d75215a4f907afa5d9552e2cceda44ffffca1a59d625ae4406e91e5bc5917170749e27fa427e423b38d2666c06372e01bf617648579cb15a8933345a342a6347a0f17f54f8f2c5cfe967b9c4b53c99498b71b12a2e00f2b3bb5f3f1cbf87e83c69b5f79dd17fcfff71475f0bc0910b2c8f61a7b24e42c057a1a4cd443951ba6964832ddf73923efb3407351216b2a322d9faba2b7ea8f99d9fe10987c78ec2839fcc01a95e9c26085e1007c043b74d558c743c9c655a1ce7a9b0102816a294c9a120e53dac3d6e04f427042641e9b83636891fcddedf9f6b5d2e8903ccd4fd4a61bbd6c3a3bbfa6a0e35a143a7baab37d4a8832917f87bb0ee00f03e50f460c11335c45d31db1d423e052ee090451590cab9b886d75ad5c8c5cf3f678085df7d2b1be3fa3669417991aaea7a9e7a7c3efdb13c29bc26422b77d6803aa2a41122d58b1303851155a4b91446f4921a6bf7673423277ee3a7afe904e96e3c0af87a67806dbe48d731757ccddc96c0234ab731c0e3635ea8248e7e6ec8bc5bc3c3f0c0f9af78271acd767c90dee6eed055741230a93b074cf0a17ccf88dc7337fe5020a788f7c47fbd9b7dc70277cfc1fe20b91720d0081ae48f0698f5be8ca57484d058a83b9a381a1463837e113ff41867aad34cd1aa5829565c798991374a8e1ce04d369ef5b824dbf3d9787f18160806441a4e78e31707cf4f743590b23062ef4e2d92ef007a89305a5f4334206ca2389af93998dabfb7ad5c36d9fe0fb8078315f7e846d3c28a8b68e0cbce3cd8555dca9e887bab8bbd5e47f6162ea18bd794aaf250661a70004c2f02322941c5cef804b5ab0e2a2e60ec2d0b1f8f887f4b5ce660534998736430ae300f5976f119212279de3a9c4958a11c4b207079d34dd175bad9f09270958c34cb40f0f4b171d8eec855cf336259f0a76e6cc9f91e006a3d05eccb79a43eadff680c5842b0dafcbff7bcae5d6e324a87d392e5dee977d0babbe6c16e8addb6f14a2b189f45b061de5c001c70df750ac1f61f8184b4238a8cd1e014a8d22a72a1209a4f7bef362461f933127a5de91a3534eb3e7c93fb9da01dfa34fb60815c1d6cff261fc1ae8795539df2a6dbd433e22b7c480127d7717fb913e799d116d5d6091fa5d4b5592d10c6b47a05b658907f7b43c84470b03fdd2f6eeba4171e84215e67370b3afaa50e9676446f2b6851e8672c49a8d41954b054a6fc08e560f5b736799b3e634c14ea7c1f733bdb84c33f444664434ea8629ec384e6ae6f30064772d72b13ecbd84383773de78ec79df4e21cfadc55a0b4bc82e9c47a9dd3186ed51d1c9a0950ed5a35991b8f9abd09d5a4b091792f2f385a4b6bf97d76b634bd8dd7de1b64e8b44a9b073e3160ee65f23e5008e0400ba11bfe5bad7afd0db6e840b5a0ed9b2942b375b9e21d29ea9ff505db727d53f7eed2adda755c7f0383669062a438cfb1f2fdeb7461da73ffb8e2d9fc5adba4659ecd97d329e965a4cfd1658ff73a66c7672fbc1dfe604b94265bfe73fd8b46dfb1372ad11abbd7b2ac8ac251ed7e86985a51a31458fcf019c4513c0d80b41d8c4c506d728d58197b1aac1163c960ed74223b5888b9588f514e3a6a186ec01b809ec004420c965484303dfd372adfad83620ed8f0597729d9ce10971742bfb5d625036bfd179dbed4e6c9e58c1f7c2acd1bab8b97edc5425ecb3d2c541225909e67c3ca74a4743c1c81008611db6b0d2fd59a782b1a297525f0a61cdfe8e1f9fcf7490de8deaa5ffd3871f1e03d1f2d873ce0abfc95a0df351c187bdeeb45d2f6e3841bb57f9c1af8829a9907ce1b423a6dd8e14417d10cac72a9623ab949dc6dffc3b23cce7b73e28debc6f58cd26f9aff395441b83186b930566cf1fc769b14c2051f31e614d6bd015528df63e273ca95cfadf3bbc6f5e96947302fde51076256685e01d4cb5237cc1edbe19e575a6a0dd1a2bd3949eab648474850657373a172798971ddb3dd918459daf3cd77e53ed1f7fd7304cd31d47f5055d9a7ac5df4d52ce320e4f54ef2a09422a58f9d222314632519bdc60f04d99b90c598d633fe5560dd641987e2860c4cd34323c8c25893ea107630aca5d805281c7a6013a7ae710a35b134650a2e7855ad89c8d3e2bfef09f749e8959871cd9fe95eb16414011c226528f69cde37532f0149bd9242c66187828c59c15861448ac756c9ccf6b0fb2b3262709470b3e07342a1b9cf3abce5a950b5bf291d75637c9b1cd42e64802500333a07738e387b3f5131f16b3d1b61f10131256d18d4f5e1a4b0b57efad911df96cbdb60cd7eb7e7b1a410ffb19fc14e3ff0bca6d3e1e71b4cd9d7c06f78f36624e0d06ed99f7319930c505542d408ed890c9ef8d8558f4ed290d67a956166bd005fae212f312127d28714dffd86acb7a09521edf34bb5b0f20c93f030175cd58eda97497af1868cf77cb5807383ecbaa7a812178f6dfa1c54265b2998ca22b284473d1a858940d8446f28c8d2314b98f77bf4ad1b4546d33f9ad31afa93d15d62593cb313921ea421465bd2a947b9c91a894ff55ed65108a2333d1bb3c29e273cc94579235fef4a0ac7dafc0b7a2c8bb878c5ad896c2c196c0deacc4b8fd7899007e01c8bf770de6d2d4e635feabbeab681586743c2777ac195758d9cf04ce14ce5fe6a718fdd58aebc2af154060053b1649372287f80dc3b1fd786f721674fa41e364777e2d6104e003cf00b16045e2ff1105c4e019cc31580189b6cf91fa2e28b011a671b04e37b1ed175b7aba7d1aa6a617785b2434d47d1fe2182e0462bf2e224f60ab33a05c64981e420a395d5ed8d2fbdbfbf02f5d46fe048df7afee953c5e4d61855246eeabba9a286afcfa808cf9baae3573a39ec3c75f67d4e548d4382573a31e0868dcfed7ef8d5cdef7dbe55227c08284185893e40ccc5a946a91a8f70cded5dfa03e08bc242177cf2af30e211677e845c2344d6c3fe4dac3724af90cbcccd5e6b5b0fcb3ab27554dec547f6c6d0b7e4f7772f869e93f4272120b7992f0433c81177eff34b80868e2c9d70b4456bc13f7b269d504aa99fbe782f24a895edaaef3b3a4855383480c73f31d68e2739600001c10057535e4e6b179ec6aafa565cc22bc55926a13c96aed1ca4b6a1de7bacfed67fa427619c004e0a9484f5e0f6091e374fd15dcc2e023a74e027482694762c0134758894e5c119fb39f54edc561bc290c1227709f9131bf47ae229ce7ec04d1408a4e75b7f13517c95e90fc631cd072d7c0e9ee95b332edd52c3636a00e7b06c7c1b1bd7b655a8f10c03f9d95161ef7f4f84e07280e82f42ad09c9c414aec55616fd5d70d7148f8d26d13ff2b4379f50bc904e65f91a1c0cf409df443b078fbaeed482a8ae67ed6553f0f7f4fb8d416b1e4647256352722834fa634b196bdd6fbdf1f4153e265f7773ab9e63da5ba4c7d01e7466edf00925c6ddfd9df0362b0b070b513d8da0c7e8bd7c15285a158e0b04406eb5e024a9527a81a62f7ef4e4d46a6360675b6bc182c491b578ba404fee7610430b91a87e6db6bba0c5ac76aa1dddf43e2046c739fa83a2db5b3b0e63f5f800af01ee733022359859f531e23515d19938a68b77efef2d1ddad38b1367a0c84db8c289a5200776400ac9f304ed0cc6e56dcbdb05af957990df33b3bc2f3913117d3508c25b7fde6d8d1b971b8f2fa37554c576a4e8c2b06832296577d2575da8e57a324154d8ceb205197c2fbe2dd2436817a475c0bcfb4e8b94304ba046db9b50c14b3002001a93441f1108da28d761ed6debf252b34bed9838f5392de065fef45594dab9f38c25af7c9433162bd5e3ed8cff6eebd8cbab5e3f9ee40dfc0bf8cd06fe970f031e97844b8d10a5113ed36a4332619a2ed670009d0ac2ffe73aec8826bfcae53e358bf6501dc9430e602ee340786bb59e281a79294ea58192be26bf101874ecec237f0178fb4c16a4cb42f3aaae5ddc53adb9d9dff86e9c33c294ad7eff8c0173eaa5b47f23338e43aa01ba3062a0b5af372e2e5931ac8b9f6a075b05a7f1841972ee8749a157ceae9031db9e9cc0730aa38f1437dbf2c8eab26841937e35c3f1c8db5c3226fa3d3a4343f1a68b4c939ba52656fa4e2f47fef0ed9d03cd564655c25f88a46bccfa7e031b80a32a7927a51163698aa2c60399e4a9124992fa7703c7cb10db397fd05d06f6e1a8f861c9a3bd77e82e7f897ff834cb20b3b0cf56b5a4b363a7eb315e11212c3fc0f7adb1c1eb62de9e1d4e9f1ee9965c7f242010999b043b409fe76dee6c0c19c8d3ae1b87ddd3374164ab0f060d287d94735738b151df3f863c318fff0262cf628c5fd3e03121c8a0a76df5d5457427aeae6c51af79e01fd3c32d06fbfd0a41e8e1c5e7f1a4747de515ecc768eb1538dc382713a369c564fc6ed8fe92bd014bf065f59093883142b8a717fe3168ea723bf06fd4cce331c7649de9c78eabf922d99f39b636b03d3c58971adace30072890904f60945753721085a07663c5350dbb49bf768992f5b33e6c8ae35baa5c2d592caaf2572e8915f28765478f7525b1f89dd62a61d426599ee9e88f4292f18449f2e267e6ad4d1ca715a8510f228f142cb14494490ab2b7a0676ea1dab7eb3c5a9c76cc97bef90d245be3cbf8e21a55721107580e67347f8d89c4be2229720f1628eb21359df0e2747a8893a140ddf19a094a7ad9eb212e2aafa467f0fd019b0c2c6edf37525b0e8ec3a9a014130fed43d8b7430a03af3b427c2e850132f9a6e41f1270741067c6a72a72c4e25c8be80c8e8716164ddb34003c78eed15b44a11f61c1f435403d17e474f416bc4a13317bb7a441ce09fdc3fb370ca1ef82d3a6f42e9a547549b53e26cda91027eb4228079ffa0197612bba6455b9deb71f5bb8a9f5184708fc29ead7f06d0932a1ac10033262e791c3dd9856c73f92bdd47911ba38e53a1ff2575d33a256b5fe467720eae58c1ea4f68241cf16797c0d3bc2095b825a47ca3f34cedcc3670d793c3223ac01359e330eb35fddb220d26879a4bccd1eec00536cab6fd74d2f13549dbb4b2277ed6a17a347210943467eacfd5c179bc6ab2c5ad16a96f0f65ecb7033be4483c52d3c6bf38a772ebf8e3bf89fc8747146e460aac0055408c8541e0be7a7a6836e96b079a8a67407112fa75811813c01f21ee68861fef89ed34e60ade580be0128abe4b3380b195127789839e17d035afb8234963a678c58874584c7248efe6c09ff3cb5c6b205aeddf910bde74ac0df86b20a330b4b89066c10947d85ddd07b4c85a2e7dce797c7ac71683025d2f255bc448a0ec09f6182eb7fff41ef0cc34b59f204b28743fb0ff1d9378ef727fd8533698d3052ca78a2d6cb8b64f7f164a2d302b0dc1f2d4af4c2dc613820cf1565f23ad400192a57a09600aa019eb042c48e389e4341b97c5ae21fc3db3458525e33226c357bed0f5ece0cae9df73ee5f4b91e635be</script>  <div class="hbe hbe-content">    <div class="hbe hbe-input hbe-input-xray">      <input class="hbe hbe-input-field hbe-input-field-xray" type="password" id="hbePass">      <label class="hbe hbe-input-label hbe-input-label-xray" for="hbePass">        <span class="hbe hbe-input-label-content hbe-input-label-content-xray">您好, 这里需要密码.</span>      </label>      <svg class="hbe hbe-graphic hbe-graphic-xray" width="300%" height="100%" viewBox="0 0 1200 60" preserveAspectRatio="none">        <path d="M0,56.5c0,0,298.666,0,399.333,0C448.336,56.5,513.994,46,597,46c77.327,0,135,10.5,200.999,10.5c95.996,0,402.001,0,402.001,0"></path>        <path d="M0,2.5c0,0,298.666,0,399.333,0C448.336,2.5,513.994,13,597,13c77.327,0,135-10.5,200.999-10.5c95.996,0,402.001,0,402.001,0"></path>      </svg>    </div>  </div></div><script data-pjax src="/lib/hbe.js"></script><link href="/css/hbe.style.css" rel="stylesheet" type="text/css">]]></content>
    
    
    <summary type="html">快速入门</summary>
    
    
    
    
  </entry>
  
  <entry>
    <title>SpringMVC(黑马)</title>
    <link href="https://silvan.chat/2025/10/28/SSM-%E9%BB%91%E9%A9%AC-SpringMVC/"/>
    <id>https://silvan.chat/2025/10/28/SSM-%E9%BB%91%E9%A9%AC-SpringMVC/</id>
    <published>2025-10-28T11:42:57.656Z</published>
    <updated>2025-10-28T11:42:57.677Z</updated>
    
    <content type="html"><![CDATA[<ul><li><p>SpringMVC属于Spring框架的一部分，主要用来进行Web开发，是对Servlet进行了封装。</p></li><li><p>SpringMVC是处于Web层的框架，所以主要作用就是用来接收前端发过来的请求和数据，然后经过处理之后将处理结果响应给前端，所以如何处理请求和响应是SpringMVC中非常重要的一块内容。</p></li><li><p><code>REST</code>是一种软件架构风格，可以降低开发的复杂性，提高系统的可伸缩性，后期的应用也非常广泛。</p></li></ul><p>对于SpringMVC的学习，<code>最终要达成的目标：</code></p><ol><li>掌握基于SpringMVC<code>获取请求参数</code>和<code>响应JSON数据</code>操作</li><li>熟练应用基于REST风格的请求路径设置与参数传递</li><li>能根据实际业务建立前后端开发通信协议，并进行实现</li><li>基于SSM整合技术开发任意业务模块功能</li></ol><h1 id="SpringMVC概述"><a href="#SpringMVC概述" class="headerlink" title="SpringMVC概述"></a>SpringMVC概述</h1><p>学习SpringMVC我们先来回顾下现在Web程序是如何做的，我们现在的Web程序大都基于MVC三层架构来实现的。</p><div class="img-wrap"><div class="img-bg"><img class="img" src="https://raw.githubusercontent.com/silvan2077/markdown_pic/main/Picgo/20251011132800.png"/></div></div><ul><li><p>如果所有的处理都交给<code>Servlet</code>来处理的话，所有的东西都耦合在一起，对后期的维护和扩展极其不利</p><ul><li>所以将后端服务器<code>Servlet</code>拆分成三层，分别是<code>web</code>、<code>service</code>和<code>dao</code><ul><li><code>web</code>层主要由<code>servlet</code>来处理，负责页面请求和数据的收集以及响应结果给前端</li><li><code>service</code>层主要负责业务逻辑的处理</li><li><code>dao</code>层主要负责数据的增删改查操作</li></ul></li></ul></li><li><p>但<code>servlet</code>处理请求和数据时，存在一个问题：一个<code>servlet</code>只能处理一个请求</p></li><li><p>针对<code>web</code>层进行优化，采用MVC设计模式，将其设计为<code>Controller</code>、<code>View</code>和<code>Model</code></p><ul><li><code>controller</code>负责请求和数据的接收，接收后将其转发给<code>service</code>进行业务处理</li><li><code>service</code>根据需要会调用<code>dao</code>对数据进行增删改查</li><li><code>dao</code>把数据处理完后，将结果交给<code>service</code>，<code>service</code>再交给<code>controller</code></li><li><code>controller</code>根据需求组装成<code>Model</code>和<code>View</code>，<code>Model</code>和<code>View</code>组合起来生成页面，转发给前端浏览器</li><li>这样做的好处就是<code>controller</code>可以处理多个请求，并对请求进行分发，执行不同的业务操作</li></ul></li></ul><p>随着互联网的发展，上面的模式因为是<code>同步调用</code>，性能慢慢的跟不上需求，所以<code>异步调用</code>是现在比较流行的一种处理方式。<br><div class="img-wrap"><div class="img-bg"><img class="img" src="https://raw.githubusercontent.com/silvan2077/markdown_pic/main/Picgo/20251011133240.png"/></div></div></p><ul><li>因为是异步调用，所以后端不需要返回View视图，将其去除</li><li>前端如果通过异步调用的方式进行交互，后端就需要将返回的数据转换成JSON格式进行返回</li><li>SpringMVC主要负责的就是<ul><li>controller如何接收请求和数据</li><li>如何将请求和数据转发给业务层</li><li>如何将响应数据转换成JSON发挥到前端</li></ul></li><li>SpringMVC是一种基于Java实现MVC模型的轻量级Web框架<ul><li>优点<ul><li>使用简单、开发快捷（相比较于Servlet）</li><li>灵活性强</li></ul></li></ul></li></ul><p>这里说的优点，我们通过下面的讲解与联系慢慢体会</p><h1 id="SpringMVC入门案例"><a href="#SpringMVC入门案例" class="headerlink" title="SpringMVC入门案例"></a>SpringMVC入门案例</h1><p>因为SpringMVC是一个<code>Web框架</code>，将来要替换Servlet,所以先来回顾下以前Servlet是如何进行开发的?</p><ol><li>创建web工程(Maven结构)</li><li>设置tomcat服务器，加载web工程(tomcat插件)</li><li>导入坐标(Servlet)</li><li>定义处理请求的功能类(UserServlet)</li><li>设置请求映射(配置映射关系)</li></ol><p>SpringMVC的制作过程和上述流程几乎是一致的，具体的实现流程是什么?</p><ol><li>创建web工程(Maven结构)</li><li>设置tomcat服务器，加载web工程(tomcat插件)</li><li>导入坐标(SpringMVC+Servlet)</li><li>定义处理请求的功能类(UserController)</li><li>设置请求映射(配置映射关系)</li><li>将SpringMVC设定加载到Tomcat容器中</li></ol><h2 id="案例制作"><a href="#案例制作" class="headerlink" title="案例制作"></a>案例制作</h2><ul><li><p><code>步骤一：</code>创建Maven项目</p></li><li><p><code>步骤二：</code>导入所需坐标(SpringMVC+Servlet)<br>在<code>pom.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></pre></td><td class="code"><pre><span class="line"><span class="comment">&lt;!--servlet--&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>javax.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>javax.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>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 class="comment">&lt;!--springmvc--&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<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-webmvc<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.2.10.RELEASE<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></li><li><p><code>步骤三：</code>创建SpringMVC控制器类（等同于我们前面做的Servlet）</p></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">//定义Controller，使用@Controller定义Bean</span></span><br><span class="line"><span class="meta">@Controller</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 class="comment">//设置当前访问路径，使用@RequestMapping</span></span><br><span class="line">    <span class="meta">@RequestMapping(&quot;/save&quot;)</span></span><br><span class="line">    <span class="comment">//设置当前对象的返回值</span></span><br><span class="line">    <span class="meta">@ResponseBody</span></span><br><span class="line">    <span class="keyword">public</span> String <span class="title function_">save</span><span class="params">()</span>&#123;</span><br><span class="line">        System.out.println(<span class="string">&quot;user save ...&quot;</span>);</span><br><span class="line">        <span class="keyword">return</span> <span class="string">&quot;&#123;&#x27;module&#x27;:&#x27;SpringMVC&#x27;&#125;&quot;</span>;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><ul><li><code>步骤四：</code>初始化SpringMVC环境（同Spring环境），设定SpringMVC加载对应的Bean</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></pre></td><td class="code"><pre><span class="line"><span class="comment">//创建SpringMVC的配置文件，加载controller对应的bean</span></span><br><span class="line"><span class="meta">@Configuration</span></span><br><span class="line"><span class="comment">//扫描controller包下的所有类</span></span><br><span class="line"><span class="meta">@ComponentScan(&quot;com.blog.controller&quot;)</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">SpringMvcConfig</span> &#123;</span><br><span class="line"></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><ul><li><p><code>步骤五：</code>初始化Servlet容器，加载SpringMVC环境，并设置SpringMVC技术处理的请求</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></pre></td><td class="code"><pre><span class="line"><span class="comment">//定义一个servlet容器的配置类，在里面加载Spring的配置，继承AbstractDispatcherServletInitializer并重写其方法</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">ServletContainerInitConfig</span> <span class="keyword">extends</span> <span class="title class_">AbstractDispatcherServletInitializer</span> &#123;</span><br><span class="line">    <span class="comment">//加载SpringMvc容器配置</span></span><br><span class="line">    <span class="keyword">protected</span> WebApplicationContext <span class="title function_">createServletApplicationContext</span><span class="params">()</span> &#123;</span><br><span class="line">        <span class="type">AnnotationConfigWebApplicationContext</span> <span class="variable">context</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">AnnotationConfigWebApplicationContext</span>();</span><br><span class="line">        context.register(SpringMvcConfig.class);</span><br><span class="line">        <span class="keyword">return</span> context;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="comment">//设置哪些请求归SpringMvc处理</span></span><br><span class="line">    <span class="keyword">protected</span> String[] getServletMappings() &#123;</span><br><span class="line">        <span class="comment">// &quot;/&quot;表示所有请求都交由SpringMVC处理</span></span><br><span class="line">        <span class="keyword">return</span> <span class="keyword">new</span> <span class="title class_">String</span>[]&#123;<span class="string">&quot;/&quot;</span>&#125;;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">//加载Spring容器配置</span></span><br><span class="line">    <span class="keyword">protected</span> WebApplicationContext <span class="title function_">createRootApplicationContext</span><span class="params">()</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></li><li><p><code>步骤六：</code>访问<code>http://localhost:8080/save</code><br>页面上成功出现<code>&#123;&#39;info&#39;:&#39;springmvc&#39;&#125;</code>，至此我们的SpringMVC入门案例就完成了</p></li></ul><div class="note warning no-icon flat"><p>注意事项</p><ul><li>SpringMVC是基于Spring的，在pom.xml只导入了<code>spring-webmvc</code>jar包的原因是它会自动依赖spring相关坐标</li><li><p><code>AbstractDispatcherServletInitializer</code>类是SpringMVC提供的快速初始化Web3.0容器的抽象类</p></li><li><p><code>AbstractDispatcherServletInitializer</code>提供了三个接口方法供用户实现</p><ul><li><code>createServletApplicationContext</code>方法，创建Servlet容器时，加载SpringMVC对应的bean并放入<code>WebApplicationContext</code>对象范围中，而<code>WebApplicationContext</code>的作用范围为<code>ServletContext</code>范围，即整个web容器范围</li><li><code>getServletMappings</code>方法，设定SpringMVC对应的请求映射路径，即SpringMVC拦截哪些请求</li><li><code>createRootApplicationContext</code>方法，如果创建Servlet容器时需要加载非SpringMVC对应的bean，使用当前方法进行，使用方式和<code>createServletApplicationContext</code>相同。</li></ul></li><li><p><code>createServletApplicationContext</code>用来加载SpringMVC环境</p></li><li><code>createRootApplicationContext</code>用来加载Spring环境</li></ul></div><p>知识点1：<code>@Controller</code></p><div class="table-container"><table><thead><tr><th style="text-align:center">名称</th><th style="text-align:center">@Controller</th></tr></thead><tbody><tr><td style="text-align:center">类型</td><td style="text-align:center">类注解</td></tr><tr><td style="text-align:center">位置</td><td style="text-align:center">SpringMVC控制器类定义上方</td></tr><tr><td style="text-align:center">作用</td><td style="text-align:center">设定SpringMVC的核心控制器bean</td></tr></tbody></table></div><p>知识点2：<code>@RequestMapping</code></p><div class="table-container"><table><thead><tr><th style="text-align:center">名称</th><th style="text-align:center">@RequestMapping</th></tr></thead><tbody><tr><td style="text-align:center">类型</td><td style="text-align:center">类注解或方法注解</td></tr><tr><td style="text-align:center">位置</td><td style="text-align:center">SpringMVC控制器类或方法定义上方</td></tr><tr><td style="text-align:center">作用</td><td style="text-align:center">设置当前控制器方法请求访问路径</td></tr><tr><td style="text-align:center">相关属性</td><td style="text-align:center">value(默认)，请求访问路径</td></tr></tbody></table></div><p>知识点3：<code>@ResponseBody</code></p><div class="table-container"><table><thead><tr><th style="text-align:center">名称</th><th style="text-align:center">@ResponseBody</th></tr></thead><tbody><tr><td style="text-align:center">类型</td><td style="text-align:center">类注解或方法注解</td></tr><tr><td style="text-align:center">位置</td><td style="text-align:center">SpringMVC控制器类或方法定义上方</td></tr><tr><td style="text-align:center">作用</td><td style="text-align:center">设置当前控制器方法响应内容为当前返回值，无需解析</td></tr></tbody></table></div><h2 id="入门案例小结"><a href="#入门案例小结" class="headerlink" title="入门案例小结"></a>入门案例小结</h2><ul><li>一次性工作<ul><li>创建工程，设置服务器，加载工程</li><li>导入坐标</li><li>创建web容器启动类，加载SpringMVC配置，并设置SpringMVC请求拦截路径</li><li>SpringMVC核心配置类（设置配置类，扫描controller包，加载Controller控制器bean）</li></ul></li><li>多次工作<ul><li>定义处理请求的控制器类</li><li>定义处理请求的控制器方法，并配置映射路径（@RequestMapping）与返回json数据（@ResponseBody）</li></ul></li></ul><h2 id="工作流程解析"><a href="#工作流程解析" class="headerlink" title="工作流程解析"></a>工作流程解析</h2><p>这里将SpringMVC分为两个阶段来分析，分别是<code>启动服务器初始化过程</code>和<code>单次请求过程</code></p><h3 id="启动服务器初始化过程"><a href="#启动服务器初始化过程" class="headerlink" title="启动服务器初始化过程"></a>启动服务器初始化过程</h3><ol><li>服务器启动，执行<code>ServletContainerInitConfig</code>类，初始化web容器</li></ol><ul><li>功能类似于web.xml</li></ul><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">public class ServletContainerInitConfig extends AbstractDispatcherServletInitializer &#123;</span><br><span class="line"></span><br><span class="line">    protected WebApplicationContext createServletApplicationContext() &#123;</span><br><span class="line">        AnnotationConfigWebApplicationContext context = new AnnotationConfigWebApplicationContext();</span><br><span class="line">        context.register(SpringMvcConfig.class);</span><br><span class="line">        return context;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    protected String[] getServletMappings() &#123;</span><br><span class="line">        return new String[]&#123;&quot;/&quot;&#125;;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    protected WebApplicationContext createRootApplicationContext() &#123;</span><br><span class="line">        return null;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><ol><li>执行createServletApplicationContext方法，创建了WebApplicationContext对象</li></ol><ul><li>该方法加载SpringMVC的配置类SpringMvcConfig来初始化SpringMVC的容器</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></pre></td><td class="code"><pre><span class="line"><span class="keyword">protected</span> WebApplicationContext <span class="title function_">createServletApplicationContext</span><span class="params">()</span> &#123;</span><br><span class="line">   <span class="type">AnnotationConfigWebApplicationContext</span> <span class="variable">context</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">AnnotationConfigWebApplicationContext</span>();</span><br><span class="line">   context.register(SpringMvcConfig.class);</span><br><span class="line">   <span class="keyword">return</span> context;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><ol><li>加载SpringMvcConfig配置类</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></pre></td><td class="code"><pre><span class="line"><span class="meta">@Configuration</span></span><br><span class="line"><span class="meta">@ComponentScan(&quot;com.blog.controller&quot;)</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">SpringMvcConfig</span> &#123;</span><br><span class="line"></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><ol><li>执行<code>@ComponentScan</code>加载对应的bean</li></ol><ul><li>扫描指定包及其子包下所有类上的注解，如Controller类上的<code>@Controller</code>注解</li></ul><ol><li>加载<code>UserController</code>，每个<code>@RequestMapping</code>的名称对应一个具体的方法<ul><li>此时就建立了<code>/save</code>和<code>save()</code>方法的对应关系，当访问/save请求时，SpringMVC会自动匹配对应的方法</li></ul></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="meta">@Controller</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 class="meta">@RequestMapping(&quot;/save&quot;)</span></span><br><span class="line">    <span class="meta">@ResponseBody</span></span><br><span class="line">    <span class="keyword">public</span> String <span class="title function_">save</span><span class="params">()</span>&#123;</span><br><span class="line">        System.out.println(<span class="string">&quot;user save ...&quot;</span>);</span><br><span class="line">        <span class="keyword">return</span> <span class="string">&quot;&#123;&#x27;module&#x27;:&#x27;SpringMVC&#x27;&#125;&quot;</span>;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><ol><li><p>执行<code>getServletMappings</code>方法，设定SpringMVC拦截请求的路径规则</p><ul><li><code>/</code>代表所拦截请求的路径规则为全部拦截，只有被拦截后才能交给SpringMVC来处理请求</li></ul></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></pre></td><td class="code"><pre><span class="line"><span class="keyword">protected</span> String[] getServletMappings() &#123;</span><br><span class="line">    <span class="keyword">return</span> <span class="keyword">new</span> <span class="title class_">String</span>[]&#123;<span class="string">&quot;/&quot;</span>&#125;;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h3 id="单次请求过程"><a href="#单次请求过程" class="headerlink" title="单次请求过程"></a>单次请求过程</h3><ol><li><p>发送请求<code>http://localhost:8080/save</code></p></li><li><p>web容器发现该请求满足SpringMVC拦截规则，将请求交给SpringMVC处理</p></li><li><p>解析请求路径/save</p></li><li><p>由<code>/save</code>匹配执行对应的方法<code>save()</code></p><ul><li>上面的第5步已经将请求路径和方法建立了对应关系，通过<code>/save</code>就能找到对应的<code>save()</code>方法</li></ul></li><li><p>执行<code>save()</code>方法</p></li><li><p>检测到有<code>@ResponseBody</code>直接将<code>save()</code>方法的返回值作为响应体返回给请求方</p></li></ol><h2 id="Bean加载控制"><a href="#Bean加载控制" class="headerlink" title="Bean加载控制"></a>Bean加载控制</h2><h3 id="问题分析"><a href="#问题分析" class="headerlink" title="问题分析"></a>问题分析</h3><p>入门案例的内容已经做完了，在入门案例中我们创建过一个<code>SpringMvcConfig</code>的配置类，在之前学习Spring的时候也创建过一个配置类<code>SpringConfig</code>。这两个配置类都需要加载资源，那么它们分别都需要加载哪些内容?</p><p>我们先来回顾一下项目结构<br><code>com.blog</code>下有<code>config</code>、<code>controller</code>、<code>service</code>、<code>dao</code>这四个包</p><ul><li><p><code>config</code>目录存入的是配置类，写过的配置类有:</p><ul><li>ServletContainersInitConfig</li><li>SpringConfig</li><li>SpringMvcConfig</li><li>JdbcConfig</li><li>MybatisConfig</li></ul></li><li><p><code>controller</code>目录存放的是<code>SpringMVC</code>的<code>controller</code>类</p></li><li><p><code>service</code>目录存放的是<code>service</code>接口和实现类</p></li><li><p><code>dao</code>目录存放的是<code>dao/Mapper</code>接口</p></li></ul><p>controller、service和dao这些类都需要被容器管理成bean对象，那么到底是该让<code>SpringMVC</code>加载还是让<code>Spring</code>加载呢?</p><ul><li><code>SpringMVC</code>控制的bean<ul><li>表现层bean,也就是<code>controller</code>包下的类</li></ul></li><li><code>Spring</code>控制的bean<ul><li>业务bean(<code>Service</code>)</li><li>功能bean(<code>DataSource</code>,<code>SqlSessionFactoryBean</code>,<code>MapperScannerConfigurer</code>等)</li></ul></li></ul><p>分析清楚谁该管哪些bean以后，接下来要解决的问题是如何让<code>Spring</code>和<code>SpringMVC</code>分开加载各自的内容。</p><h3 id="思路分析"><a href="#思路分析" class="headerlink" title="思路分析"></a>思路分析</h3><p>对于上面的问题，解决方案也比较简单</p><ul><li>加载Spring控制的bean的时候，<code>排除掉</code>SpringMVC控制的bean</li></ul><p>那么具体该如何实现呢？</p><ul><li>方式一：Spring加载的bean设定扫描范围<code>com.blog</code>，排除掉<code>controller</code>包内的bean</li><li>方式二：Spring加载的bean设定扫描范围为精确扫描，具体到<code>service</code>包，<code>dao</code>包等</li><li>方式三：不区分Spring与SpringMVC的环境，加载到同一个环境中(<code>了解即可</code>)</li></ul><h3 id="环境准备"><a href="#环境准备" class="headerlink" title="环境准备"></a>环境准备</h3><p>在入门案例的基础上追加一些类来完成环境准备</p><ul><li>导入坐标</li></ul><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="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.1.16<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="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>3.5.6<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="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>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-java<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.1.46<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="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<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-jdbc<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.2.10.RELEASE<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="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-spring<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.3.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><ul><li><code>com.blog.config</code>下新建<code>SpringConfig</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></pre></td><td class="code"><pre><span class="line"><span class="meta">@Configuration</span></span><br><span class="line"><span class="meta">@ComponentScan(&quot;com.blog&quot;)</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">SpringConfig</span> &#123;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><ul><li><p>创建一张数据表</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></pre></td><td class="code"><pre><span class="line"><span class="keyword">create table</span> tb_user(</span><br><span class="line">    id <span class="type">int</span> <span class="keyword">primary key</span> auto_increment,</span><br><span class="line">    name <span class="type">varchar</span>(<span class="number">25</span>),</span><br><span class="line">    age <span class="type">int</span></span><br><span class="line">)</span><br></pre></td></tr></table></figure></li><li><p>新建<code>com.blog.service</code>，<code>com.blog.dao</code>，<code>com.blog.domain</code>包，并编写如下几个类</p></li></ul><div class="tabs" id="bean加载环境准备"><ul class="nav-tabs"><li class="tab active"><button type="button" data-href="#bean加载环境准备-1">User</button></li><li class="tab"><button type="button" data-href="#bean加载环境准备-2">UserDao</button></li><li class="tab"><button type="button" data-href="#bean加载环境准备-3">UserService</button></li><li class="tab"><button type="button" data-href="#bean加载环境准备-4">UserServiceImpl</button></li></ul><div class="tab-contents"><div class="tab-item-content active" id="bean加载环境准备-1"><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><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">public</span> <span class="keyword">class</span> <span class="title class_">User</span> &#123;</span><br><span class="line">    <span class="keyword">private</span> Integer 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"></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">(Integer id, String name, Integer age)</span> &#123;</span><br><span class="line">        <span class="built_in">this</span>.id = id;</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> Integer <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">(Integer 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_">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> Integer <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">(Integer age)</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"></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;, name=&#x27;&quot;</span> + name + <span class="string">&#x27;\&#x27;&#x27;</span> +</span><br><span class="line">                <span class="string">&quot;, age=&quot;</span> + age +</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><button type="button" class="tab-to-top" aria-label="scroll to top"><i class="fas fa-arrow-up"></i></button></div><div class="tab-item-content" id="bean加载环境准备-2"><figure class="highlight java"><table><tr><td class="gutter"><pre><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="keyword">public</span> <span class="keyword">interface</span> <span class="title class_">UserDao</span> &#123;</span><br><span class="line">    <span class="meta">@Insert(&quot;insert into tb_user(`name`,age) values (#&#123;name&#125;,#&#123;age&#125;)&quot;)</span></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>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><button type="button" class="tab-to-top" aria-label="scroll to top"><i class="fas fa-arrow-up"></i></button></div><div class="tab-item-content" id="bean加载环境准备-3"><figure class="highlight java"><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="keyword">public</span> <span class="keyword">interface</span> <span class="title class_">UserService</span> &#123;</span><br><span class="line">    <span class="keyword">void</span> <span class="title function_">save</span><span class="params">(User user)</span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><button type="button" class="tab-to-top" aria-label="scroll to top"><i class="fas fa-arrow-up"></i></button></div><div class="tab-item-content" id="bean加载环境准备-4"><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><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_">UserServiceImpl</span> <span class="keyword">implements</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_">save</span><span class="params">(User user)</span> &#123;</span><br><span class="line">        System.out.println(<span class="string">&quot;user service ...&quot;</span>);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><button type="button" class="tab-to-top" aria-label="scroll to top"><i class="fas fa-arrow-up"></i></button></div></div></div><ul><li>编写App运行类</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="keyword">public</span> <span class="keyword">class</span> <span class="title class_">App</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">AnnotationConfigApplicationContext</span> <span class="variable">context</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">AnnotationConfigApplicationContext</span>(SpringConfig.class);</span><br><span class="line">        System.out.println(context.getBean(UserController.class));</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h3 id="设置bean加载控制"><a href="#设置bean加载控制" class="headerlink" title="设置bean加载控制"></a>设置bean加载控制</h3><ul><li><p>运行App运行类，如果Spring配置类扫描到了UserController类，则会正常输出，否则将报错<br>当前配置环境下，将正常输出</p><blockquote><p>com.blog.controller.UserController@8e0379d</p></blockquote></li><li><p>解决方案一：修改Spring配置类，设定扫描范围为精准范围</p></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></pre></td><td class="code"><pre><span class="line"><span class="meta">@Configuration</span></span><br><span class="line"><span class="meta">@ComponentScan(&#123;&quot;com.blog.dao&quot;,&quot;com.blog.service&quot;&#125;)</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">SpringConfig</span> &#123;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>再次运行App运行类，报错<code>NoSuchBeanDefinitionException</code>，说明Spring配置类没有扫描到UserController，目的达成</p><ul><li>解决方案二：修改Spring配置类，设定扫描范围为com.blog，排除掉controller包中的bean</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></pre></td><td class="code"><pre><span class="line"><span class="meta">@Configuration</span></span><br><span class="line"><span class="meta">@ComponentScan(value = &quot;com.blog&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</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_">SpringConfig</span> &#123;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><ul><li><code>excludeFilters属性</code>：设置扫描加载bean时，排除的过滤规则</li><li><code>type属性</code>：设置排除规则，当前使用按照bean定义时的注解类型进行排除<ul><li>ANNOTATION：按照注解排除</li><li>ASSIGNABLE_TYPE:按照指定的类型过滤</li><li>ASPECTJ:按照Aspectj表达式排除，基本上不会用</li><li>REGEX:按照正则表达式排除</li><li>CUSTOM:按照自定义规则排除</li></ul></li><li><code>classes属性</code>：设置排除的具体注解类，当前设置排除<code>@Controller</code>定义的bean</li></ul><p>运行程序之前，我们还需要把<code>SpringMvcConfig</code>配置类上的<code>@ComponentScan</code>注解注释掉，否则不会报错，将正常输出</p><ul><li>出现问题的原因是<ul><li>Spring配置类扫描的包是<code>com.blog</code></li><li>SpringMVC的配置类，<code>SpringMvcConfig</code>上有一个<code>@Configuration</code>注解，也会被Spring扫描到</li><li>SpringMvcConfig上又有一个<code>@ComponentScan</code>，把controller类又给扫描进来了</li><li>所以如果不把<code>@ComponentScan</code>注释掉，Spring配置类将Controller排除，但是因为扫描到SpringMVC的配置类，又将其加载回来，演示的效果就出不来</li><li>解决方案也简单，把SpringMVC的配置类移出Spring配置类的扫描范围即可。</li></ul></li></ul><p>运行程序，同样报错<code>NoSuchBeanDefinitionException</code>，目的达成</p><p>最后一个问题，有了Spring的配置类，要想在tomcat服务器启动将其加载，我们需要修改ServletContainersInitConfig</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></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">ServletContainerInitConfig</span> <span class="keyword">extends</span> <span class="title class_">AbstractDispatcherServletInitializer</span> &#123;</span><br><span class="line">    <span class="comment">//加载SpringMvc配置</span></span><br><span class="line">    <span class="keyword">protected</span> WebApplicationContext <span class="title function_">createServletApplicationContext</span><span class="params">()</span> &#123;</span><br><span class="line">        <span class="type">AnnotationConfigWebApplicationContext</span> <span class="variable">context</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">AnnotationConfigWebApplicationContext</span>();</span><br><span class="line">        context.register(SpringMvcConfig.class);</span><br><span class="line">        <span class="keyword">return</span> context;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="comment">//设置哪些请求归SpringMvc处理</span></span><br><span class="line">    <span class="keyword">protected</span> String[] getServletMappings() &#123;</span><br><span class="line">        <span class="keyword">return</span> <span class="keyword">new</span> <span class="title class_">String</span>[]&#123;<span class="string">&quot;/&quot;</span>&#125;;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">//加载Spring容器配置</span></span><br><span class="line">    <span class="keyword">protected</span> WebApplicationContext <span class="title function_">createRootApplicationContext</span><span class="params">()</span> &#123;</span><br><span class="line">        <span class="type">AnnotationConfigWebApplicationContext</span> <span class="variable">context</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">AnnotationConfigWebApplicationContext</span>();</span><br><span class="line">        context.register(SpringConfig.class);</span><br><span class="line">        <span class="keyword">return</span> context;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>对于上面的<code>ServletContainerInitConfig</code>配置类，Spring还提供了一种更简单的配置方式，可以不用再去创建<code>AnnotationConfigWebApplicationContext</code>对象，不用手动<code>register</code>对应的配置类<br>我们改用继承它的子类<code>AbstractAnnotationConfigDispatcherServletInitializer</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></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">ServletContainerInitConfig</span> <span class="keyword">extends</span> <span class="title class_">AbstractAnnotationConfigDispatcherServletInitializer</span> &#123;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">protected</span> Class&lt;?&gt;[] getRootConfigClasses() &#123;</span><br><span class="line">        <span class="keyword">return</span> <span class="keyword">new</span> <span class="title class_">Class</span>[]&#123;SpringConfig.class&#125;;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">protected</span> Class&lt;?&gt;[] getServletConfigClasses() &#123;</span><br><span class="line">        <span class="keyword">return</span> <span class="keyword">new</span> <span class="title class_">Class</span>[]&#123;SpringMvcConfig.class&#125;;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">protected</span> String[] getServletMappings() &#123;</span><br><span class="line">        <span class="keyword">return</span> <span class="keyword">new</span> <span class="title class_">String</span>[]&#123;<span class="string">&quot;/&quot;</span>&#125;;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>知识点：<code>@ComponentScan</code></p><div class="table-container"><table><thead><tr><th style="text-align:center">名称</th><th style="text-align:center">@ComponentScan</th></tr></thead><tbody><tr><td style="text-align:center">类型</td><td style="text-align:center">类注解</td></tr><tr><td style="text-align:center">位置</td><td style="text-align:center">类定义上方</td></tr><tr><td style="text-align:center">作用</td><td style="text-align:center">设置spring配置类扫描路径，用于加载使用注解格式定义的bean</td></tr><tr><td style="text-align:center">相关属性</td><td style="text-align:center">excludeFilters:排除扫描路径中加载的bean,需要指定类别(type)和具体项(classes) includeFilters:加载指定的bean，需要指定类别(type)和具体项(classes)</td></tr></tbody></table></div><h1 id="PostMan工具的使用"><a href="#PostMan工具的使用" class="headerlink" title="PostMan工具的使用"></a>PostMan工具的使用</h1><ul><li>官网下载：<a href="https://www.postman.com/downloads/">https://www.postman.com/downloads/</a></li></ul><h2 id="PostMan使用"><a href="#PostMan使用" class="headerlink" title="PostMan使用"></a>PostMan使用</h2><h3 id="创建WorkSpace工作空间"><a href="#创建WorkSpace工作空间" class="headerlink" title="创建WorkSpace工作空间"></a>创建WorkSpace工作空间</h3><div class="img-wrap"><div class="img-bg"><img class="img" src="https://raw.githubusercontent.com/silvan2077/markdown_pic/main/Picgo/20251011141407.png"/></div></div><h3 id="发送请求"><a href="#发送请求" class="headerlink" title="发送请求"></a>发送请求</h3><div class="img-wrap"><div class="img-bg"><img class="img" src="https://pic.imgdb.cn/item/6319dd8416f2c2beb1664a18.jpg"/></div></div><h3 id="保存当前请求"><a href="#保存当前请求" class="headerlink" title="保存当前请求"></a>保存当前请求</h3><div class="img-wrap"><div class="img-bg"><img class="img" src="https://pic.imgdb.cn/item/6319ddb116f2c2beb1667f04.jpg"/></div></div><h1 id="请求与响应"><a href="#请求与响应" class="headerlink" title="请求与响应"></a>请求与响应</h1><p><strong>SpringMVC是web层的框架，主要的作用是接收请求、接收数据、响应结果。</strong><br>所以这部分是学习SpringMVC的重点内容，这里主要会讲解四部分内容:</p><ul><li>请求映射路径</li><li>请求参数</li><li>日期类型参数传递</li><li>响应JSON数据</li></ul><h2 id="设置请求映射路径"><a href="#设置请求映射路径" class="headerlink" title="设置请求映射路径"></a>设置请求映射路径</h2><h3 id="环境准备-1"><a href="#环境准备-1" class="headerlink" title="环境准备"></a>环境准备</h3><ul><li>创建一个Maven项目</li><li><p>导入坐标<br>这里暂时只导<code>servlet</code>和<code>springmvc</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></pre></td><td class="code"><pre><span class="line"><span class="comment">&lt;!--servlet--&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>javax.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>javax.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>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 class="comment">&lt;!--springmvc--&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<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-webmvc<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.2.10.RELEASE<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></li><li><p>编写UserController和BookController</p><div class="tabs" id="设置请求路径1"><ul class="nav-tabs"><li class="tab active"><button type="button" data-href="#设置请求路径1-1">UserController</button></li><li class="tab"><button type="button" data-href="#设置请求路径1-2">BookController</button></li></ul><div class="tab-contents"><div class="tab-item-content active" id="设置请求路径1-1"><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><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="meta">@Controller</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">@RequestMapping(&quot;/save&quot;)</span></span><br><span class="line">    <span class="meta">@ResponseBody</span></span><br><span class="line">    <span class="keyword">public</span> String <span class="title function_">save</span><span class="params">()</span>&#123;</span><br><span class="line">        System.out.println(<span class="string">&quot;user save ..&quot;</span>);</span><br><span class="line">        <span class="keyword">return</span> <span class="string">&quot;&#123;&#x27;module&#x27;:&#x27;user save&#x27;&#125;&quot;</span>;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@RequestMapping(&quot;/delete&quot;)</span></span><br><span class="line">    <span class="meta">@ResponseBody</span></span><br><span class="line">    <span class="keyword">public</span> String <span class="title function_">delete</span><span class="params">()</span>&#123;</span><br><span class="line">        System.out.println(<span class="string">&quot;user delete ..&quot;</span>);</span><br><span class="line">        <span class="keyword">return</span> <span class="string">&quot;&#123;&#x27;module&#x27;:&#x27;user delete&#x27;&#125;&quot;</span>;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><button type="button" class="tab-to-top" aria-label="scroll to top"><i class="fas fa-arrow-up"></i></button></div><div class="tab-item-content" id="设置请求路径1-2"><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><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">@Controller</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">BookController</span> &#123;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@RequestMapping(&quot;/save&quot;)</span></span><br><span class="line">    <span class="meta">@ResponseBody</span></span><br><span class="line">    <span class="keyword">public</span> String <span class="title function_">save</span><span class="params">()</span>&#123;</span><br><span class="line">        System.out.println(<span class="string">&quot;book save ..&quot;</span>);</span><br><span class="line">        <span class="keyword">return</span> <span class="string">&quot;&#123;&#x27;module&#x27;:&#x27;book module&#x27;&#125;&quot;</span>;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><button type="button" class="tab-to-top" aria-label="scroll to top"><i class="fas fa-arrow-up"></i></button></div></div></div></li></ul><ul><li>创建<code>SpringMvcConfig</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></pre></td><td class="code"><pre><span class="line"><span class="meta">@Configuration</span></span><br><span class="line"><span class="meta">@ComponentScan(&quot;com.blog.controller&quot;)</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">SpringMvcConfig</span> &#123;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><ul><li>创建<code>ServletContainersInitConfig</code>类，初始化web容器</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></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">ServletContainersInitConfig</span> <span class="keyword">extends</span> <span class="title class_">AbstractAnnotationConfigDispatcherServletInitializer</span> &#123;</span><br><span class="line">    <span class="keyword">protected</span> Class&lt;?&gt;[] getRootConfigClasses() &#123;</span><br><span class="line">        <span class="keyword">return</span> <span class="keyword">new</span> <span class="title class_">Class</span>[<span class="number">0</span>];</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">protected</span> Class&lt;?&gt;[] getServletConfigClasses() &#123;</span><br><span class="line">        <span class="keyword">return</span> <span class="keyword">new</span> <span class="title class_">Class</span>[]&#123;SpringMvcConfig.class&#125;;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">protected</span> String[] getServletMappings() &#123;</span><br><span class="line">        <span class="keyword">return</span> <span class="keyword">new</span> <span class="title class_">String</span>[]&#123;<span class="string">&quot;/&quot;</span>&#125;;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><ul><li><p>直接启动Tomcat服务器，会报错</p><blockquote><p>com.blog.controller.UserController#save()<br>to { /save}: There is already ‘bookController’ bean method<br>com.blog.controller.BookController#save() mapped.</p></blockquote></li></ul><p>从错误信息可以看出:</p><ul><li><code>UserController</code>有一个save方法，访问路径为<code>http://localhost/save</code></li><li><code>BookController</code>也有一个save方法，访问路径为<code>http://localhost/save</code></li><li>当访问<code>http://localhost/save</code>的时候，到底是访问<code>UserController</code>还是<code>BookController</code>?</li></ul><h3 id="问题分析-1"><a href="#问题分析-1" class="headerlink" title="问题分析"></a>问题分析</h3><p>团队多人开发，每人设置不同的请求路径，冲突问题该如何解决?</p><ul><li>解决思路:为不同模块设置模块名作为请求路径前置<ul><li>对于Book模块的save,将其访问路径设置<code>http://localhost/book/save</code></li><li>对于User模块的save,将其访问路径设置<code>http://localhost/user/save</code></li></ul></li></ul><p>这样在同一个模块中出现命名冲突的情况就比较少了。</p><h3 id="设置映射路径"><a href="#设置映射路径" class="headerlink" title="设置映射路径"></a>设置映射路径</h3><ul><li>修改Controller</li></ul><div class="tabs" id="设置映射路径"><ul class="nav-tabs"><li class="tab active"><button type="button" data-href="#设置映射路径-1">UserController</button></li><li class="tab"><button type="button" data-href="#设置映射路径-2">BookController</button></li></ul><div class="tab-contents"><div class="tab-item-content active" id="设置映射路径-1"><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><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="meta">@Controller</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">@RequestMapping(&quot;/user/save&quot;)</span></span><br><span class="line">    <span class="meta">@ResponseBody</span></span><br><span class="line">    <span class="keyword">public</span> String <span class="title function_">save</span><span class="params">()</span>&#123;</span><br><span class="line">        System.out.println(<span class="string">&quot;user save ..&quot;</span>);</span><br><span class="line">        <span class="keyword">return</span> <span class="string">&quot;&#123;&#x27;module&#x27;:&#x27;user save&#x27;&#125;&quot;</span>;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@RequestMapping(&quot;/user/delete&quot;)</span></span><br><span class="line">    <span class="meta">@ResponseBody</span></span><br><span class="line">    <span class="keyword">public</span> String <span class="title function_">delete</span><span class="params">()</span>&#123;</span><br><span class="line">        System.out.println(<span class="string">&quot;user delete ..&quot;</span>);</span><br><span class="line">        <span class="keyword">return</span> <span class="string">&quot;&#123;&#x27;module&#x27;:&#x27;user delete&#x27;&#125;&quot;</span>;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><button type="button" class="tab-to-top" aria-label="scroll to top"><i class="fas fa-arrow-up"></i></button></div><div class="tab-item-content" id="设置映射路径-2"><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><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">@Controller</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">BookController</span> &#123;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@RequestMapping(&quot;/book/save&quot;)</span></span><br><span class="line">    <span class="meta">@ResponseBody</span></span><br><span class="line">    <span class="keyword">public</span> String <span class="title function_">save</span><span class="params">()</span>&#123;</span><br><span class="line">        System.out.println(<span class="string">&quot;book save ..&quot;</span>);</span><br><span class="line">        <span class="keyword">return</span> <span class="string">&quot;&#123;&#x27;module&#x27;:&#x27;book module&#x27;&#125;&quot;</span>;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><button type="button" class="tab-to-top" aria-label="scroll to top"><i class="fas fa-arrow-up"></i></button></div></div></div><p>这样路径冲突的问题解决了，可每个方法前都要加一串路径，写起来麻烦且有很多重复代码，后期如果/user发生了变化，每个方法都要改，耦合度太高</p><ul><li>优化路径配置</li></ul><div class="tabs" id="优化路径配置"><ul class="nav-tabs"></ul><div class="tab-contents"></div></div><div class="note warning no-icon flat"><p>注意:</p><ul><li>当类上和方法上都添加了<code>@RequestMapping</code>注解，前端发送请求的时候，要和两个注解的value值相加匹配才能访问到。</li><li><code>@RequestMapping</code>注解value属性前面加不加/都可以</li></ul></div><h2 id="请求参数"><a href="#请求参数" class="headerlink" title="请求参数"></a>请求参数</h2><p>请求路径设置好后，只要确保页面发送请求地址和后台Controller类中配置的路径一致，就可以接收到前端的请求，接收到请求后，如何接收页面传递的参数?</p><p>关于请求参数的传递与接收是和请求方式有关系的，目前比较常见的两种请求方式为：</p><ul><li><code>GET</code></li><li><code>POST</code></li></ul><p>针对于不同的请求前端如何发送，后端如何接收?</p><h3 id="环境准备-2"><a href="#环境准备-2" class="headerlink" title="环境准备"></a>环境准备</h3><ul><li>继续使用上面的环境即可，编写两个模型类<code>User</code>类和<code>Address</code>类</li></ul><div class="tabs" id="请求参数环境准备"><ul class="nav-tabs"><li class="tab active"><button type="button" data-href="#请求参数环境准备-1">User</button></li></ul><div class="tab-contents"><div class="tab-item-content active" id="请求参数环境准备-1"><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><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="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> 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="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 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> 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="built_in">this</span>.age = age;</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;name=&#x27;&quot;</span> + name + <span class="string">&#x27;\&#x27;&#x27;</span> +</span><br><span class="line">                <span class="string">&quot;, age=&quot;</span> + age +</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><button type="button" class="tab-to-top" aria-label="scroll to top"><i class="fas fa-arrow-up"></i></button></div></div></div><ul><li>同时修改一下<code>UserController</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><span class="line">11</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;/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">@RequestMapping(&quot;/commonParam&quot;)</span></span><br><span class="line">    <span class="meta">@ResponseBody</span></span><br><span class="line">    <span class="keyword">public</span> String <span class="title function_">commonParam</span><span class="params">(String name)</span>&#123;</span><br><span class="line">        System.out.println(<span class="string">&quot;普通参数传递name --&gt; &quot;</span> + name);</span><br><span class="line">        <span class="keyword">return</span> <span class="string">&quot;&#123;&#x27;module&#x27;:&#x27;commonParam&#x27;&#125;&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="note info no-icon flat"><p>对于普通的参数，只需要在路径对应的方法上添加一个形参即可(形参名称需要与发送的参数名称相同)</p></div><ul><li><p>GET发送单个参数</p><ul><li><p>启动Tomcat服务器，发送请求与参数：<code>http://localhost/commonParam?name=Jerry</code></p></li><li><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></pre></td><td class="code"><pre><span class="line"><span class="meta">@Controller</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">@RequestMapping(&quot;/commonParam&quot;)</span></span><br><span class="line">    <span class="meta">@ResponseBody</span></span><br><span class="line">    <span class="keyword">public</span> String <span class="title function_">commonParam</span><span class="params">(String name)</span>&#123;</span><br><span class="line">        System.out.println(<span class="string">&quot;普通参数传递name --&gt; &quot;</span> + name);</span><br><span class="line">        <span class="keyword">return</span> <span class="string">&quot;&#123;&#x27;module&#x27;:&#x27;commonParam&#x27;&#125;&quot;</span>;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>注意get请求的key需与commonParam中的形参名一致<br>控制台输出<code>普通参数传递name --&gt; Jerry</code></p></li></ul></li><li><p>GET发送多个参数</p><ul><li><p>发送请求与参数：<code>localhost:8080/user/commonParam?name=Jerry&amp;age=18</code></p></li><li><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></pre></td><td class="code"><pre><span class="line"><span class="meta">@Controller</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">@RequestMapping(&quot;/commonParam&quot;)</span></span><br><span class="line">    <span class="meta">@ResponseBody</span></span><br><span class="line">    <span class="keyword">public</span> String <span class="title function_">commonParam</span><span class="params">(String name,<span class="type">int</span> age)</span>&#123;</span><br><span class="line">        System.out.println(<span class="string">&quot;普通参数传递name --&gt; &quot;</span> + name);</span><br><span class="line">        System.out.println(<span class="string">&quot;普通参数传递age --&gt; &quot;</span> + age);</span><br><span class="line">        <span class="keyword">return</span> <span class="string">&quot;&#123;&#x27;module&#x27;:&#x27;commonParam&#x27;&#125;&quot;</span>;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>控制台输出</p><blockquote><p>普通参数传递name —&gt; Jerry<br>普通参数传递age —&gt; 18</p></blockquote></li></ul></li><li><p>POST发送参数</p><div class="img-wrap"><div class="img-bg"><img class="img" src="https://pic.imgdb.cn/item/6319f77f16f2c2beb187c5c2.jpg"/></div></div></li><li><p>接收参数<br>和GET一致，不用做任何修改</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">@Controller</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">@RequestMapping(&quot;/commonParam&quot;)</span></span><br><span class="line">    <span class="meta">@ResponseBody</span></span><br><span class="line">    <span class="keyword">public</span> String <span class="title function_">commonParam</span><span class="params">(String name,<span class="type">int</span> age)</span>&#123;</span><br><span class="line">        System.out.println(<span class="string">&quot;普通参数传递name --&gt; &quot;</span> + name);</span><br><span class="line">        System.out.println(<span class="string">&quot;普通参数传递age --&gt; &quot;</span> + age);</span><br><span class="line">        <span class="keyword">return</span> <span class="string">&quot;&#123;&#x27;module&#x27;:&#x27;commonParam&#x27;&#125;&quot;</span>;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>控制台输出如下</p><blockquote><p>普通参数传递name —&gt; Tom<br>普通参数传递age —&gt; 19</p></blockquote></li><li><p>POST请求中文乱码<br>如果我们在发送post请求的时候，使用了中文，则会出现乱码</p></li><li><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_">ServletContainersInitConfig</span> <span class="keyword">extends</span> <span class="title class_">AbstractAnnotationConfigDispatcherServletInitializer</span> &#123;</span><br><span class="line">    <span class="keyword">protected</span> Class&lt;?&gt;[] getRootConfigClasses() &#123;</span><br><span class="line">        <span class="keyword">return</span> <span class="keyword">new</span> <span class="title class_">Class</span>[<span class="number">0</span>];</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">protected</span> Class&lt;?&gt;[] getServletConfigClasses() &#123;</span><br><span class="line">        <span class="keyword">return</span> <span class="keyword">new</span> <span class="title class_">Class</span>[]&#123;SpringMvcConfig.class&#125;;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">protected</span> String[] getServletMappings() &#123;</span><br><span class="line">        <span class="keyword">return</span> <span class="keyword">new</span> <span class="title class_">String</span>[]&#123;<span class="string">&quot;/&quot;</span>&#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">@Override</span></span><br><span class="line">    <span class="keyword">protected</span> Filter[] getServletFilters() &#123;</span><br><span class="line">        <span class="type">CharacterEncodingFilter</span> <span class="variable">filter</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">CharacterEncodingFilter</span>();</span><br><span class="line">        filter.setEncoding(<span class="string">&quot;utf-8&quot;</span>);</span><br><span class="line">        <span class="keyword">return</span> <span class="keyword">new</span> <span class="title class_">Filter</span>[]&#123;filter&#125;;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>重启Tomcat服务器，并发送post请求，使用中文，控制台输出如下</p><blockquote><p>普通参数传递name —&gt; 张三<br>普通参数传递age —&gt; 19</p></blockquote></li></ul><h2 id="五种类型参数传递"><a href="#五种类型参数传递" class="headerlink" title="五种类型参数传递"></a>五种类型参数传递</h2><p>前面我们使用GET或POST来发送请求和数据，所携带的数据都是比较简单的数据，接下来在这个基础上，我们来研究一些比较复杂的参数传递，常见的参数种类有</p><ul><li>普通类型</li><li>POJO类型参数</li><li>嵌套POJO类型参数</li><li>数组类型参数</li><li>集合类型参数</li></ul><p>下面我们就来挨个学习这五种类型参数如何发送，后台如何接收</p><h3 id="普通类型"><a href="#普通类型" class="headerlink" title="普通类型"></a>普通类型</h3><p>普通参数：url地址传参，地址参数名与形参变量名相同，在方法上添加形参即可接收参数。</p><ul><li><p>发送请求与参数：<code>localhost:8080/user/commonParam?name=Helsing&amp;age=1024</code></p></li><li><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></pre></td><td class="code"><pre><span class="line"><span class="meta">@Controller</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">@RequestMapping(&quot;/commonParam&quot;)</span></span><br><span class="line">    <span class="meta">@ResponseBody</span></span><br><span class="line">    <span class="keyword">public</span> String <span class="title function_">commonParam</span><span class="params">(String name,<span class="type">int</span> age)</span>&#123;</span><br><span class="line">        System.out.println(<span class="string">&quot;普通参数传递name --&gt; &quot;</span> + name);</span><br><span class="line">        System.out.println(<span class="string">&quot;普通参数传递age --&gt; &quot;</span> + age);</span><br><span class="line">        <span class="keyword">return</span> <span class="string">&quot;&#123;&#x27;module&#x27;:&#x27;commonParam&#x27;&#125;&quot;</span>;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></li><li><p>控制台输出</p></li></ul><blockquote><p>普通参数传递name —&gt; Helsing<br>普通参数传递age —&gt; 1024</p></blockquote><p>如果形参与地址参数名不一致该如何解决?例如地址参数名为<code>username</code>，而形参变量名为<code>name</code>，因为前端给的是<code>username</code>，后台接收使用的是<code>name</code>,两个名称对不上，会导致接收数据失败</p><ul><li><p>解决方案：使用<code>@RequestParam</code>注解</p><ul><li><p>发送请求与参数：<code>localhost:8080/user/commonParam?username=Helsing&amp;age=1024</code></p></li><li><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></pre></td><td class="code"><pre><span class="line"><span class="meta">@Controller</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">@RequestMapping(&quot;/commonParam&quot;)</span></span><br><span class="line">    <span class="meta">@ResponseBody</span></span><br><span class="line">    <span class="keyword">public</span> String <span class="title function_">commonParam</span><span class="params">(<span class="meta">@RequestParam(&quot;username&quot;)</span> String name, <span class="type">int</span> age)</span>&#123;</span><br><span class="line">        System.out.println(<span class="string">&quot;普通参数传递name --&gt; &quot;</span> + name);</span><br><span class="line">        System.out.println(<span class="string">&quot;普通参数传递age --&gt; &quot;</span> + age);</span><br><span class="line">        <span class="keyword">return</span> <span class="string">&quot;&#123;&#x27;module&#x27;:&#x27;commonParam&#x27;&#125;&quot;</span>;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></li></ul></li></ul><h3 id="POJO数据类型"><a href="#POJO数据类型" class="headerlink" title="POJO数据类型"></a>POJO数据类型</h3><p>简单数据类型一般处理的是参数个数比较少的请求，如果参数比较多，那么后台接收参数的时候就比较复杂，这个时候我们可以考虑使用POJO数据类型。</p><ul><li>POJO参数：<code>请求参数名</code>与<code>形参对象的属性名</code>相同，定义POJO类型形参即可接收参数</li></ul><p>此时需要使用前面准备好的两个POJO类</p><div class="tabs" id="pojo数据类型"><ul class="nav-tabs"><li class="tab active"><button type="button" data-href="#pojo数据类型-1">User</button></li><li class="tab"><button type="button" data-href="#pojo数据类型-2">Address</button></li></ul><div class="tab-contents"><div class="tab-item-content active" id="pojo数据类型-1"><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><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="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> 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="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 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> 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="built_in">this</span>.age = age;</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;name=&#x27;&quot;</span> + name + <span class="string">&#x27;\&#x27;&#x27;</span> +</span><br><span class="line">                <span class="string">&quot;, age=&quot;</span> + age +</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><button type="button" class="tab-to-top" aria-label="scroll to top"><i class="fas fa-arrow-up"></i></button></div><div class="tab-item-content" id="pojo数据类型-2"><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><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="keyword">public</span> <span class="keyword">class</span> <span class="title class_">Address</span> &#123;</span><br><span class="line">    <span class="keyword">private</span> String province;</span><br><span class="line">    <span class="keyword">private</span> String city;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">public</span> String <span class="title function_">getProvince</span><span class="params">()</span> &#123;</span><br><span class="line">        <span class="keyword">return</span> province;</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_">setProvince</span><span class="params">(String province)</span> &#123;</span><br><span class="line">        <span class="built_in">this</span>.province = province;</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_">getCity</span><span class="params">()</span> &#123;</span><br><span class="line">        <span class="keyword">return</span> city;</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_">setCity</span><span class="params">(String city)</span> &#123;</span><br><span class="line">        <span class="built_in">this</span>.city = city;</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_">Address</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_">Address</span><span class="params">(String province, String city)</span> &#123;</span><br><span class="line">        <span class="built_in">this</span>.province = province;</span><br><span class="line">        <span class="built_in">this</span>.city = city;</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;Address&#123;&quot;</span> +</span><br><span class="line">                <span class="string">&quot;province=&#x27;&quot;</span> + province + <span class="string">&#x27;\&#x27;&#x27;</span> +</span><br><span class="line">                <span class="string">&quot;, city=&#x27;&quot;</span> + city + <span class="string">&#x27;\&#x27;&#x27;</span> +</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><button type="button" class="tab-to-top" aria-label="scroll to top"><i class="fas fa-arrow-up"></i></button></div></div></div><ul><li><p>发送请求和参数：<code>localhost:8080/user/pojoParam?name=Helsing&amp;age=1024</code></p></li><li><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></pre></td><td class="code"><pre><span class="line"><span class="comment">//POJO参数：请求参数与形参对象中的属性对应即可完成参数传递</span></span><br><span class="line"><span class="meta">@RequestMapping(&quot;/pojoParam&quot;)</span></span><br><span class="line"><span class="meta">@ResponseBody</span></span><br><span class="line"><span class="keyword">public</span> String <span class="title function_">pojoParam</span><span class="params">(User user)</span>&#123;</span><br><span class="line">    System.out.println(<span class="string">&quot;POJO参数传递user --&gt; &quot;</span> + user);</span><br><span class="line">    <span class="keyword">return</span> <span class="string">&quot;&#123;&#x27;module&#x27;:&#x27;pojo param&#x27;&#125;&quot;</span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></li><li><p>控制台输出如下</p><blockquote><p>POJO参数传递user —&gt; User{name=’Helsing’, age=1024}</p></blockquote></li></ul><div class="note warning no-icon flat"><ul><li>注意:<ul><li>POJO参数接收，前端GET和POST发送请求数据的方式不变。</li><li>请求参数key的名称要和POJO中属性的名称一致，否则无法封装。</li></ul></li></ul></div><h3 id="嵌套POJO类型"><a href="#嵌套POJO类型" class="headerlink" title="嵌套POJO类型"></a>嵌套POJO类型</h3><ul><li>嵌套POJO类型即一个定义的引用类型中还有一个属性类型为引用类型</li></ul><ul><li><p>环境准备<br>先将之前写的Address类，嵌套在User类中</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="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> 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="keyword">private</span> Address address;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">public</span> Address <span class="title function_">getAddress</span><span class="params">()</span> &#123;</span><br><span class="line">        <span class="keyword">return</span> address;</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_">setAddress</span><span class="params">(Address address)</span> &#123;</span><br><span class="line">        <span class="built_in">this</span>.address = address;</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_">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="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="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 name, <span class="type">int</span> age, Address address)</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>.address = address;</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;name=&#x27;&quot;</span> + name + <span class="string">&#x27;\&#x27;&#x27;</span> +</span><br><span class="line">                <span class="string">&quot;, age=&quot;</span> + age +</span><br><span class="line">                <span class="string">&quot;, address=&quot;</span> + address +</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></li><li><p>嵌套POJO参数：请求参数名与形参对象属性名相同，按照<code>对象层次结构关系</code>即可接收嵌套POJO属性参数</p></li><li><p>发送请求和参数：<code>localhost:8080/user/pojoParam?name=Helsing&amp;age=1024&amp;address.province=Beijing&amp;address.city=Beijing</code></p></li><li><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></pre></td><td class="code"><pre><span class="line"><span class="meta">@RequestMapping(&quot;/pojoParam&quot;)</span></span><br><span class="line"><span class="meta">@ResponseBody</span></span><br><span class="line"><span class="keyword">public</span> String <span class="title function_">pojoParam</span><span class="params">(User user)</span>&#123;</span><br><span class="line">    System.out.println(<span class="string">&quot;POJO参数传递user --&gt; &quot;</span> + user);</span><br><span class="line">    <span class="keyword">return</span> <span class="string">&quot;&#123;&#x27;module&#x27;:&#x27;pojo param&#x27;&#125;&quot;</span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></li><li><p>控制台输出如下</p><blockquote><p>POJO参数传递user —&gt; User{name=’Helsing’, age=1024, address=Address{province=’Beijing’, city=’Beijing’}}</p></blockquote></li></ul><div class="note warning no-icon flat"><p>注意：请求参数key的名称要和POJO中属性的名称一致，否则无法封装</p></div><h3 id="数组类型"><a href="#数组类型" class="headerlink" title="数组类型"></a>数组类型</h3><p>举个简单的例子，如果前端需要获取用户的爱好，爱好绝大多数情况下都是多选，如何发送请求数据和接收数据呢?</p><ul><li>数组参数：请求参数名与形参对象属性名相同且请求参数为多个，定义数组类型即可接收参数</li><li>发送请求和参数：<code>localhost:8080/user/arrayParam?hobbies=sing&amp;hobbies=jump&amp;hobbies=rap&amp;hobbies=basketball</code></li><li><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></pre></td><td class="code"><pre><span class="line"><span class="meta">@RequestMapping(&quot;/arrayParam&quot;)</span></span><br><span class="line"><span class="meta">@ResponseBody</span></span><br><span class="line"><span class="keyword">public</span> String <span class="title function_">arrayParam</span><span class="params">(String[] hobbies)</span>&#123;</span><br><span class="line">    System.out.println(<span class="string">&quot;数组参数传递user --&gt; &quot;</span> + Arrays.toString(hobbies));</span><br><span class="line">    <span class="keyword">return</span> <span class="string">&quot;&#123;&#x27;module&#x27;:&#x27;array param&#x27;&#125;&quot;</span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></li><li><p>控制台输出如下</p><blockquote><p>数组参数传递user —&gt; [sing, jump, rap, basketball]</p></blockquote></li></ul><h3 id="集合类型"><a href="#集合类型" class="headerlink" title="集合类型"></a>集合类型</h3><p>数组能接收多个值，那么集合是否也可以实现这个功能呢?</p><ul><li><p>发送请求和参数：<code>localhost:8080/user/listParam?hobbies=sing&amp;hobbies=jump&amp;hobbies=rap&amp;hobbies=basketball</code></p></li><li><p>后台接收参数</p></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="meta">@RequestMapping(&quot;/listParam&quot;)</span></span><br><span class="line"><span class="meta">@ResponseBody</span></span><br><span class="line"><span class="keyword">public</span> String <span class="title function_">listParam</span><span class="params">(List hobbies)</span> &#123;</span><br><span class="line">    System.out.println(<span class="string">&quot;集合参数传递user --&gt; &quot;</span> + hobbies);</span><br><span class="line">    <span class="keyword">return</span> <span class="string">&quot;&#123;&#x27;module&#x27;:&#x27;list param&#x27;&#125;&quot;</span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><ul><li><p>运行程序，报错<code>java.lang.IllegalArgumentException: Cannot generate variable name for non-typed Collection parameter type</code></p><ul><li><strong>错误原因：SpringMVC将List看做是一个POJO对象来处理，将其创建一个对象并准备把前端的数据封装到对象中，但是List是一个接口无法创建对象，所以报错。</strong></li></ul></li><li><p>解决方案是：使用<code>@RequestParam</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></pre></td><td class="code"><pre><span class="line"><span class="meta">@RequestMapping(&quot;/listParam&quot;)</span></span><br><span class="line"><span class="meta">@ResponseBody</span></span><br><span class="line"><span class="keyword">public</span> String <span class="title function_">listParam</span><span class="params">(<span class="meta">@RequestParam</span> List hobbies)</span> &#123;</span><br><span class="line">    System.out.println(<span class="string">&quot;集合参数传递user --&gt; &quot;</span> + hobbies);</span><br><span class="line">    <span class="keyword">return</span> <span class="string">&quot;&#123;&#x27;module&#x27;:&#x27;list param&#x27;&#125;&quot;</span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></li><li><p>控制台输出如下</p><blockquote><p>集合参数传递user —&gt; [sing, jump, rap, basketball]</p></blockquote></li></ul><p>知识点：<code>@RequestParam</code></p><div class="table-container"><table><thead><tr><th style="text-align:center">名称</th><th style="text-align:center">@RequestParam</th></tr></thead><tbody><tr><td style="text-align:center">类型</td><td style="text-align:center">形参注解</td></tr><tr><td style="text-align:center">位置</td><td style="text-align:center">SpringMVC控制器方法形参定义前面</td></tr><tr><td style="text-align:center">作用</td><td style="text-align:center">绑定请求参数与处理器方法形参间的关系</td></tr><tr><td style="text-align:center">相关参数</td><td style="text-align:center">required：是否为必传参数 defaultValue：参数默认值</td></tr></tbody></table></div><h2 id="JSON数据传输参数"><a href="#JSON数据传输参数" class="headerlink" title="JSON数据传输参数"></a>JSON数据传输参数</h2><div class="note info no-icon flat"><p>现在比较流行的开发方式为<code>异步调用</code>。前后台以异步方式进行交换，传输的数据使用的是JSON，所以前端如果发送的是JSON数据，后端该如何接收?</p></div><p>对于JSON数据类型，常见的有三种:</p><ul><li>json普通数组（[“value1”,”value2”,”value3”,…]）</li><li>json对象（{key1:value1,key2:value2,…}）</li><li>json对象数组（[{key1:value1,…},{key2:value2,…}]）</li></ul><p>下面学习对于以上三种数据类型，前端如何发送，后端如何接收</p><h3 id="JSON普通数组"><a href="#JSON普通数组" class="headerlink" title="JSON普通数组"></a>JSON普通数组</h3><ul><li><p><code>步骤一：</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></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.fasterxml.jackson.core<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>jackson-databind<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.9.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></li><li><p><code>步骤二：</code>开启SpringMVC注解支持<br>使用<code>@EnableWebMvc</code>，在SpringMVC的配置类中开启SpringMVC的注解支持，这里面就包含了将JSON转换成对象的功能。</p></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="meta">@Configuration</span></span><br><span class="line"><span class="meta">@ComponentScan(&quot;com.blog.controller&quot;)</span></span><br><span class="line"><span class="comment">//开启json数据类型自动转换</span></span><br><span class="line"><span class="meta">@EnableWebMvc</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">SpringMvcConfig</span> &#123;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><ul><li><p><code>步骤三：</code>使用PostMan发送JSON数据</p><div class="img-wrap"><div class="img-bg"><img class="img" src="https://pic.imgdb.cn/item/631addfe16f2c2beb1732627.jpg"/></div></div></li><li><p><code>步骤四：</code>后台接收参数，参数前添加<code>@RequestBody</code><br>使用<code>@RequestBody</code>注解将外部传递的json数组数据映射到形参的集合对象中作为数据</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></pre></td><td class="code"><pre><span class="line"><span class="meta">@RequestMapping(&quot;/jsonArrayParam&quot;)</span></span><br><span class="line"><span class="meta">@ResponseBody</span></span><br><span class="line"><span class="keyword">public</span> String <span class="title function_">jsonArrayParam</span><span class="params">(<span class="meta">@RequestBody</span> List&lt;String&gt; hobbies)</span> &#123;</span><br><span class="line">    System.out.println(<span class="string">&quot;JSON数组参数传递hobbies --&gt; &quot;</span> + hobbies);</span><br><span class="line">    <span class="keyword">return</span> <span class="string">&quot;&#123;&#x27;module&#x27;:&#x27;json array param&#x27;&#125;&quot;</span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>控制台输出如下</p><blockquote><p>JSON数组参数传递hobbies —&gt; [唱, 跳, Rap, 篮球]</p></blockquote></li></ul><h3 id="JSON对象"><a href="#JSON对象" class="headerlink" title="JSON对象"></a>JSON对象</h3><ul><li><p>请求和数据的发送:</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></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;菲茨罗伊&quot;</span><span class="punctuation">,</span></span><br><span class="line">    <span class="attr">&quot;age&quot;</span><span class="punctuation">:</span><span class="string">&quot;27&quot;</span><span class="punctuation">,</span></span><br><span class="line">    <span class="attr">&quot;address&quot;</span><span class="punctuation">:</span><span class="punctuation">&#123;</span></span><br><span class="line">        <span class="attr">&quot;city&quot;</span><span class="punctuation">:</span><span class="string">&quot;萨尔沃&quot;</span><span class="punctuation">,</span></span><br><span class="line">        <span class="attr">&quot;province&quot;</span><span class="punctuation">:</span><span class="string">&quot;外域&quot;</span></span><br><span class="line">    <span class="punctuation">&#125;</span></span><br><span class="line"><span class="punctuation">&#125;</span></span><br></pre></td></tr></table></figure></li><li><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></pre></td><td class="code"><pre><span class="line"><span class="meta">@RequestMapping(&quot;/jsonPojoParam&quot;)</span></span><br><span class="line"><span class="meta">@ResponseBody</span></span><br><span class="line"><span class="keyword">public</span> String <span class="title function_">jsonPojoParam</span><span class="params">(<span class="meta">@RequestBody</span> User user)</span> &#123;</span><br><span class="line">    System.out.println(<span class="string">&quot;JSON对象参数传递user --&gt; &quot;</span> + user);</span><br><span class="line">    <span class="keyword">return</span> <span class="string">&quot;&#123;&#x27;module&#x27;:&#x27;json pojo param&#x27;&#125;&quot;</span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>控制台输出如下</p><blockquote><p>JSON对象参数传递user —&gt; User{name=’菲茨罗伊’, age=27, address=Address{province=’外域’, city=’萨尔沃’}}</p></blockquote></li></ul><h3 id="JSON对象数组"><a href="#JSON对象数组" class="headerlink" title="JSON对象数组"></a>JSON对象数组</h3><ul><li><p>发送请求和数据</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><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><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="punctuation">[</span></span><br><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;菲茨罗伊&quot;</span><span class="punctuation">,</span></span><br><span class="line">        <span class="attr">&quot;age&quot;</span><span class="punctuation">:</span><span class="string">&quot;27&quot;</span><span class="punctuation">,</span></span><br><span class="line">        <span class="attr">&quot;address&quot;</span><span class="punctuation">:</span><span class="punctuation">&#123;</span></span><br><span class="line">            <span class="attr">&quot;city&quot;</span><span class="punctuation">:</span><span class="string">&quot;萨尔沃&quot;</span><span class="punctuation">,</span></span><br><span class="line">            <span class="attr">&quot;province&quot;</span><span class="punctuation">:</span><span class="string">&quot;外域&quot;</span></span><br><span class="line">        <span class="punctuation">&#125;</span></span><br><span class="line">    <span class="punctuation">&#125;</span><span class="punctuation">,</span></span><br><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;地平线&quot;</span><span class="punctuation">,</span></span><br><span class="line">        <span class="attr">&quot;age&quot;</span><span class="punctuation">:</span><span class="string">&quot;136&quot;</span><span class="punctuation">,</span></span><br><span class="line">        <span class="attr">&quot;address&quot;</span><span class="punctuation">:</span><span class="punctuation">&#123;</span></span><br><span class="line">            <span class="attr">&quot;city&quot;</span><span class="punctuation">:</span><span class="string">&quot;奥林匹斯&quot;</span><span class="punctuation">,</span></span><br><span class="line">            <span class="attr">&quot;province&quot;</span><span class="punctuation">:</span><span class="string">&quot;外域&quot;</span></span><br><span class="line">        <span class="punctuation">&#125;</span></span><br><span class="line">    <span class="punctuation">&#125;</span></span><br><span class="line"><span class="punctuation">]</span></span><br></pre></td></tr></table></figure></li><li><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></pre></td><td class="code"><pre><span class="line"><span class="meta">@RequestMapping(&quot;/jsonPojoListParam&quot;)</span></span><br><span class="line"><span class="meta">@ResponseBody</span></span><br><span class="line"><span class="keyword">public</span> String <span class="title function_">jsonPojoListParam</span><span class="params">(<span class="meta">@RequestBody</span> List&lt;User&gt; users)</span> &#123;</span><br><span class="line">    System.out.println(<span class="string">&quot;JSON对象数组参数传递user --&gt; &quot;</span> + users);</span><br><span class="line">    <span class="keyword">return</span> <span class="string">&quot;&#123;&#x27;module&#x27;:&#x27;json pojo list param&#x27;&#125;&quot;</span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>控制台输出如下</p><blockquote><p>JSON对象数组参数传递user —&gt; [User{name=’菲茨罗伊’, age=27, address=Address{province=’外域’, city=’萨尔沃’}}, User{name=’地平线’, age=136, address=Address{province=’外域’, city=’奥林匹斯’}}]</p></blockquote></li></ul><h3 id="小结"><a href="#小结" class="headerlink" title="小结"></a>小结</h3><p>SpringMVC接收JSON数据的实现步骤为:</p><ol><li>导入jackson包</li><li>开启SpringMVC注解驱动，在配置类上添加<code>@EnableWebMvc</code>注解</li><li>使用PostMan发送JSON数据</li><li>Controller方法的参数前添加<code>@RequestBody</code>注解</li></ol><p>知识点1：<code>@EnableWebMvc</code></p><div class="table-container"><table><thead><tr><th style="text-align:center">名称</th><th style="text-align:center">@EnableWebMvc</th></tr></thead><tbody><tr><td style="text-align:center">类型</td><td style="text-align:center">配置类注解</td></tr><tr><td style="text-align:center">位置</td><td style="text-align:center">SpringMVC配置类定义上方</td></tr><tr><td style="text-align:center">作用</td><td style="text-align:center">开启SpringMVC多项辅助功能</td></tr></tbody></table></div><p>知识点2：<code>@RequestBody</code></p><div class="table-container"><table><thead><tr><th style="text-align:center">名称</th><th style="text-align:center">@RequestBody</th></tr></thead><tbody><tr><td style="text-align:center">类型</td><td style="text-align:center">形参注解</td></tr><tr><td style="text-align:center">位置</td><td style="text-align:center">SpringMVC控制器方法形参定义前面</td></tr><tr><td style="text-align:center">作用</td><td style="text-align:center">将请求中请求体所包含的数据传递给请求参数，此注解一个处理器方法只能使用一次</td></tr></tbody></table></div><div class="note info no-icon flat"><p><code>@RequestBody</code>与<code>@RequestParam</code>区别</p><ul><li>区别<ul><li><code>@RequestParam</code>用于接收url地址传参，表单传参【application/x-www-form-urlencoded】</li><li><code>@RequestBody</code>用于接收json数据【application/json】</li></ul></li><li>应用<ul><li>后期开发中，发送json格式数据为主，<code>@RequestBody</code>应用较广</li><li>如果发送非json格式数据，选用<code>@RequestParam</code>接收请求参数</li></ul></li></ul></div><h2 id="日期类型参数传递"><a href="#日期类型参数传递" class="headerlink" title="日期类型参数传递"></a>日期类型参数传递</h2><p>日期类型比较特殊，因为对于日期的格式有N种输入方式，比如</p><ul><li>2088-08-18</li><li>2088/08/18</li><li>08/18/2088</li><li>…</li></ul><p>针对这么多日期格式，SpringMVC该如何接收呢？下面我们来开始学习</p><ul><li><p><code>步骤一：</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></pre></td><td class="code"><pre><span class="line"><span class="meta">@RequestMapping(&quot;/dateParam&quot;)</span></span><br><span class="line"><span class="meta">@ResponseBody</span></span><br><span class="line"><span class="keyword">public</span> String <span class="title function_">dateParam</span><span class="params">(Date date)</span> &#123;</span><br><span class="line">    System.out.println(<span class="string">&quot;参数传递date --&gt; &quot;</span> + date);</span><br><span class="line">    <span class="keyword">return</span> <span class="string">&quot;&#123;&#x27;module&#x27;:&#x27;date param&#x27;&#125;&quot;</span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></li><li><p><code>步骤二：</code>启动Tomcat服务器</p></li><li><p><code>步骤三：</code>使用PostMan发送请求：<code>localhost:8080/user/dateParam?date=2077/12/21</code></p></li><li><p><code>步骤四：</code>查看控制台，输出如下</p><blockquote><p>参数传递date —&gt; Tue Dec 21 00:00:00 CST 2077</p></blockquote></li><li><p><code>步骤五：</code>更换日期格式<br>为了能更好的看到程序运行的结果，我们在方法中多添加一个日期参数</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="meta">@RequestMapping(&quot;/dateParam&quot;)</span></span><br><span class="line"><span class="meta">@ResponseBody</span></span><br><span class="line"><span class="keyword">public</span> String <span class="title function_">dateParam</span><span class="params">(Date date1,Date date2)</span> &#123;</span><br><span class="line">    System.out.println(<span class="string">&quot;参数传递date1 --&gt; &quot;</span> + date1);</span><br><span class="line">    System.out.println(<span class="string">&quot;参数传递date2 --&gt; &quot;</span> + date2);</span><br><span class="line">    <span class="keyword">return</span> <span class="string">&quot;&#123;&#x27;module&#x27;:&#x27;date param&#x27;&#125;&quot;</span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>使用PostMan发送请求，携带两个不同的日期格式，<code>localhost:8080/user/dateParam?date1=2077/12/21&amp;date2=1997-02-13</code><br>发送请求和数据后，页面会报400，<code>The request sent by the client was syntactically incorrect.</code><br>错误的原因是将<code>1997-02-13</code>转换成日期类型的时候失败了，原因是SpringMVC默认支持的字符串转日期的格式为<code>yyyy/MM/dd</code>,而我们现在传递的不符合其默认格式，SpringMVC就无法进行格式转换，所以报错。<br>解决方案也比较简单，需要使用<code>@DateTimeFormat</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></pre></td><td class="code"><pre><span class="line"><span class="meta">@RequestMapping(&quot;/dateParam&quot;)</span></span><br><span class="line"><span class="meta">@ResponseBody</span></span><br><span class="line"><span class="keyword">public</span> String <span class="title function_">dateParam</span><span class="params">(Date date1,<span class="meta">@DateTimeFormat(pattern = &quot;yyyy-MM-dd&quot;)</span> Date date2)</span> &#123;</span><br><span class="line">    System.out.println(<span class="string">&quot;参数传递date1 --&gt; &quot;</span> + date1);</span><br><span class="line">    System.out.println(<span class="string">&quot;参数传递date2 --&gt; &quot;</span> + date2);</span><br><span class="line">    <span class="keyword">return</span> <span class="string">&quot;&#123;&#x27;module&#x27;:&#x27;date param&#x27;&#125;&quot;</span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>重新发送请求与数据，控制台输出如下，问题解决</p><blockquote><p>参数传递date1 —&gt; Tue Dec 21 00:00:00 CST 2077<br>参数传递date2 —&gt; Thu Feb 13 00:00:00 CST 1997</p></blockquote></li><li><p><code>步骤六：</code>携带具体时间的日期<br>接下来我们再来发送一个携带具体时间的日期，如<code>localhost:8080/user/dateParam?date1=2077/12/21&amp;date2=1997-02-13&amp;date3=2022/09/09 16:34:07</code>，那么SpringMVC该怎么处理呢？<br>继续修改UserController类，添加第三个参数，同时使用<code>@DateTimeFormat</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></pre></td><td class="code"><pre><span class="line"><span class="meta">@RequestMapping(&quot;/dateParam&quot;)</span></span><br><span class="line"><span class="meta">@ResponseBody</span></span><br><span class="line"><span class="keyword">public</span> String <span class="title function_">dateParam</span><span class="params">(Date date1,</span></span><br><span class="line"><span class="params">                        <span class="meta">@DateTimeFormat(pattern = &quot;yyyy-MM-dd&quot;)</span> Date date2,</span></span><br><span class="line"><span class="params">                        <span class="meta">@DateTimeFormat(pattern =&quot;yyyy/MM/dd HH:mm:ss&quot;)</span> Date date3)</span> &#123;</span><br><span class="line">    System.out.println(<span class="string">&quot;参数传递date1 --&gt; &quot;</span> + date1);</span><br><span class="line">    System.out.println(<span class="string">&quot;参数传递date2 --&gt; &quot;</span> + date2);</span><br><span class="line">    System.out.println(<span class="string">&quot;参数传递date3 --&gt; &quot;</span> + date3);</span><br><span class="line">    <span class="keyword">return</span> <span class="string">&quot;&#123;&#x27;module&#x27;:&#x27;date param&#x27;&#125;&quot;</span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>控制台输出如下</p><blockquote><p>参数传递date1 —&gt; Tue Dec 21 00:00:00 CST 2077<br>参数传递date2 —&gt; Thu Feb 13 00:00:00 CST 1997<br>参数传递date3 —&gt; Fri Sep 09 16:34:07 CST 2022</p></blockquote></li></ul><p>知识点：<code>@DateTimeFormat</code></p><div class="table-container"><table><thead><tr><th style="text-align:center">名称</th><th style="text-align:center">@DateTimeFormat</th></tr></thead><tbody><tr><td style="text-align:center">类型</td><td style="text-align:center">形参注解</td></tr><tr><td style="text-align:center">位置</td><td style="text-align:center">SpringMVC控制器方法形参前面</td></tr><tr><td style="text-align:center">作用</td><td style="text-align:center">设定日期时间型数据格式</td></tr><tr><td style="text-align:center">相关属性</td><td style="text-align:center">pattern：指定日期时间格式字符串</td></tr></tbody></table></div><h3 id="内部实现原理"><a href="#内部实现原理" class="headerlink" title="内部实现原理"></a>内部实现原理</h3><p>我们首先先来思考一个问题:</p><ul><li>前端传递字符串，后端使用日期Date接收</li><li>前端传递JSON数据，后端使用对象接收</li><li>前端传递字符串，后端使用Integer接收</li><li>后台需要的数据类型有很多种</li><li>在数据的传递过程中存在很多类型的转换</li></ul><p><code>问</code>:谁来做这个类型转换?</p><ul><li><code>答</code>:SpringMVC</li></ul><p><code>问</code>:SpringMVC是如何实现类型转换的?</p><ul><li><code>答</code>:SpringMVC中提供了很多类型转换接口和实现类</li></ul><p>在框架中，有一些类型转换接口，其中有:</p><ol><li><p><code>Converter</code>接口</p><p>注意：Converter所属的包为<code>org.springframework.core.convert.converter</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></pre></td><td class="code"><pre><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment">*    S: the source type</span></span><br><span class="line"><span class="comment">*    T: the target type</span></span><br><span class="line"><span class="comment">*/</span></span><br><span class="line"><span class="meta">@FunctionalInterface</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">interface</span> <span class="title class_">Converter</span>&lt;S, T&gt; &#123;</span><br><span class="line">    <span class="meta">@Nullable</span></span><br><span class="line">    <span class="comment">//该方法就是将从页面上接收的数据(S)转换成我们想要的数据类型(T)返回</span></span><br><span class="line">    T <span class="title function_">convert</span><span class="params">(S source)</span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>到了源码页面我们按<code>Ctrl+H</code>可以来看看<code>Converter</code>接口的层次结构</p><p>这里给我们提供了很多对应<code>Converter</code>接口的实现类，用来实现不同数据类型之间的转换</p><p><img src="data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7" alt="img"></p></li><li><p><code>HttpMessageConverter</code>接口<br>该接口是实现对象与JSON之间的转换工作<br>注意：需要在SpringMVC的配置类把<code>@EnableWebMvc</code>当做标配配置上去，不要省略</p></li></ol><h2 id="响应"><a href="#响应" class="headerlink" title="响应"></a>响应</h2><p>SpringMVC接收到请求和数据后，会进行一系列处理，当然这个处理可以是转发给Service，Service层再调用Dao层完成的，不管怎样，处理完以后，都需要将结果告知给用户。</p><p>比如：根据用户ID查询用户信息、查询用户列表、新增用户等。<br>对于响应，主要就包含两部分内容：</p><ul><li>响应页面</li><li>响应数据<ul><li>文本数据</li><li>json数据</li></ul></li></ul><p>因为异步调用是目前常用的主流方式，所以我们需要更关注的就是如何返回JSON数据，对于其他只需要认识了解即可。</p><h3 id="环境准备-3"><a href="#环境准备-3" class="headerlink" title="环境准备"></a>环境准备</h3><p>在之前的环境上加点东西就可以了</p><ul><li><p>在webapp下新建<code>page.jsp</code></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></pre></td><td class="code"><pre><span class="line">&lt;%@ page contentType=<span class="string">&quot;text/html;charset=UTF-8&quot;</span> language=<span class="string">&quot;java&quot;</span> %&gt;</span><br><span class="line">&lt;html&gt;</span><br><span class="line">&lt;head&gt;</span><br><span class="line">    &lt;title&gt;Title&lt;/title&gt;</span><br><span class="line">&lt;/head&gt;</span><br><span class="line">&lt;body&gt;</span><br><span class="line">HELLO SPRING MVC!!</span><br><span class="line">&lt;/body&gt;</span><br><span class="line">&lt;/html&gt;</span><br></pre></td></tr></table></figure></li><li><p>修改<code>UserController</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></pre></td><td class="code"><pre><span class="line"><span class="meta">@Controller</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">&#125;</span><br></pre></td></tr></table></figure></li></ul><h3 id="响应页面（了解）"><a href="#响应页面（了解）" class="headerlink" title="响应页面（了解）"></a>响应页面（了解）</h3><ul><li><p><code>步骤一：</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></pre></td><td class="code"><pre><span class="line"><span class="meta">@Controller</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 class="meta">@RequestMapping(&quot;/toJumpPage&quot;)</span></span><br><span class="line">    <span class="comment">//注意</span></span><br><span class="line">    <span class="comment">//1.此处不能添加@ResponseBody,如果加了该注入，会直接将page.jsp当字符串返回前端</span></span><br><span class="line">    <span class="comment">//2.方法需要返回String</span></span><br><span class="line">    <span class="keyword">public</span> String <span class="title function_">toJumpPage</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">        <span class="keyword">return</span> <span class="string">&quot;page.jsp&quot;</span>;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></li><li><p><code>步骤二：</code>启动程序测试<br>打开浏览器，访问<code>http://localhost:8080/toJumpPage</code><br>将跳转到<code>page.jsp</code>页面，并展示<code>page.jsp</code>页面的内容</p></li></ul><h3 id="返回文本数据（了解）"><a href="#返回文本数据（了解）" class="headerlink" title="返回文本数据（了解）"></a>返回文本数据（了解）</h3><ul><li><p><code>步骤一：</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="meta">@RequestMapping(&quot;toText&quot;)</span></span><br><span class="line"><span class="comment">//此时就需要添加@ResponseBody，将`response text`当成字符串返回给前端</span></span><br><span class="line"><span class="comment">//如果不写@ResponseBody，则会将response text当成页面名去寻找，找不到报404</span></span><br><span class="line"><span class="meta">@ResponseBody</span></span><br><span class="line"><span class="keyword">public</span> String <span class="title function_">toText</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">    <span class="keyword">return</span> <span class="string">&quot;response text&quot;</span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></li><li><p><code>步骤二：</code>启动程序测试<br>浏览器访问<code>http://localhost:8080/toText</code><br>页面上出现<code>response text</code>文本数据</p></li></ul><h3 id="响应JSON数据"><a href="#响应JSON数据" class="headerlink" title="响应JSON数据"></a>响应JSON数据</h3><ul><li><p>响应POJO对象</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">@RequestMapping(&quot;toJsonPojo&quot;)</span></span><br><span class="line"><span class="meta">@ResponseBody</span></span><br><span class="line"><span class="keyword">public</span> User <span class="title function_">toJsonPojo</span><span class="params">()</span>&#123;</span><br><span class="line">    System.out.println(<span class="string">&quot;返回json对象数据&quot;</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.setName(<span class="string">&quot;Helsing&quot;</span>);</span><br><span class="line">    user.setAge(<span class="number">9527</span>);</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>因为设置了<code>@ResponseBody</code>注解，所以返回的实体类会被转换为JSON格式数据</p></li><li><p>访问<code>http://localhost:8080/toJsonPojo</code>，页面上成功出现JSON类型数据</p><blockquote><p>{“name”:”Helsing”,”age”:9527,”address”:null}</p></blockquote></li></ul><p><code>HttpMessageConverter</code>接口帮我们实现了对象与JSON之间的转换工作，我们只需要在<code>SpringMvcConfig</code>配置类上加上<code>@EnableWebMvc</code>注解即可</p><ul><li><p>响应POJO集合对象</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="meta">@RequestMapping(&quot;toJsonList&quot;)</span></span><br><span class="line"><span class="meta">@ResponseBody</span></span><br><span class="line"><span class="keyword">public</span> List&lt;User&gt; <span class="title function_">toJsonList</span><span class="params">()</span>&#123;</span><br><span class="line">    List&lt;User&gt; users = <span class="keyword">new</span> <span class="title class_">ArrayList</span>&lt;User&gt;();</span><br><span class="line"></span><br><span class="line">    <span class="type">User</span> <span class="variable">user1</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">User</span>();</span><br><span class="line">    user1.setName(<span class="string">&quot;马文&quot;</span>);</span><br><span class="line">    user1.setAge(<span class="number">27</span>);</span><br><span class="line">    users.add(user1);</span><br><span class="line"></span><br><span class="line">    <span class="type">User</span> <span class="variable">user2</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">User</span>();</span><br><span class="line">    user2.setName(<span class="string">&quot;马武&quot;</span>);</span><br><span class="line">    user2.setAge(<span class="number">28</span>);</span><br><span class="line">    users.add(user2);</span><br><span class="line"></span><br><span class="line">    <span class="keyword">return</span> users;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></li><li><p>访问<code>http://localhost:8080/toJsonList</code>，页面上成功出现JSON集合类型数据</p><blockquote><p>[{“name”:”马文”,”age”:27,”address”:null},{“name”:”马武”,”age”:28,”address”:null}]</p></blockquote></li></ul><p>知识点：@ResponseBody</p><div class="table-container"><table><thead><tr><th style="text-align:center">名称</th><th style="text-align:center">@ResponseBody</th></tr></thead><tbody><tr><td style="text-align:center">类型</td><td style="text-align:center">方法\类注解</td></tr><tr><td style="text-align:center">位置</td><td style="text-align:center">SpringMVC控制器方法定义上方和控制类上</td></tr><tr><td style="text-align:center">作用</td><td style="text-align:center">设置当前控制器返回值作为响应体, 写在类上，该类的所有方法都有该注解功能</td></tr><tr><td style="text-align:center">相关属性</td><td style="text-align:center">pattern：指定日期时间格式字符串</td></tr></tbody></table></div><div class="note info no-icon flat"><p><strong>说明:</strong></p><ul><li>该注解可以写在类上或者方法上</li><li>写在类上就是该类下的所有方法都有<code>@ReponseBody</code>功能</li><li>当方法上有<code>@ReponseBody</code>注解后<ul><li>方法的返回值为字符串，会将其作为文本内容直接响应给前端</li><li>方法的返回值为对象，会将对象转换成JSON响应给前端</li></ul></li></ul><p>此处又使用到了类型转换，内部还是通过<code>HttpMessageConverter</code>接口完成的，所以<code>Converter</code>除了前面所说的功能外，它还可以实现:</p><ul><li>对象转Json数据(POJO -&gt; json)</li><li>集合转Json数据(Collection -&gt; json)</li></ul></div><h1 id="REST风格"><a href="#REST风格" class="headerlink" title="REST风格"></a>REST风格</h1><h2 id="REST简介"><a href="#REST简介" class="headerlink" title="REST简介"></a>REST简介</h2><p>REST是一种软件架构<code>风格</code><br>当我们想表示一个网络资源时，可以使用两种方式：</p><ul><li>传统风格资源描述形式<ul><li><code>http://localhost/user/getById?id=1</code> 查询id为1的用户信息</li><li><code>http://localhost/user/saveUser</code> 保存用户信息</li></ul></li><li>REST风格描述形式<ul><li><code>http://localhost/user/1</code></li><li><code>http://localhost/user</code></li></ul></li></ul><p>传统方式一般是一个请求url对应一种操作，这样做不仅麻烦，而且将信息直接显示在地址栏也不安全，通过请求的<code>URL</code>地址，就大致能推测出该<code>URL</code>实现的是什么操作<br>反观REST风格的描述，请求地址变简洁了，而且只看请求<code>URL</code>并不很容易能猜出来该<code>URL</code>的具体功能</p><p>所以<code>REST</code>的优点有：</p><ul><li>隐藏资源的访问行为，无法通过地址得知该资源是何种操作</li><li>书写简化</li></ul><p>那么问题也随之而来，一个相同的<code>URL</code>地址既可以是增加操作，也可以是修改或者查询，那么我们该如何区分该请求到底是什么操作呢？</p><ul><li><p>按照REST风格访问资源时，使用<code>行为动作</code>区分对资源进行了何种操作</p><ul><li><code>http://localhost/users</code> 查询全部用户信息 <code>GET</code>（查询）</li><li><code>http://localhost/users/1</code> 查询指定用户信息 <code>GET</code>（查询）</li><li><code>http://localhost/users</code> 添加用户信息 <code>POST</code>（新增/保存）</li><li><code>http://localhost/users</code> 修改用户信息 <code>PUT</code>（修改/更新）</li><li><code>http://localhost/users/1</code> 删除用户信息 <code>DELETE</code>（删除）</li></ul></li></ul><div class="note warning no-icon flat"><p>注意：</p><ul><li>上述行为是约定方式，约定不是规范，约定可以打破，所以成为<code>REST风格</code>，而不是<code>REST规范</code><ul><li>REST提供了对应的架构方式，按照这种架构方式设计项目可以降低开发的复杂性，提高系统的可伸缩性</li><li>REST中规定<code>GET</code>/<code>POST</code>/<code>PUT</code>/<code>DELETE</code>针对的是查询/新增/修改/删除，但如果我们非要使用<code>GET</code>请求做删除，这点在程序上运行是可以实现的</li><li>但是如果大多数人都遵循这种风格，你不遵循，那你写的代码在别人看来就有点莫名其妙了，所以最好还是遵循REST风格</li></ul></li><li>描述模块的名称通常使用复数，也就是加s的格式描述，表示此类的资源，而非单个的资源，例如<code>users</code>、<code>books</code>、<code>accounts</code>..</li></ul></div><p>搞清楚了什么是REST风格后，后面会经常提到一个概念叫<code>RESTful</code>，那么什么是<code>RESTful</code>呢？</p><ul><li>根据REST风格对资源进行访问称为<code>RESTful</code></li></ul><p>在我们后期的开发过程中，大多数都是遵循<code>REST</code>风格来访问我们的后台服务。</p><h2 id="RESTful入门案例"><a href="#RESTful入门案例" class="headerlink" title="RESTful入门案例"></a>RESTful入门案例</h2><h3 id="环境准备-4"><a href="#环境准备-4" class="headerlink" title="环境准备"></a>环境准备</h3><ul><li>新建一个web的maven项目</li><li><p>导入坐标</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></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>javax.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>javax.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>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 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<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-webmvc<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.2.10.RELEASE<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>com.fasterxml.jackson.core<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>jackson-databind<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.9.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></li><li><p>创建对应的配置类</p></li></ul><div class="tabs" id="restful环境"><ul class="nav-tabs"><li class="tab active"><button type="button" data-href="#restful环境-1">ServletContainersInitConfig</button></li><li class="tab"><button type="button" data-href="#restful环境-2">SpringMvcConfig</button></li></ul><div class="tab-contents"><div class="tab-item-content active" id="restful环境-1"><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><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_">ServletContainersInitConfig</span> <span class="keyword">extends</span> <span class="title class_">AbstractAnnotationConfigDispatcherServletInitializer</span> &#123;</span><br><span class="line">    <span class="keyword">protected</span> Class&lt;?&gt;[] getRootConfigClasses() &#123;</span><br><span class="line">        <span class="keyword">return</span> <span class="keyword">new</span> <span class="title class_">Class</span>[<span class="number">0</span>];</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">protected</span> Class&lt;?&gt;[] getServletConfigClasses() &#123;</span><br><span class="line">        <span class="keyword">return</span> <span class="keyword">new</span> <span class="title class_">Class</span>[]&#123;SpringMvcConfig.class&#125;;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">protected</span> String[] getServletMappings() &#123;</span><br><span class="line">        <span class="keyword">return</span> <span class="keyword">new</span> <span class="title class_">String</span>[]&#123;<span class="string">&quot;/&quot;</span>&#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">@Override</span></span><br><span class="line">    <span class="keyword">protected</span> Filter[] getServletFilters() &#123;</span><br><span class="line">        <span class="type">CharacterEncodingFilter</span> <span class="variable">filter</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">CharacterEncodingFilter</span>();</span><br><span class="line">        filter.setEncoding(<span class="string">&quot;utf-8&quot;</span>);</span><br><span class="line">        <span class="keyword">return</span> <span class="keyword">new</span> <span class="title class_">Filter</span>[]&#123;filter&#125;;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><button type="button" class="tab-to-top" aria-label="scroll to top"><i class="fas fa-arrow-up"></i></button></div><div class="tab-item-content" id="restful环境-2"><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><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="meta">@Configuration</span></span><br><span class="line"><span class="meta">@ComponentScan(&quot;com.blog.controller&quot;)</span></span><br><span class="line"><span class="comment">//开启JSON数据类型自动转换</span></span><br><span class="line"><span class="meta">@EnableWebMvc</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">SpringMvcConfig</span> &#123;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><button type="button" class="tab-to-top" aria-label="scroll to top"><i class="fas fa-arrow-up"></i></button></div></div></div><ul><li><p>编写模型类User</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="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> 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="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 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> 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="built_in">this</span>.age = age;</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;name=&#x27;&quot;</span> + name + <span class="string">&#x27;\&#x27;&#x27;</span> +</span><br><span class="line">                <span class="string">&quot;, age=&quot;</span> + age +</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></li><li><p>编写<code>UserController</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></pre></td><td class="code"><pre><span class="line"><span class="meta">@Controller</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 class="meta">@RequestMapping(&quot;/save&quot;)</span></span><br><span class="line">    <span class="meta">@ResponseBody</span></span><br><span class="line">    <span class="keyword">public</span> String <span class="title function_">save</span><span class="params">(<span class="meta">@RequestBody</span> User user)</span>&#123;</span><br><span class="line">        System.out.println(<span class="string">&quot;user save ...&quot;</span> + user);</span><br><span class="line">        <span class="keyword">return</span> <span class="string">&quot;&#123;&#x27;module&#x27;:&#x27;user save&#x27;&#125;&quot;</span>;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="meta">@RequestMapping(&quot;/delete&quot;)</span></span><br><span class="line">    <span class="meta">@ResponseBody</span></span><br><span class="line">    <span class="keyword">public</span> String <span class="title function_">delete</span><span class="params">(Integer id)</span>&#123;</span><br><span class="line">        System.out.println(<span class="string">&quot;user delete ...&quot;</span> + id);</span><br><span class="line">        <span class="keyword">return</span> <span class="string">&quot;&#123;&#x27;module&#x27;:&#x27;user delete&#x27;&#125;&quot;</span>;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="meta">@RequestMapping(&quot;/update&quot;)</span></span><br><span class="line">    <span class="meta">@ResponseBody</span></span><br><span class="line">    <span class="keyword">public</span> String <span class="title function_">update</span><span class="params">(<span class="meta">@RequestBody</span> User user)</span>&#123;</span><br><span class="line">        System.out.println(<span class="string">&quot;user update ...&quot;</span> + user);</span><br><span class="line">        <span class="keyword">return</span> <span class="string">&quot;&#123;&#x27;module&#x27;:&#x27;user update&#x27;&#125;&quot;</span>;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="meta">@RequestMapping(&quot;/getById&quot;)</span></span><br><span class="line">    <span class="meta">@ResponseBody</span></span><br><span class="line">    <span class="keyword">public</span> String <span class="title function_">getById</span><span class="params">(Integer id)</span>&#123;</span><br><span class="line">        System.out.println(<span class="string">&quot;user getById ...&quot;</span> + id);</span><br><span class="line">        <span class="keyword">return</span> <span class="string">&quot;&#123;&#x27;module&#x27;:&#x27;user getById&#x27;&#125;&quot;</span>;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="meta">@RequestMapping(&quot;/getAll&quot;)</span></span><br><span class="line">    <span class="meta">@ResponseBody</span></span><br><span class="line">    <span class="keyword">public</span> String <span class="title function_">getAll</span><span class="params">()</span>&#123;</span><br><span class="line">        System.out.println(<span class="string">&quot;user getAll ...&quot;</span>);</span><br><span class="line">        <span class="keyword">return</span> <span class="string">&quot;&#123;&#x27;module&#x27;:&#x27;user getAll&#x27;&#125;&quot;</span>;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></li></ul><h3 id="思路分析-1"><a href="#思路分析-1" class="headerlink" title="思路分析"></a>思路分析</h3><p>需求:将之前的增删改查替换成<code>RESTful</code>的开发方式。</p><ol><li>之前不同的请求有不同的路径,现在要将其修改为统一的请求路径<ul><li>修改前: 新增：<code>/save</code>，修改: <code>/update</code>，删除 <code>/delete</code>..</li><li>修改后: 增删改查：<code>/users</code></li></ul></li><li>根据<code>GET</code>查询、<code>POST</code>新增、<code>PUT</code>修改、<code>DELETE</code>删除对方法的请求方式进行限定</li><li>发送请求的过程中如何设置请求参数?</li></ol><h3 id="修改RESTfuil风格"><a href="#修改RESTfuil风格" class="headerlink" title="修改RESTfuil风格"></a>修改RESTfuil风格</h3><ul><li><p><code>新增</code><br>将请求路径更改为<code>/users</code>，并设置当前请求方法为<code>POST</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></pre></td><td class="code"><pre><span class="line"><span class="meta">@RequestMapping(value = &quot;/users&quot;, method = RequestMethod.POST)</span></span><br><span class="line"><span class="meta">@ResponseBody</span></span><br><span class="line"><span class="keyword">public</span> String <span class="title function_">save</span><span class="params">(<span class="meta">@RequestBody</span> User user)</span> &#123;</span><br><span class="line">    System.out.println(<span class="string">&quot;user save ...&quot;</span> + user);</span><br><span class="line">    <span class="keyword">return</span> <span class="string">&quot;&#123;&#x27;module&#x27;:&#x27;user save&#x27;&#125;&quot;</span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>使用method属性限定该方法的访问方式为<code>POST</code>，如果使用<code>GET</code>请求将报错<br>发送POST请求与参数：</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></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;菲茨罗伊&quot;</span><span class="punctuation">,</span></span><br><span class="line">    <span class="attr">&quot;age&quot;</span><span class="punctuation">:</span><span class="string">&quot;27&quot;</span></span><br><span class="line"><span class="punctuation">&#125;</span></span><br></pre></td></tr></table></figure><p>控制台输出如下</p><blockquote><p>user save …User{name=’菲茨罗伊’, age=27}</p></blockquote></li><li><p><code>删除</code><br>将请求路径更改为<code>/users</code>，并设置当前请求方法为<code>DELETE</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></pre></td><td class="code"><pre><span class="line"><span class="meta">@RequestMapping(value = &quot;/delete&quot;,method = RequestMethod.DELETE)</span></span><br><span class="line"><span class="meta">@ResponseBody</span></span><br><span class="line"><span class="keyword">public</span> String <span class="title function_">delete</span><span class="params">(Integer id)</span>&#123;</span><br><span class="line">    System.out.println(<span class="string">&quot;user delete ...&quot;</span> + id);</span><br><span class="line">    <span class="keyword">return</span> <span class="string">&quot;&#123;&#x27;module&#x27;:&#x27;user delete&#x27;&#125;&quot;</span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>但是现在的删除方法没有携带所要删除数据的id，所以针对RESTful的开发，如何携带数据参数?</p></li><li><p>修改@RequestMapping的value属性，将其中修改为<code>/users/&#123;id&#125;</code>，目的是和路径匹配</p></li><li><p>在方法的形参前添加<code>@PathVariable</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></pre></td><td class="code"><pre><span class="line"><span class="meta">@RequestMapping(value = &quot;/users/&#123;id&#125;&quot;,method = RequestMethod.DELETE)</span></span><br><span class="line"><span class="meta">@ResponseBody</span></span><br><span class="line"><span class="keyword">public</span> String <span class="title function_">delete</span><span class="params">(<span class="meta">@PathVariable</span> Integer id)</span>&#123;</span><br><span class="line">    System.out.println(<span class="string">&quot;user delete ...&quot;</span> + id);</span><br><span class="line">    <span class="keyword">return</span> <span class="string">&quot;&#123;&#x27;module&#x27;:&#x27;user delete&#x27;&#125;&quot;</span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>发送<code>DELETE</code>请求访问<code>localhost:8080/users/9421</code></p><p>控制台输出如下</p><blockquote><p>user delete …9421</p></blockquote></li></ul><p>疑问：如果方法形参的名称和路径<code>&#123;&#125;</code>中的值不一致，该怎么办?<br>例如<code>&quot;/users/&#123;id&#125;&quot;</code>和<code>delete(@PathVariable Integer userId)</code></p><ul><li><p>解答：如果这两个值不一致，就无法获取参数，此时我们可以在注解后面加上属性，让注解的属性值与<code>&#123;&#125;</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></pre></td><td class="code"><pre><span class="line"><span class="meta">@RequestMapping(value = &quot;/users/&#123;id&#125;&quot;,method = RequestMethod.DELETE)</span></span><br><span class="line"><span class="meta">@ResponseBody</span></span><br><span class="line"><span class="keyword">public</span> String <span class="title function_">delete</span><span class="params">(<span class="meta">@PathVariable(&quot;id&quot;)</span> Integer userId)</span>&#123;</span><br><span class="line">    System.out.println(<span class="string">&quot;user delete ...&quot;</span> + userId);</span><br><span class="line">    <span class="keyword">return</span> <span class="string">&quot;&#123;&#x27;module&#x27;:&#x27;user delete&#x27;&#125;&quot;</span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></li></ul><p>疑问：如果有多个参数需要传递该如何编写?<br>前端发送请求时使用<code>localhost:8080/users/9421/Tom</code>，路径中的<code>9421</code>和<code>Tom</code>就是我们想传递的两个参数</p><ul><li><p>解答：我们在路径后面再加一个<code>/&#123;name&#125;</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></pre></td><td class="code"><pre><span class="line"><span class="meta">@RequestMapping(value = &quot;/users/&#123;id&#125;/&#123;name&#125;&quot;,method = RequestMethod.DELETE)</span></span><br><span class="line"><span class="meta">@ResponseBody</span></span><br><span class="line"><span class="keyword">public</span> String <span class="title function_">delete</span><span class="params">(<span class="meta">@PathVariable(&quot;id&quot;)</span> Integer userId,<span class="meta">@PathVariable</span> String name)</span>&#123;</span><br><span class="line">    System.out.println(<span class="string">&quot;user delete ...&quot;</span> + userId + <span class="string">&quot;:&quot;</span> + name);</span><br><span class="line">    <span class="keyword">return</span> <span class="string">&quot;&#123;&#x27;module&#x27;:&#x27;user delete&#x27;&#125;&quot;</span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>发送<code>DELETE</code>请求访问<code>localhost:8080/users/9421/Tom</code><br>控制台输出如下</p><blockquote><p>user delete …9421:Tom</p></blockquote></li><li><p><code>修改</code><br>将请求路径更改为<code>/users</code>，并设置当前请求方法为<code>PUT</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></pre></td><td class="code"><pre><span class="line"><span class="meta">@RequestMapping(value = &quot;/users&quot;,method = RequestMethod.PUT)</span></span><br><span class="line"><span class="meta">@ResponseBody</span></span><br><span class="line"><span class="keyword">public</span> String <span class="title function_">update</span><span class="params">(<span class="meta">@RequestBody</span> User user)</span>&#123;</span><br><span class="line">    System.out.println(<span class="string">&quot;user update ...&quot;</span> + user);</span><br><span class="line">    <span class="keyword">return</span> <span class="string">&quot;&#123;&#x27;module&#x27;:&#x27;user update&#x27;&#125;&quot;</span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>发送<code>PUT</code>请求<code>localhost:8080/users</code>，访问并携带参数</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></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;菲茨罗伊&quot;</span><span class="punctuation">,</span></span><br><span class="line">    <span class="attr">&quot;age&quot;</span><span class="punctuation">:</span><span class="string">&quot;27&quot;</span></span><br><span class="line"><span class="punctuation">&#125;</span></span><br></pre></td></tr></table></figure><p>控制台输出如下</p><blockquote><p>user update …User{name=’菲茨罗伊’, age=27}</p></blockquote></li><li><p><code>根据ID查询</code><br>将请求路径更改为<code>/users/&#123;id&#125;</code>，并设置当前请求方法为<code>GET</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></pre></td><td class="code"><pre><span class="line"><span class="meta">@RequestMapping(value = &quot;/users/&#123;id&#125;&quot;,method = RequestMethod.GET)</span></span><br><span class="line"><span class="meta">@ResponseBody</span></span><br><span class="line"><span class="keyword">public</span> String <span class="title function_">getById</span><span class="params">(<span class="meta">@PathVariable</span> Integer id)</span>&#123;</span><br><span class="line">    System.out.println(<span class="string">&quot;user getById ...&quot;</span> + id);</span><br><span class="line">    <span class="keyword">return</span> <span class="string">&quot;&#123;&#x27;module&#x27;:&#x27;user getById&#x27;&#125;&quot;</span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>发送<code>GET</code>请求访问<code>localhost:8080/users/2077</code><br>控制台输出如下</p><blockquote><p>user getById …2077</p></blockquote></li><li><p><code>查询所有</code><br>将请求路径更改为<code>/users</code>，并设置当前请求方法为<code>GET</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></pre></td><td class="code"><pre><span class="line"><span class="meta">@RequestMapping(value = &quot;/users&quot;,method = RequestMethod.GET)</span></span><br><span class="line"><span class="meta">@ResponseBody</span></span><br><span class="line"><span class="keyword">public</span> String <span class="title function_">getAll</span><span class="params">()</span>&#123;</span><br><span class="line">    System.out.println(<span class="string">&quot;user getAll ...&quot;</span>);</span><br><span class="line">    <span class="keyword">return</span> <span class="string">&quot;&#123;&#x27;module&#x27;:&#x27;user getAll&#x27;&#125;&quot;</span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>发送<code>GET</code>请求访问<code>localhost:8080/users</code><br>控制台输出如下</p><blockquote><p>user getAll …</p></blockquote></li><li><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></pre></td><td class="code"><pre><span class="line"><span class="meta">@Controller</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 class="meta">@RequestMapping(value = &quot;/users&quot;, method = RequestMethod.POST)</span></span><br><span class="line">    <span class="meta">@ResponseBody</span></span><br><span class="line">    <span class="keyword">public</span> String <span class="title function_">save</span><span class="params">(<span class="meta">@RequestBody</span> User user)</span> &#123;</span><br><span class="line">        System.out.println(<span class="string">&quot;user save ...&quot;</span> + user);</span><br><span class="line">        <span class="keyword">return</span> <span class="string">&quot;&#123;&#x27;module&#x27;:&#x27;user save&#x27;&#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="meta">@RequestMapping(value = &quot;/users/&#123;id&#125;/&#123;name&#125;&quot;,method = RequestMethod.DELETE)</span></span><br><span class="line">    <span class="meta">@ResponseBody</span></span><br><span class="line">    <span class="keyword">public</span> String <span class="title function_">delete</span><span class="params">(<span class="meta">@PathVariable(&quot;id&quot;)</span> Integer userId,<span class="meta">@PathVariable</span> String name)</span>&#123;</span><br><span class="line">        System.out.println(<span class="string">&quot;user delete ...&quot;</span> + userId + <span class="string">&quot;:&quot;</span> + name);</span><br><span class="line">        <span class="keyword">return</span> <span class="string">&quot;&#123;&#x27;module&#x27;:&#x27;user delete&#x27;&#125;&quot;</span>;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@RequestMapping(value = &quot;/users&quot;,method = RequestMethod.PUT)</span></span><br><span class="line">    <span class="meta">@ResponseBody</span></span><br><span class="line">    <span class="keyword">public</span> String <span class="title function_">update</span><span class="params">(<span class="meta">@RequestBody</span> User user)</span>&#123;</span><br><span class="line">        System.out.println(<span class="string">&quot;user update ...&quot;</span> + user);</span><br><span class="line">        <span class="keyword">return</span> <span class="string">&quot;&#123;&#x27;module&#x27;:&#x27;user update&#x27;&#125;&quot;</span>;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@RequestMapping(value = &quot;/users/&#123;id&#125;&quot;,method = RequestMethod.GET)</span></span><br><span class="line">    <span class="meta">@ResponseBody</span></span><br><span class="line">    <span class="keyword">public</span> String <span class="title function_">getById</span><span class="params">(<span class="meta">@PathVariable</span> Integer id)</span>&#123;</span><br><span class="line">        System.out.println(<span class="string">&quot;user getById ...&quot;</span> + id);</span><br><span class="line">        <span class="keyword">return</span> <span class="string">&quot;&#123;&#x27;module&#x27;:&#x27;user getById&#x27;&#125;&quot;</span>;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@RequestMapping(value = &quot;/users&quot;,method = RequestMethod.GET)</span></span><br><span class="line">    <span class="meta">@ResponseBody</span></span><br><span class="line">    <span class="keyword">public</span> String <span class="title function_">getAll</span><span class="params">()</span>&#123;</span><br><span class="line">        System.out.println(<span class="string">&quot;user getAll ...&quot;</span>);</span><br><span class="line">        <span class="keyword">return</span> <span class="string">&quot;&#123;&#x27;module&#x27;:&#x27;user getAll&#x27;&#125;&quot;</span>;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>从整体代码来看，有些臃肿，好多代码都是重复的，下一小节我们就会来解决这个问题</p></li></ul><h3 id="小结-1"><a href="#小结-1" class="headerlink" title="小结"></a>小结</h3><p>RESTful入门案例，我们需要记住的内容如下:</p><ol><li><p>设定Http请求动作(动词)</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">@RequestMapping(value=&quot;&quot;,method = RequestMethod.POST|GET|PUT|DELETE)</span></span><br></pre></td></tr></table></figure></li><li><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></pre></td><td class="code"><pre><span class="line"><span class="meta">@RequestMapping(value=&quot;/users/&#123;id&#125;&quot;,method = RequestMethod.DELETE)</span></span><br><span class="line"><span class="meta">@ReponseBody</span></span><br><span class="line"><span class="keyword">public</span> String <span class="title function_">delete</span><span class="params">(<span class="meta">@PathVariable</span> Integer id)</span>&#123;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></li></ol><p>知识点：<code>@PathVariable</code></p><div class="table-container"><table><thead><tr><th style="text-align:center">名称</th><th style="text-align:center">@PathVariable</th></tr></thead><tbody><tr><td style="text-align:center">类型</td><td style="text-align:center">形参注解</td></tr><tr><td style="text-align:center">位置</td><td style="text-align:center">SpringMVC控制器方法形参定义前面</td></tr><tr><td style="text-align:center">作用</td><td style="text-align:center">绑定路径参数与处理器方法形参间的关系，要求路径参数名与形参名一一对应</td></tr></tbody></table></div><p>关于接收参数，我们学过三个注解<code>@RequestBody</code>、<code>@RequestParam</code>、<code>@PathVariable</code>，这三个注解之间的区别和应用分别是什么?</p><ul><li>区别<ul><li><code>@RequestParam</code>用于接收url地址传参或表单传参</li><li><code>@RequestBody</code>用于接收JSON数据</li><li><code>@PathVariable</code>用于接收路径参数，使用{参数名称}描述路径参数</li></ul></li><li>应用<ul><li>后期开发中，发送请求参数超过1个时，以JSON格式为主，<code>@RequestBody</code>应用较广</li><li>如果发送非JSON格式数据，选用<code>@RequestParam</code>接收请求参数</li><li>采用<code>RESTful</code>进行开发，当参数数量较少时，例如1个，可以采用<code>@PathVariable</code>接收请求路径变量，通常用于传递id值</li></ul></li></ul><h3 id="RESTful快速开发"><a href="#RESTful快速开发" class="headerlink" title="RESTful快速开发"></a>RESTful快速开发</h3><p>做完了上面的<code>RESTful</code>的开发，就感觉好麻烦，主要体现在以下三部分</p><ul><li><p>每个方法的<code>@RequestMapping</code>注解中都定义了访问路径<code>/users</code>，重复性太高。</p><ul><li>解决方案：将<code>@RequestMapping</code>提到类上面，用来定义所有方法共同的访问路径。</li></ul></li><li><p>每个方法的<code>@RequestMapping</code>注解中都要使用method属性定义请求方式，重复性太高。</p><ul><li>解决方案：使用<code>@GetMapping</code>、<code>@PostMapping</code>、<code>@PutMapping</code>、<code>@DeleteMapping</code>代替</li></ul></li><li><p>每个方法响应json都需要加上<code>@ResponseBody</code>注解，重复性太高。</p><ul><li>解决方案：<ul><li>将<code>@ResponseBody</code>提到类上面，让所有的方法都有<code>@ResponseBody</code>的功能</li><li>使用<code>@RestController</code>注解替换<code>@Controller</code>与<code>@ResponseBody</code>注解，简化书写</li></ul></li></ul></li><li><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="meta">@RestController</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 class="meta">@PostMapping</span></span><br><span class="line">    <span class="keyword">public</span> String <span class="title function_">save</span><span class="params">(<span class="meta">@RequestBody</span> User user)</span> &#123;</span><br><span class="line">        System.out.println(<span class="string">&quot;user save ...&quot;</span> + user);</span><br><span class="line">        <span class="keyword">return</span> <span class="string">&quot;&#123;&#x27;module&#x27;:&#x27;user save&#x27;&#125;&quot;</span>;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@DeleteMapping(&quot;/&#123;id&#125;/&#123;name&#125;&quot;)</span></span><br><span class="line">    <span class="keyword">public</span> String <span class="title function_">delete</span><span class="params">(<span class="meta">@PathVariable(&quot;id&quot;)</span> Integer userId, <span class="meta">@PathVariable</span> String name)</span> &#123;</span><br><span class="line">        System.out.println(<span class="string">&quot;user delete ...&quot;</span> + userId + <span class="string">&quot;:&quot;</span> + name);</span><br><span class="line">        <span class="keyword">return</span> <span class="string">&quot;&#123;&#x27;module&#x27;:&#x27;user delete&#x27;&#125;&quot;</span>;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@PutMapping()</span></span><br><span class="line">    <span class="keyword">public</span> String <span class="title function_">update</span><span class="params">(<span class="meta">@RequestBody</span> User user)</span> &#123;</span><br><span class="line">        System.out.println(<span class="string">&quot;user update ...&quot;</span> + user);</span><br><span class="line">        <span class="keyword">return</span> <span class="string">&quot;&#123;&#x27;module&#x27;:&#x27;user update&#x27;&#125;&quot;</span>;</span><br><span class="line">    &#125;</span><br><span class="line"></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> String <span class="title function_">getById</span><span class="params">(<span class="meta">@PathVariable</span> Integer id)</span> &#123;</span><br><span class="line">        System.out.println(<span class="string">&quot;user getById ...&quot;</span> + id);</span><br><span class="line">        <span class="keyword">return</span> <span class="string">&quot;&#123;&#x27;module&#x27;:&#x27;user getById&#x27;&#125;&quot;</span>;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@GetMapping</span></span><br><span class="line">    <span class="keyword">public</span> String <span class="title function_">getAll</span><span class="params">()</span> &#123;</span><br><span class="line">        System.out.println(<span class="string">&quot;user getAll ...&quot;</span>);</span><br><span class="line">        <span class="keyword">return</span> <span class="string">&quot;&#123;&#x27;module&#x27;:&#x27;user getAll&#x27;&#125;&quot;</span>;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></li></ul><h2 id="RESTful案例"><a href="#RESTful案例" class="headerlink" title="RESTful案例"></a>RESTful案例</h2><h3 id="需求分析"><a href="#需求分析" class="headerlink" title="需求分析"></a>需求分析</h3><ul><li><p>需求一：图片列表查询，从后台返回数据，将数据展示在页面上<br><a href="https://pic.imgdb.cn/item/631bdd1f16f2c2beb17823f9.jpg"><img src="Untitled/631bdd1f16f2c2beb17823f9.jpg" alt="img"></a></p></li><li><p>需求二：新增图片，将新增图书的数据传递到后台，并在控制台打印</p><p><img src="https://pic.imgdb.cn/item/631bdd2a16f2c2beb1782c1c.jpg" alt="img"></p><p>说明：此次案例的重点是在SpringMVC中如何使用RESTful实现前后台交互，所以本案例并没有和数据库进行交互，所有数据使用<code>假数据</code>来完成开发。</p></li></ul><p>步骤分析:</p><ol><li>搭建项目导入jar包</li><li>编写Controller类，提供两个方法，一个用来做列表查询，一个用来做新增</li><li>在方法上使用RESTful进行路径设置</li><li>完成请求、参数的接收和结果的响应</li><li>使用PostMan进行测试</li><li>将前端页面拷贝到项目中</li><li>页面发送ajax请求</li><li>完成页面数据的展示</li></ol><h4 id="环境准备-5"><a href="#环境准备-5" class="headerlink" title="环境准备"></a>环境准备</h4><ul><li><p>创建一个web的maven项目</p></li><li><p>导入坐标</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></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>javax.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>javax.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>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 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<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-webmvc<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.2.10.RELEASE<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>com.fasterxml.jackson.core<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>jackson-databind<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.9.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></li><li><p>创建对应的配置类</p><ul><li>ServletContainersInitConfig</li><li>SpringMvcConfig</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></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">ServletContainersInitConfig</span> <span class="keyword">extends</span> <span class="title class_">AbstractAnnotationConfigDispatcherServletInitializer</span> &#123;</span><br><span class="line">    <span class="keyword">protected</span> Class&lt;?&gt;[] getRootConfigClasses() &#123;</span><br><span class="line">        <span class="keyword">return</span> <span class="keyword">new</span> <span class="title class_">Class</span>[<span class="number">0</span>];</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">protected</span> Class&lt;?&gt;[] getServletConfigClasses() &#123;</span><br><span class="line">        <span class="keyword">return</span> <span class="keyword">new</span> <span class="title class_">Class</span>[]&#123;SpringMvcConfig.class&#125;;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">protected</span> String[] getServletMappings() &#123;</span><br><span class="line">        <span class="keyword">return</span> <span class="keyword">new</span> <span class="title class_">String</span>[]&#123;<span class="string">&quot;/&quot;</span>&#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> Filter[] getServletFilters() &#123;</span><br><span class="line">        <span class="type">CharacterEncodingFilter</span> <span class="variable">filter</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">CharacterEncodingFilter</span>();</span><br><span class="line">        filter.setEncoding(<span class="string">&quot;utf-8&quot;</span>);</span><br><span class="line">        <span class="keyword">return</span> <span class="keyword">new</span> <span class="title class_">Filter</span>[]&#123;filter&#125;;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></li></ul><ul><li><p>编写模型类Book</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></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">Book</span> &#123;</span><br><span class="line">   <span class="keyword">private</span> Integer id;</span><br><span class="line">   <span class="keyword">private</span> String type;</span><br><span class="line">   <span class="keyword">private</span> String name;</span><br><span class="line">   <span class="keyword">private</span> String description;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">public</span> Integer <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">(Integer 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_">getType</span><span class="params">()</span> &#123;</span><br><span class="line">        <span class="keyword">return</span> type;</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_">setType</span><span class="params">(String type)</span> &#123;</span><br><span class="line">        <span class="built_in">this</span>.type = type;</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_">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> String <span class="title function_">getDescription</span><span class="params">()</span> &#123;</span><br><span class="line">        <span class="keyword">return</span> description;</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_">setDescription</span><span class="params">(String description)</span> &#123;</span><br><span class="line">        <span class="built_in">this</span>.description = description;</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;Book&#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;, type=&#x27;&quot;</span> + type + <span class="string">&#x27;\&#x27;&#x27;</span> +</span><br><span class="line">                <span class="string">&quot;, name=&#x27;&quot;</span> + name + <span class="string">&#x27;\&#x27;&#x27;</span> +</span><br><span class="line">                <span class="string">&quot;, description=&#x27;&quot;</span> + description + <span class="string">&#x27;\&#x27;&#x27;</span> +</span><br><span class="line">                <span class="string">&#x27;&#125;&#x27;</span>;</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_">Book</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_">Book</span><span class="params">(Integer id, String type, String name, String description)</span> &#123;</span><br><span class="line">        <span class="built_in">this</span>.id = id;</span><br><span class="line">        <span class="built_in">this</span>.type = type;</span><br><span class="line">        <span class="built_in">this</span>.name = name;</span><br><span class="line">        <span class="built_in">this</span>.description = description;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></li><li><p>编写BookController</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></pre></td><td class="code"><pre><span class="line"><span class="meta">@RestController</span></span><br><span class="line"><span class="meta">@RequestMapping(&quot;/books&quot;)</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">BookController</span> &#123;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></li></ul><h4 id="后台接口开发"><a href="#后台接口开发" class="headerlink" title="后台接口开发"></a>后台接口开发</h4><ul><li><p>编写Controller类，并使用RESTful配置</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">@RestController</span></span><br><span class="line"><span class="meta">@RequestMapping(&quot;/books&quot;)</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">BookController</span> &#123;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@PostMapping</span></span><br><span class="line">    <span class="keyword">public</span> String <span class="title function_">save</span><span class="params">(<span class="meta">@RequestBody</span> Book book)</span>&#123;</span><br><span class="line">        System.out.println(<span class="string">&quot;book save --&gt; &quot;</span> + book);</span><br><span class="line">        <span class="keyword">return</span> <span class="string">&quot;&#123;&#x27;module&#x27;:&#x27;book save success&#x27;&#125;&quot;</span>;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@GetMapping</span></span><br><span class="line">    <span class="keyword">public</span> List&lt;Book&gt; <span class="title function_">getAll</span><span class="params">()</span>&#123;</span><br><span class="line">        System.out.println(<span class="string">&quot;book getAll running ..&quot;</span>);</span><br><span class="line">        ArrayList&lt;Book&gt; bookList = <span class="keyword">new</span> <span class="title class_">ArrayList</span>&lt;Book&gt;();</span><br><span class="line"></span><br><span class="line">        <span class="type">Book</span> <span class="variable">book1</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">Book</span>();</span><br><span class="line">        book1.setType(<span class="string">&quot;计算机&quot;</span>);</span><br><span class="line">        book1.setName(<span class="string">&quot;SpringMVC入门教程&quot;</span>);</span><br><span class="line">        book1.setDescription(<span class="string">&quot;小试牛刀&quot;</span>);</span><br><span class="line">        bookList.add(book1);</span><br><span class="line"></span><br><span class="line">        <span class="type">Book</span> <span class="variable">book2</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">Book</span>();</span><br><span class="line">        book2.setType(<span class="string">&quot;计算机&quot;</span>);</span><br><span class="line">        book2.setName(<span class="string">&quot;SpringMVC实战教程&quot;</span>);</span><br><span class="line">        book2.setDescription(<span class="string">&quot;一代宗师&quot;</span>);</span><br><span class="line">        bookList.add(book2);</span><br><span class="line"></span><br><span class="line">        <span class="type">Book</span> <span class="variable">book3</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">Book</span>();</span><br><span class="line">        book3.setType(<span class="string">&quot;计算机丛书&quot;</span>);</span><br><span class="line">        book3.setName(<span class="string">&quot;SpringMVC实战教程进阶&quot;</span>);</span><br><span class="line">        book3.setDescription(<span class="string">&quot;一代宗师呕心创作&quot;</span>);</span><br><span class="line">        bookList.add(book3);</span><br><span class="line">        </span><br><span class="line">        <span class="keyword">return</span> bookList;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></li><li><p>使用PostMan进行测试</p><ul><li><p>测试新增</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></pre></td><td class="code"><pre><span class="line"><span class="punctuation">&#123;</span></span><br><span class="line">    <span class="attr">&quot;type&quot;</span><span class="punctuation">:</span><span class="string">&quot;计算机&quot;</span><span class="punctuation">,</span></span><br><span class="line">    <span class="attr">&quot;name&quot;</span><span class="punctuation">:</span><span class="string">&quot;SpringMVC入门教程&quot;</span><span class="punctuation">,</span></span><br><span class="line">    <span class="attr">&quot;description&quot;</span><span class="punctuation">:</span><span class="string">&quot;小试牛刀&quot;</span></span><br><span class="line"><span class="punctuation">&#125;</span></span><br></pre></td></tr></table></figure><p>访问<code>localhost:8080/books</code>，发送POST请求与数据，控制台输出如下</p><blockquote><p>book save —&gt; Book{id=null, type=’计算机’, name=’SpringMVC入门教程’, description=’小试牛刀’}</p></blockquote></li><li><p>测试查询<br>访问<code>localhost:8080/books</code>，发送GET请求，查询到如下内容</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><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><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="punctuation">[</span></span><br><span class="line">    <span class="punctuation">&#123;</span></span><br><span class="line">        <span class="attr">&quot;id&quot;</span><span class="punctuation">:</span> <span class="literal"><span class="keyword">null</span></span><span class="punctuation">,</span></span><br><span class="line">        <span class="attr">&quot;type&quot;</span><span class="punctuation">:</span> <span class="string">&quot;计算机&quot;</span><span class="punctuation">,</span></span><br><span class="line">        <span class="attr">&quot;name&quot;</span><span class="punctuation">:</span> <span class="string">&quot;SpringMVC入门教程&quot;</span><span class="punctuation">,</span></span><br><span class="line">        <span class="attr">&quot;description&quot;</span><span class="punctuation">:</span> <span class="string">&quot;小试牛刀&quot;</span></span><br><span class="line">    <span class="punctuation">&#125;</span><span class="punctuation">,</span></span><br><span class="line">    <span class="punctuation">&#123;</span></span><br><span class="line">        <span class="attr">&quot;id&quot;</span><span class="punctuation">:</span> <span class="literal"><span class="keyword">null</span></span><span class="punctuation">,</span></span><br><span class="line">        <span class="attr">&quot;type&quot;</span><span class="punctuation">:</span> <span class="string">&quot;计算机&quot;</span><span class="punctuation">,</span></span><br><span class="line">        <span class="attr">&quot;name&quot;</span><span class="punctuation">:</span> <span class="string">&quot;SpringMVC实战教程&quot;</span><span class="punctuation">,</span></span><br><span class="line">        <span class="attr">&quot;description&quot;</span><span class="punctuation">:</span> <span class="string">&quot;一代宗师&quot;</span></span><br><span class="line">    <span class="punctuation">&#125;</span><span class="punctuation">,</span></span><br><span class="line">    <span class="punctuation">&#123;</span></span><br><span class="line">        <span class="attr">&quot;id&quot;</span><span class="punctuation">:</span> <span class="literal"><span class="keyword">null</span></span><span class="punctuation">,</span></span><br><span class="line">        <span class="attr">&quot;type&quot;</span><span class="punctuation">:</span> <span class="string">&quot;计算机丛书&quot;</span><span class="punctuation">,</span></span><br><span class="line">        <span class="attr">&quot;name&quot;</span><span class="punctuation">:</span> <span class="string">&quot;SpringMVC实战教程进阶&quot;</span><span class="punctuation">,</span></span><br><span class="line">        <span class="attr">&quot;description&quot;</span><span class="punctuation">:</span> <span class="string">&quot;一代宗师呕心创作&quot;</span></span><br><span class="line">    <span class="punctuation">&#125;</span></span><br><span class="line"><span class="punctuation">]</span></span><br></pre></td></tr></table></figure><p>控制台输出如下</p><blockquote><p>book getAll running ..</p></blockquote></li></ul></li></ul><h4 id="页面访问处理"><a href="#页面访问处理" class="headerlink" title="页面访问处理"></a>页面访问处理</h4><ul><li><p><code>步骤一：</code>导入提供好的静态页面</p></li><li><p><code>步骤二：</code>访问pages目录下的books.html</p><ul><li><p>打开浏览器访问<code>http://localhost:8080/pages/book.html</code>，报404，为什么呢？</p></li><li><p>SpringMvcConfig拦截了所有资源路径</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></pre></td><td class="code"><pre><span class="line"><span class="keyword">protected</span> String[] getServletMappings() &#123;</span><br><span class="line">    <span class="keyword">return</span> <span class="keyword">new</span> <span class="title class_">String</span>[]&#123;<span class="string">&quot;/&quot;</span>&#125;;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></li><li><p>解决方案：SpringMVC需要将静态资源进行放行</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">@Configuration</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">SpringMvcSupport</span> <span class="keyword">extends</span> <span class="title class_">WebMvcConfigurationSupport</span> &#123;</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> <span class="keyword">void</span> <span class="title function_">addResourceHandlers</span><span class="params">(ResourceHandlerRegistry registry)</span> &#123;</span><br><span class="line">        <span class="comment">//当访问/pages/xxx时候，从/pages目录下查找内容</span></span><br><span class="line">        registry.addResourceHandler(<span class="string">&quot;/pages/**&quot;</span>).addResourceLocations(<span class="string">&quot;/pages/&quot;</span>);</span><br><span class="line">        registry.addResourceHandler(<span class="string">&quot;/js/**&quot;</span>).addResourceLocations(<span class="string">&quot;/js/&quot;</span>);</span><br><span class="line">        registry.addResourceHandler(<span class="string">&quot;/css/**&quot;</span>).addResourceLocations(<span class="string">&quot;/css/&quot;</span>);</span><br><span class="line">        registry.addResourceHandler(<span class="string">&quot;/plugins/**&quot;</span>).addResourceLocations(<span class="string">&quot;/plugins/&quot;</span>);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></li><li><p>该配置类是在config目录下，SpringMVC扫描的是controller包，所以该配置类还未生效，要想生效需要将SpringMvcConfig配置类进行修改</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></pre></td><td class="code"><pre><span class="line"><span class="meta">@Configuration</span></span><br><span class="line"><span class="comment">//将我们刚刚写的配置类也扫描进去</span></span><br><span class="line"><span class="meta">@ComponentScan(&#123;&quot;com.blog.controller&quot;,&quot;com.blog.config&quot;&#125;)</span></span><br><span class="line"><span class="meta">@EnableWebMvc</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">SpringMvcConfig</span> &#123;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></li></ul></li><li><p>步骤三：修改books.html页面</p><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><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></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><br><span class="line"><span class="tag">&lt;<span class="name">html</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="comment">&lt;!-- 页面meta --&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>SpringMVC案例<span class="tag">&lt;/<span class="name">title</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">link</span> <span class="attr">rel</span>=<span class="string">&quot;stylesheet&quot;</span> <span class="attr">href</span>=<span class="string">&quot;../plugins/elementui/index.css&quot;</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">link</span> <span class="attr">rel</span>=<span class="string">&quot;stylesheet&quot;</span> <span class="attr">href</span>=<span class="string">&quot;../plugins/font-awesome/css/font-awesome.min.css&quot;</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">link</span> <span class="attr">rel</span>=<span class="string">&quot;stylesheet&quot;</span> <span class="attr">href</span>=<span class="string">&quot;../css/style.css&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><br><span class="line"><span class="tag">&lt;<span class="name">body</span> <span class="attr">class</span>=<span class="string">&quot;hold-transition&quot;</span>&gt;</span></span><br><span class="line"></span><br><span class="line"><span class="tag">&lt;<span class="name">div</span> <span class="attr">id</span>=<span class="string">&quot;app&quot;</span>&gt;</span></span><br><span class="line"></span><br><span class="line">    <span class="tag">&lt;<span class="name">div</span> <span class="attr">class</span>=<span class="string">&quot;content-header&quot;</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">div</span>&gt;</span></span><br><span class="line"></span><br><span class="line">    <span class="tag">&lt;<span class="name">div</span> <span class="attr">class</span>=<span class="string">&quot;app-container&quot;</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;box&quot;</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;filter-container&quot;</span>&gt;</span></span><br><span class="line">                <span class="tag">&lt;<span class="name">el-input</span> <span class="attr">placeholder</span>=<span class="string">&quot;图书名称&quot;</span> <span class="attr">style</span>=<span class="string">&quot;width: 200px;&quot;</span> <span class="attr">class</span>=<span class="string">&quot;filter-item&quot;</span>&gt;</span><span class="tag">&lt;/<span class="name">el-input</span>&gt;</span></span><br><span class="line">                <span class="tag">&lt;<span class="name">el-button</span> <span class="attr">class</span>=<span class="string">&quot;dalfBut&quot;</span>&gt;</span>查询<span class="tag">&lt;/<span class="name">el-button</span>&gt;</span></span><br><span class="line">                <span class="tag">&lt;<span class="name">el-button</span> <span class="attr">type</span>=<span class="string">&quot;primary&quot;</span> <span class="attr">class</span>=<span class="string">&quot;butT&quot;</span> @<span class="attr">click</span>=<span class="string">&quot;openSave()&quot;</span>&gt;</span>新建<span class="tag">&lt;/<span class="name">el-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">el-table</span> <span class="attr">size</span>=<span class="string">&quot;small&quot;</span> <span class="attr">current-row-key</span>=<span class="string">&quot;id&quot;</span> <span class="attr">:data</span>=<span class="string">&quot;dataList&quot;</span> <span class="attr">stripe</span> <span class="attr">highlight-current-row</span>&gt;</span></span><br><span class="line">                <span class="tag">&lt;<span class="name">el-table-column</span> <span class="attr">type</span>=<span class="string">&quot;index&quot;</span> <span class="attr">align</span>=<span class="string">&quot;center&quot;</span> <span class="attr">label</span>=<span class="string">&quot;序号&quot;</span>&gt;</span><span class="tag">&lt;/<span class="name">el-table-column</span>&gt;</span></span><br><span class="line">                <span class="tag">&lt;<span class="name">el-table-column</span> <span class="attr">prop</span>=<span class="string">&quot;type&quot;</span> <span class="attr">label</span>=<span class="string">&quot;图书类别&quot;</span> <span class="attr">align</span>=<span class="string">&quot;center&quot;</span>&gt;</span><span class="tag">&lt;/<span class="name">el-table-column</span>&gt;</span></span><br><span class="line">                <span class="tag">&lt;<span class="name">el-table-column</span> <span class="attr">prop</span>=<span class="string">&quot;name&quot;</span> <span class="attr">label</span>=<span class="string">&quot;图书名称&quot;</span> <span class="attr">align</span>=<span class="string">&quot;center&quot;</span>&gt;</span><span class="tag">&lt;/<span class="name">el-table-column</span>&gt;</span></span><br><span class="line">                <span class="tag">&lt;<span class="name">el-table-column</span> <span class="attr">prop</span>=<span class="string">&quot;description&quot;</span> <span class="attr">label</span>=<span class="string">&quot;描述&quot;</span> <span class="attr">align</span>=<span class="string">&quot;center&quot;</span>&gt;</span><span class="tag">&lt;/<span class="name">el-table-column</span>&gt;</span></span><br><span class="line">                <span class="tag">&lt;<span class="name">el-table-column</span> <span class="attr">label</span>=<span class="string">&quot;操作&quot;</span> <span class="attr">align</span>=<span class="string">&quot;center&quot;</span>&gt;</span></span><br><span class="line">                    <span class="tag">&lt;<span class="name">template</span> <span class="attr">slot-scope</span>=<span class="string">&quot;scope&quot;</span>&gt;</span></span><br><span class="line">                        <span class="tag">&lt;<span class="name">el-button</span> <span class="attr">type</span>=<span class="string">&quot;primary&quot;</span> <span class="attr">size</span>=<span class="string">&quot;mini&quot;</span>&gt;</span>编辑<span class="tag">&lt;/<span class="name">el-button</span>&gt;</span></span><br><span class="line">                        <span class="tag">&lt;<span class="name">el-button</span> <span class="attr">size</span>=<span class="string">&quot;mini&quot;</span> <span class="attr">type</span>=<span class="string">&quot;danger&quot;</span>&gt;</span>删除<span class="tag">&lt;/<span class="name">el-button</span>&gt;</span></span><br><span class="line">                    <span class="tag">&lt;/<span class="name">template</span>&gt;</span></span><br><span class="line">                <span class="tag">&lt;/<span class="name">el-table-column</span>&gt;</span></span><br><span class="line">            <span class="tag">&lt;/<span class="name">el-table</span>&gt;</span></span><br><span class="line"></span><br><span class="line">            <span class="tag">&lt;<span class="name">div</span> <span class="attr">class</span>=<span class="string">&quot;pagination-container&quot;</span>&gt;</span></span><br><span class="line">                <span class="tag">&lt;<span class="name">el-pagination</span></span></span><br><span class="line"><span class="tag">                        <span class="attr">class</span>=<span class="string">&quot;pagiantion&quot;</span></span></span><br><span class="line"><span class="tag">                        @<span class="attr">current-change</span>=<span class="string">&quot;handleCurrentChange&quot;</span></span></span><br><span class="line"><span class="tag">                        <span class="attr">:current-page</span>=<span class="string">&quot;pagination.currentPage&quot;</span></span></span><br><span class="line"><span class="tag">                        <span class="attr">:page-size</span>=<span class="string">&quot;pagination.pageSize&quot;</span></span></span><br><span class="line"><span class="tag">                        <span class="attr">layout</span>=<span class="string">&quot;total, prev, pager, next, jumper&quot;</span></span></span><br><span class="line"><span class="tag">                        <span class="attr">:total</span>=<span class="string">&quot;pagination.total&quot;</span>&gt;</span></span><br><span class="line">                <span class="tag">&lt;/<span class="name">el-pagination</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="comment">&lt;!-- 新增标签弹层 --&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;add-form&quot;</span>&gt;</span></span><br><span class="line">                <span class="tag">&lt;<span class="name">el-dialog</span> <span class="attr">title</span>=<span class="string">&quot;新增图书&quot;</span> <span class="attr">:visible.sync</span>=<span class="string">&quot;dialogFormVisible&quot;</span>&gt;</span></span><br><span class="line">                    <span class="tag">&lt;<span class="name">el-form</span> <span class="attr">ref</span>=<span class="string">&quot;dataAddForm&quot;</span> <span class="attr">:model</span>=<span class="string">&quot;formData&quot;</span> <span class="attr">:rules</span>=<span class="string">&quot;rules&quot;</span> <span class="attr">label-position</span>=<span class="string">&quot;right&quot;</span></span></span><br><span class="line"><span class="tag">                             <span class="attr">label-width</span>=<span class="string">&quot;100px&quot;</span>&gt;</span></span><br><span class="line">                        <span class="tag">&lt;<span class="name">el-row</span>&gt;</span></span><br><span class="line">                            <span class="tag">&lt;<span class="name">el-col</span> <span class="attr">:span</span>=<span class="string">&quot;12&quot;</span>&gt;</span></span><br><span class="line">                                <span class="tag">&lt;<span class="name">el-form-item</span> <span class="attr">label</span>=<span class="string">&quot;图书类别&quot;</span> <span class="attr">prop</span>=<span class="string">&quot;type&quot;</span>&gt;</span></span><br><span class="line">                                    <span class="tag">&lt;<span class="name">el-input</span> <span class="attr">v-model</span>=<span class="string">&quot;formData.type&quot;</span>/&gt;</span></span><br><span class="line">                                <span class="tag">&lt;/<span class="name">el-form-item</span>&gt;</span></span><br><span class="line">                            <span class="tag">&lt;/<span class="name">el-col</span>&gt;</span></span><br><span class="line">                            <span class="tag">&lt;<span class="name">el-col</span> <span class="attr">:span</span>=<span class="string">&quot;12&quot;</span>&gt;</span></span><br><span class="line">                                <span class="tag">&lt;<span class="name">el-form-item</span> <span class="attr">label</span>=<span class="string">&quot;图书名称&quot;</span> <span class="attr">prop</span>=<span class="string">&quot;name&quot;</span>&gt;</span></span><br><span class="line">                                    <span class="tag">&lt;<span class="name">el-input</span> <span class="attr">v-model</span>=<span class="string">&quot;formData.name&quot;</span>/&gt;</span></span><br><span class="line">                                <span class="tag">&lt;/<span class="name">el-form-item</span>&gt;</span></span><br><span class="line">                            <span class="tag">&lt;/<span class="name">el-col</span>&gt;</span></span><br><span class="line">                        <span class="tag">&lt;/<span class="name">el-row</span>&gt;</span></span><br><span class="line">                        <span class="tag">&lt;<span class="name">el-row</span>&gt;</span></span><br><span class="line">                            <span class="tag">&lt;<span class="name">el-col</span> <span class="attr">:span</span>=<span class="string">&quot;24&quot;</span>&gt;</span></span><br><span class="line">                                <span class="tag">&lt;<span class="name">el-form-item</span> <span class="attr">label</span>=<span class="string">&quot;描述&quot;</span>&gt;</span></span><br><span class="line">                                    <span class="tag">&lt;<span class="name">el-input</span> <span class="attr">v-model</span>=<span class="string">&quot;formData.description&quot;</span> <span class="attr">type</span>=<span class="string">&quot;textarea&quot;</span>&gt;</span><span class="tag">&lt;/<span class="name">el-input</span>&gt;</span></span><br><span class="line">                                <span class="tag">&lt;/<span class="name">el-form-item</span>&gt;</span></span><br><span class="line">                            <span class="tag">&lt;/<span class="name">el-col</span>&gt;</span></span><br><span class="line">                        <span class="tag">&lt;/<span class="name">el-row</span>&gt;</span></span><br><span class="line">                    <span class="tag">&lt;/<span class="name">el-form</span>&gt;</span></span><br><span class="line">                    <span class="tag">&lt;<span class="name">div</span> <span class="attr">slot</span>=<span class="string">&quot;footer&quot;</span> <span class="attr">class</span>=<span class="string">&quot;dialog-footer&quot;</span>&gt;</span></span><br><span class="line">                        <span class="tag">&lt;<span class="name">el-button</span> @<span class="attr">click</span>=<span class="string">&quot;dialogFormVisible = false&quot;</span>&gt;</span>取消<span class="tag">&lt;/<span class="name">el-button</span>&gt;</span></span><br><span class="line">                        <span class="tag">&lt;<span class="name">el-button</span> <span class="attr">type</span>=<span class="string">&quot;primary&quot;</span> @<span class="attr">click</span>=<span class="string">&quot;saveBook()&quot;</span>&gt;</span>确定<span class="tag">&lt;/<span class="name">el-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 class="tag">&lt;/<span class="name">el-dialog</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">div</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 class="tag">&lt;/<span class="name">div</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><br><span class="line"><span class="comment">&lt;!-- 引入组件库 --&gt;</span></span><br><span class="line"><span class="tag">&lt;<span class="name">script</span> <span class="attr">src</span>=<span class="string">&quot;../js/vue.js&quot;</span>&gt;</span><span class="tag">&lt;/<span class="name">script</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;<span class="name">script</span> <span class="attr">src</span>=<span class="string">&quot;../plugins/elementui/index.js&quot;</span>&gt;</span><span class="tag">&lt;/<span class="name">script</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;<span class="name">script</span> <span class="attr">type</span>=<span class="string">&quot;text/javascript&quot;</span> <span class="attr">src</span>=<span class="string">&quot;../js/jquery.min.js&quot;</span>&gt;</span><span class="tag">&lt;/<span class="name">script</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;<span class="name">script</span> <span class="attr">src</span>=<span class="string">&quot;../js/axios-0.18.0.js&quot;</span>&gt;</span><span class="tag">&lt;/<span class="name">script</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">var</span> vue = <span class="keyword">new</span> <span class="title class_">Vue</span>(&#123;</span></span><br><span class="line"><span class="language-javascript"></span></span><br><span class="line"><span class="language-javascript">        <span class="attr">el</span>: <span class="string">&#x27;#app&#x27;</span>,</span></span><br><span class="line"><span class="language-javascript"></span></span><br><span class="line"><span class="language-javascript">        <span class="attr">data</span>: &#123;</span></span><br><span class="line"><span class="language-javascript">            <span class="attr">dataList</span>: [],<span class="comment">//当前页要展示的分页列表数据</span></span></span><br><span class="line"><span class="language-javascript">            <span class="attr">formData</span>: &#123;&#125;,<span class="comment">//表单数据</span></span></span><br><span class="line"><span class="language-javascript">            <span class="attr">dialogFormVisible</span>: <span class="literal">false</span>,<span class="comment">//增加表单是否可见</span></span></span><br><span class="line"><span class="language-javascript">            <span class="attr">dialogFormVisible4Edit</span>: <span class="literal">false</span>,<span class="comment">//编辑表单是否可见</span></span></span><br><span class="line"><span class="language-javascript">            <span class="attr">pagination</span>: &#123;&#125;,<span class="comment">//分页模型数据，暂时弃用</span></span></span><br><span class="line"><span class="language-javascript">        &#125;,</span></span><br><span class="line"><span class="language-javascript"></span></span><br><span class="line"><span class="language-javascript">        <span class="comment">//钩子函数，VUE对象初始化完成后自动执行</span></span></span><br><span class="line"><span class="language-javascript">        <span class="title function_">created</span>(<span class="params"></span>) &#123;</span></span><br><span class="line"><span class="language-javascript">            <span class="variable language_">this</span>.<span class="title function_">getAll</span>();</span></span><br><span class="line"><span class="language-javascript">        &#125;,</span></span><br><span class="line"><span class="language-javascript"></span></span><br><span class="line"><span class="language-javascript">        <span class="attr">methods</span>: &#123;</span></span><br><span class="line"><span class="language-javascript">            <span class="comment">// 重置表单</span></span></span><br><span class="line"><span class="language-javascript">            <span class="title function_">resetForm</span>(<span class="params"></span>) &#123;</span></span><br><span class="line"><span class="language-javascript">                <span class="comment">//清空输入框</span></span></span><br><span class="line"><span class="language-javascript">                <span class="variable language_">this</span>.<span class="property">formData</span> = &#123;&#125;;</span></span><br><span class="line"><span class="language-javascript">            &#125;,</span></span><br><span class="line"><span class="language-javascript"></span></span><br><span class="line"><span class="language-javascript">            <span class="comment">// 弹出添加窗口</span></span></span><br><span class="line"><span class="language-javascript">            <span class="title function_">openSave</span>(<span class="params"></span>) &#123;</span></span><br><span class="line"><span class="language-javascript">                <span class="variable language_">this</span>.<span class="property">dialogFormVisible</span> = <span class="literal">true</span>;</span></span><br><span class="line"><span class="language-javascript">                <span class="variable language_">this</span>.<span class="title function_">resetForm</span>();</span></span><br><span class="line"><span class="language-javascript">            &#125;,</span></span><br><span class="line"><span class="language-javascript"></span></span><br><span class="line"><span class="language-javascript">            <span class="comment">//添加</span></span></span><br><span class="line"><span class="language-javascript">            <span class="title function_">saveBook</span>(<span class="params"></span>) &#123;</span></span><br><span class="line"><span class="language-javascript">                axios.<span class="title function_">post</span>(<span class="string">&quot;/books&quot;</span>, <span class="variable language_">this</span>.<span class="property">formData</span>).<span class="title function_">then</span>(<span class="function">(<span class="params">res</span>) =&gt;</span> &#123;</span></span><br><span class="line"><span class="language-javascript"></span></span><br><span class="line"><span class="language-javascript">                &#125;);</span></span><br><span class="line"><span class="language-javascript">            &#125;,</span></span><br><span class="line"><span class="language-javascript"></span></span><br><span class="line"><span class="language-javascript">            <span class="comment">//主页列表查询</span></span></span><br><span class="line"><span class="language-javascript">            <span class="title function_">getAll</span>(<span class="params"></span>) &#123;</span></span><br><span class="line"><span class="language-javascript">                axios.<span class="title function_">get</span>(<span class="string">&quot;/books&quot;</span>).<span class="title function_">then</span>(<span class="function">(<span class="params">res</span>) =&gt;</span> &#123;</span></span><br><span class="line"><span class="language-javascript">                    <span class="variable language_">this</span>.<span class="property">dataList</span> = res.<span class="property">data</span>;</span></span><br><span class="line"><span class="language-javascript">                &#125;);</span></span><br><span class="line"><span class="language-javascript">            &#125;,</span></span><br><span class="line"><span class="language-javascript"></span></span><br><span class="line"><span class="language-javascript">        &#125;</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">html</span>&gt;</span></span><br></pre></td></tr></table></figure><p>由于按钮的事件都绑定好了，所以我们重点只关注<code>saveBook()</code>和<code>getAll()</code>这两个函数就行</p></li><li><p>当调用<code>getAll()</code>函数时，我们需要将响应的JSON数据赋给dataList即可，而响应JSON数据我们在上一小节已经完成了</p></li></ul>]]></content>
    
    
      
      
    <summary type="html">&lt;ul&gt;
&lt;li&gt;&lt;p&gt;SpringMVC属于Spring框架的一部分，主要用来进行Web开发，是对Servlet进行了封装。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;SpringMVC是处于Web层的框架，所以主要作用就是用来接收前端发过来的请求和数据，然后经过处理之后将处理结果响应</summary>
      
    
    
    
    
    <category term="SpringMVC" scheme="https://silvan.chat/tags/SpringMVC/"/>
    
  </entry>
  
  <entry>
    <title>Spring(黑马)</title>
    <link href="https://silvan.chat/2025/10/28/SSM-%E9%BB%91%E9%A9%AC-Spring-%E9%BB%91%E9%A9%AC/"/>
    <id>https://silvan.chat/2025/10/28/SSM-%E9%BB%91%E9%A9%AC-Spring-%E9%BB%91%E9%A9%AC/</id>
    <published>2025-10-28T11:42:57.655Z</published>
    <updated>2025-10-28T11:42:57.655Z</updated>
    
    <content type="html"><![CDATA[<h1 id="课程介绍"><a href="#课程介绍" class="headerlink" title="课程介绍"></a>课程介绍</h1><div class="note info no-icon flat"><p>Spring是一款功能强大的框架，主要优势是<code>简化开发</code>和<code>框架整合</code></p></div><ul><li><code>简化开发</code>：Spring框架中提供了两个大的核心技术，分别是:<ul><li>IOC</li><li>AOP<ul><li>事务处理</li></ul></li><li>IoC和AOP是Spring的重要知识点，事务处理可以简化项目中的事务管理，也是一大亮点</li></ul></li><li><code>框架整合</code>：Spring整合了市面几乎所有主流框架，如Hibernate、MyBatis、Struts2等</li></ul><div class="note danger no-icon flat"><p>综上，对Spring的学习，主要学习四块内容：</p><ol><li>IOC</li><li>整合Mybatis(IOC的具体应用)</li><li>AOP</li><li>声明式事务(AOP的具体应用)</li></ol></div><h1 id="Spring相关概念"><a href="#Spring相关概念" class="headerlink" title="Spring相关概念"></a>Spring相关概念</h1><h2 id="Spring发展史"><a href="#Spring发展史" class="headerlink" title="Spring发展史"></a>Spring发展史</h2><p>Spring发展至今早已形成了一种开发的生态圈，提供了若干个项目，这些项目组合起来便被称为<code>Spring全家桶</code><br><div class="img-wrap"><div class="img-bg"><img class="img" src="https://raw.githubusercontent.com/silvan2077/markdown_pic/main/Picgo/20251006114234.png"/></div></div><br>可以到<a href="https://spring.io/projects">Spring官方网站</a>查看具体图标的含义，这里重点关注<code>Spring Framework</code>、<code>SpringBoot</code>和<code>SpringCloud</code>:</p><ul><li><strong>Spring Framework</strong>：Spring框架，是Spring中最早最核心的技术，也是所有其他技术的基础。</li><li><strong>SpringBoot</strong>：Spring是来简化开发，而SpringBoot是来帮助Spring在简化的基础上能更快速进行开发。</li><li><strong>SpringCloud</strong>：这个是用来做分布式之微服务架构的相关开发。</li></ul><div class="note info no-icon flat"><p>除了这三个，还有还有很多其他的技术，也比较流行，如<code>SpringData</code>，<code>SpringSecurity</code>等，这里学习的Spring其实指的是<code>Spring Framework</code>。</p></div><p>接下来介绍Spring Framework是怎么来的：<br><div class="img-wrap"><div class="img-bg"><img class="img" src="https://raw.githubusercontent.com/silvan2077/markdown_pic/main/Picgo/20251006133152.png"/></div></div></p><ul><li>IBM(IT公司-国际商业机器公司)在1997年提出了EJB思想,早期的JAVAEE开发大都基于该思想。</li><li>Rod Johnson(Java和J2EE开发领域的专家)在2002年出版的<code>Expert One-on-One J2EE Design and Development</code>,书中有阐述在开发中使用EJB该如何做。</li><li>Rod Johnson在2004年出版的<code>Expert One-on-One J2EE Development without EJB</code>,书中提出了比EJB思想更高效的实现方案，并且在同年将方案进行了具体的落地实现，这个实现就是Spring1.0。</li><li>随着时间推移，版本不断更新维护，目前最新的是Spring5<ul><li>Spring1.0是纯配置文件开发</li><li>Spring2.0为了简化开发引入了注解开发，此时是配置文件加注解的开发方式</li><li>Spring3.0已经可以进行纯注解开发，使开发效率大幅提升，我们的课程会以注解开发为主</li><li>Spring4.0根据JDK的版本升级对个别API进行了调整</li><li>Spring5.0已经全面支持JDK8，所以学习期间最好用JDK8版本</li></ul></li><li>本节介绍了Spring家族与Spring的发展史，需要我们重点掌握的是:<ul><li>Spring其实是Spring家族中的Spring Framework</li><li>Spring Framework是Spring家族中其他框架的底层基础，学好Spring可以为其他Spring框架的学习打好基础</li></ul></li></ul><h2 id="Spring系统架构"><a href="#Spring系统架构" class="headerlink" title="Spring系统架构"></a>Spring系统架构</h2><ul><li>SpringFramework是Spring其他项目的根基。</li><li>Spring Framework的发展也经历了很多版本的变更，每个版本都有相应的调整<div class="img-wrap"><div class="img-bg"><img class="img" src="https://pic.imgdb.cn/item/630c2c6e16f2c2beb1ee0194.jpg"/></div></div></li><li>Spring Framework的5版本目前没有最新的架构图，而最新的是4版本，所以接下来主要研究的是4的架构图<div class="img-wrap"><div class="img-bg"><img class="img" src="https://raw.githubusercontent.com/silvan2077/markdown_pic/main/Picgo/20251006133515.png"/></div></div></li></ul><ol><li><strong>核心层</strong><ul><li>Core Container：核心容器，这个模块是Spring最核心的模块，其他的都需要依赖该模块</li></ul></li><li><strong>AOP层</strong><ul><li>AOP(Aspect Oriented Programming)：面向切面编程，它依赖核心层容器，目的是在不改变原有代码的前提下对其进行功能增强</li><li>Aspects：AOP是思想，Aspects是对AOP思想的具体实现</li></ul></li><li><strong>数据层</strong><ul><li>Data Access：数据访问，Spring全家桶中有对数据访问的具体实现技术</li><li>Data Integration：数据集成，Spring支持整合其他的数据层解决方案，比如Mybatis</li><li>Transactions：事务，Spring中事务管理是Spring AOP的一个具体实现，也是后期学习的重点内容</li></ul></li><li><strong>Web层</strong><ul><li>这一层的内容将在SpringMVC框架具体学习</li></ul></li><li><strong>Test层</strong><ul><li>Spring主要整合了Junit来完成单元测试和集成测试</li></ul></li></ol><h2 id="Spring学习路线"><a href="#Spring学习路线" class="headerlink" title="Spring学习路线"></a>Spring学习路线</h2><ol><li>Spring的IOC/DI</li><li>Spring的AOP</li><li>AOP的具体应用,事务管理</li><li>IOC/DI的具体应用,整合Mybatis</li></ol><div class="img-wrap"><div class="img-bg"><img class="img" src="https://raw.githubusercontent.com/silvan2077/markdown_pic/main/Picgo/20251006134142.png"/></div></div><h2 id="Spring核心概念"><a href="#Spring核心概念" class="headerlink" title="Spring核心概念"></a>Spring核心概念</h2><p>这部分主要包括<code>IOC/DI</code>、<code>IOC容器</code>和<code>Bean</code></p><h3 id="目前的问题"><a href="#目前的问题" class="headerlink" title="目前的问题"></a>目前的问题</h3><p>分析代码在编写中的问题：</p><ol><li>业务层需要调用数据层的方法，就需要在业务层new数据层的对象</li><li>如果数据层的实现类发生变化，那么业务层的代码也需要跟着改变，发生变更后，都需要进行编译打包和重部署<div class="note info no-icon flat"><p>编写代码存在的问题：耦合度偏高，一个类发生变化，其他类也要跟着修改</p></div></li></ol><div class="tabs" id="问题提出"><ul class="nav-tabs"><li class="tab active"><button type="button" data-href="#问题提出-1">业务层实现</button></li><li class="tab"><button type="button" data-href="#问题提出-2">数据层实现</button></li></ul><div class="tab-contents"><div class="tab-item-content active" id="问题提出-1"><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><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_">BookServlet</span> <span class="keyword">extends</span> <span class="title class_">BookServlet</span> &#123;</span><br><span class="line">    <span class="comment">//这个地方是写死了的</span></span><br><span class="line">    <span class="keyword">private</span> <span class="type">BookDao</span> <span class="variable">bookDao</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">BookDaoImpl</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">()</span> &#123;</span><br><span class="line">        bookDao.save();</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><button type="button" class="tab-to-top" aria-label="scroll to top"><i class="fas fa-arrow-up"></i></button></div><div class="tab-item-content" id="问题提出-2"><p>假如BookDaoImpl2比BookDaoImpl1好用，我们想更换数据层实现类，那么业务层的代码也得跟着变，耦合度太高了<br><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><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_">BookDaoImpl1</span> <span class="keyword">implements</span> <span class="title class_">BookDao</span> &#123;</span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">save</span><span class="params">()</span> &#123;</span><br><span class="line">        System.out.println(<span class="string">&quot;book dao save ... 1&quot;</span>);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><br><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><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_">BookDaoImpl2</span> <span class="keyword">implements</span> <span class="title class_">BookDao</span> &#123;</span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">save</span><span class="params">()</span> &#123;</span><br><span class="line">        System.out.println(<span class="string">&quot;book dao save ... 2&quot;</span>);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></p><button type="button" class="tab-to-top" aria-label="scroll to top"><i class="fas fa-arrow-up"></i></button></div></div></div><ul><li>主要耦合度高的原因是在业务层中直接new了数据层的实现类，如果我们不new对象，只声明一下，数据层跟业务层不就解耦了吗？<ul><li>答案显然不行，因为不new对象此时BookDao是一个空引用，强行调用save()方法会报<code>空指针异常</code>,所以现在主要问题是如何做到我们不创建对象，但运行时数据层对象被创建，且被赋值给业务层，这该如何实现？</li></ul></li><li><strong>针对该问题，Spring提出的解决方案：使用对象时，程序中不再主动new对象，而让Spring为我们提供并管理这个对象</strong></li></ul><h3 id="IOC-DI"><a href="#IOC-DI" class="headerlink" title="IOC/DI"></a>IOC/DI</h3><ul><li><p><strong>IOC(Inversion of Control)控制反转</strong></p><ul><li><p><strong>什么是控制反转？</strong></p><ul><li>控制反转是一种更宽泛的<code>设计模式</code>或架构原则，能够用来降低代码代码之间的耦合度，符合<code>依赖倒置</code>原则。</li><li><strong>控制反转的核心是：将对象的创建权交出去，将对象和对象之间关系的管理权交出去，有第三方容器来负责创建与维护</strong></li></ul></li><li><p><strong>Spring和IOC之间的关系是什么呢?</strong></p><ul><li>Spring技术对IOC思想进行了实现</li><li>Spring提供了一个容器，称为<code>IOC容器</code>，用来充当IOC思想中的”外部”</li><li>IOC思想中的<code>别人[外部]</code>指的就是<code>Spring的IOC容器</code></li></ul></li><li><strong>IOC容器的作用以及内部存放的是什么?</strong><ul><li>IOC容器负责对象的创建、初始化等一系列工作，其中包含了数据层和业务层的类对象</li><li>被创建或被管理的对象在IOC容器中统称为<code>Bean</code></li></ul></li><li><strong>当IOC容器中创建好service和dao对象后，程序就能正确执行了吗?</strong><ul><li>不行，因为service运行需要依赖dao对象</li><li>IOC容器中虽然有service和dao对象,但此时service对象和dao对象是两个单独的类，没有任何关系，需要把dao对象交给service,也就是说要绑定service和dao对象之间的关系才行</li><li>像这种在容器中建立对象与对象之间的绑定关系就要用到<code>DI(Dependency Injection)依赖注入</code>.</li></ul></li></ul></li><li><strong>DI(Dependency Injection)依赖注入</strong><ul><li><strong>什么是依赖注入?</strong><ul><li>在容器中建立bean与bean之间的依赖关系的整个过程，称为<code>依赖注入</code><ul><li>业务层要用数据层的类对象，以前是自己<code>new</code>的</li><li>现在自己不new了，靠<code>别人[外部其实指的就是IOC容器]</code>来给注入进来</li></ul></li></ul></li><li><strong>IOC容器中哪些bean之间要建立依赖关系呢?</strong><ul><li>这个需要程序员根据业务需求提前建立好关系，如业务层需要依赖数据层，service就要和dao建立依赖关系</li></ul></li><li>我们会发现Spring容器中IOC和AOP这两个概念的最终目标就是:<code>充分解耦</code>，具体实现靠:<ul><li>使用IOC容器管理bean(IOC)</li><li>在IOC容器内将有依赖关系的bean进行关系绑定（DI）</li><li><strong>最终结果为:使用对象时不仅可以直接从IOC容器中获取，并且获取到的bean已经绑定了所有的依赖关系。</strong></li></ul></li></ul></li></ul><h3 id="核心概念小结"><a href="#核心概念小结" class="headerlink" title="核心概念小结"></a>核心概念小结</h3><ol><li><strong>什么是IOC/DI思想？</strong><ul><li>IOC:控制反转，控制反转的是对象的创建权,由Spring为我们创建并管理对象</li><li>DI:依赖注入，绑定对象与对象之间的依赖关系,即给对象的属性赋值</li></ul></li><li><strong>什么是IOC容器?</strong><ul><li>Spring创建了一个容器用来存放所创建的对象，这个容器就叫IOC容器</li></ul></li><li><strong>什么是Bean?</strong><ul><li>容器中所存放的一个个对象就叫Bean</li></ul></li></ol><h1 id="入门案例"><a href="#入门案例" class="headerlink" title="入门案例"></a>入门案例</h1><h2 id="IOC入门案例"><a href="#IOC入门案例" class="headerlink" title="IOC入门案例"></a>IOC入门案例</h2><div class="note info no-icon flat"><p>需求：将BookServiceImpl和BookDaoImpl交给Spring管理，并从容器中获取对应的bean对象调用其方法</p></div><ol><li>创建Maven项目</li><li>添加Spring需要的依赖,仅仅使用IOC功能则引入Spring-context这个jar包即可<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>org.springframework<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-context<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.2.10.RELEASE<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></li><li>创建BookDao，BookDaoImpl，BookService和BookServiceImpl四个类<div class="tabs" id="ioc入门案例"><ul class="nav-tabs"><li class="tab active"><button type="button" data-href="#ioc入门案例-1">BookDao</button></li><li class="tab"><button type="button" data-href="#ioc入门案例-2">BookDaoImpl</button></li><li class="tab"><button type="button" data-href="#ioc入门案例-3">BookService</button></li><li class="tab"><button type="button" data-href="#ioc入门案例-4">BookServiceImpl</button></li></ul><div class="tab-contents"><div class="tab-item-content active" id="ioc入门案例-1"><figure class="highlight java"><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="keyword">public</span> <span class="keyword">interface</span> <span class="title class_">BookDao</span> &#123;</span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">save</span><span class="params">()</span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><button type="button" class="tab-to-top" aria-label="scroll to top"><i class="fas fa-arrow-up"></i></button></div><div class="tab-item-content" id="ioc入门案例-2"><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><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_">BookDaoImpl</span> <span class="keyword">implements</span> <span class="title class_">BookDao</span> &#123;</span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">save</span><span class="params">()</span> &#123;</span><br><span class="line">        System.out.println(<span class="string">&quot;book dao save ...&quot;</span>);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><button type="button" class="tab-to-top" aria-label="scroll to top"><i class="fas fa-arrow-up"></i></button></div><div class="tab-item-content" id="ioc入门案例-3"><figure class="highlight java"><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="keyword">public</span> <span class="keyword">interface</span> <span class="title class_">BookService</span> &#123;</span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">save</span><span class="params">()</span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><button type="button" class="tab-to-top" aria-label="scroll to top"><i class="fas fa-arrow-up"></i></button></div><div class="tab-item-content" id="ioc入门案例-4"><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><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_">BookServiceImpl</span> <span class="keyword">implements</span> <span class="title class_">BookService</span> &#123;</span><br><span class="line">    <span class="comment">// 这里不考虑依赖注入，仅仅学习IOC相关内容，所以手动new了一个BookDaoImpl对象</span></span><br><span class="line">    <span class="keyword">private</span> <span class="type">BookDao</span> <span class="variable">bookDao</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">BookDaoImpl</span>();</span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">save</span><span class="params">()</span> &#123;</span><br><span class="line">        System.out.println(<span class="string">&quot;book service save ...&quot;</span>);</span><br><span class="line">        bookDao.save();</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><button type="button" class="tab-to-top" aria-label="scroll to top"><i class="fas fa-arrow-up"></i></button></div></div></div></li><li>创建Spring配置文件<blockquote><p>在resource目录下新建-&gt;XML配置文件-&gt;Spring配置</p></blockquote></li><li>在配置文件中完成Bean的配置<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="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">beans</span> <span class="attr">xmlns</span>=<span class="string">&quot;http://www.springframework.org/schema/beans&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://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd&quot;</span>&gt;</span></span><br><span class="line">      <span class="comment">&lt;!--</span></span><br><span class="line"><span class="comment">      id属性标示给bean起名字</span></span><br><span class="line"><span class="comment">      class属性写类的全路径，得是具体的实现类而不是接口，要靠这个造对象</span></span><br><span class="line"><span class="comment">      还可以写name属性，作用和id一样，可以用来给对象起别名</span></span><br><span class="line"><span class="comment">      --&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">bean</span> <span class="attr">id</span>=<span class="string">&quot;bookDao&quot;</span> <span class="attr">class</span>=<span class="string">&quot;com.blog.dao.impl.BookDaoImpl&quot;</span>/&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">bean</span> <span class="attr">id</span>=<span class="string">&quot;bookService&quot;</span> <span class="attr">class</span>=<span class="string">&quot;com.blog.service.impl.BookServiceImpl&quot;</span>/&gt;</span></span><br><span class="line"><span class="tag">&lt;/<span class="name">beans</span>&gt;</span></span><br></pre></td></tr></table></figure><div class="note warning no-icon flat"><p>Bean定义时，id属性和class属性是必须的，其他属性都是可选的，比如name属性<br>Bean的id属性在同一个配置文件不能重复</p></div></li><li>获取IOC容器<br>使用Spring提供的接口完成IoC容器的获取<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><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="keyword">public</span> <span class="keyword">class</span> <span class="title class_">App</span> &#123;</span><br><span class="line">    <span class="comment">// ClassPathXmlApplicationContext是使用配置文件的方式获取IOC容器</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_">main</span><span class="params">(String[] args)</span> &#123;</span><br><span class="line">        <span class="type">ApplicationContext</span> <span class="variable">context</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">ClassPathXmlApplicationContext</span>(<span class="string">&quot;applicationContext.xml&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></li><li>从容器中获取对象并调用方法<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><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="keyword">public</span> <span class="keyword">class</span> <span class="title class_">App</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">ApplicationContext</span> <span class="variable">context</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">ClassPathXmlApplicationContext</span>(<span class="string">&quot;applicationContext.xml&quot;</span>);</span><br><span class="line">        <span class="type">BookService</span> <span class="variable">bookService</span> <span class="operator">=</span> (BookService) context.getBean(<span class="string">&quot;bookService&quot;</span>);</span><br><span class="line">        bookService.save();</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></li><li>运行程序<br>测试结果：<blockquote><p>book service save …<br>book dao save …</p></blockquote></li></ol><div class="note info no-icon flat"><p>对Ioc的入门案例已经完成，但在上文BookServiceImpl中的bookDao对象是手动new的，耦合度依旧较高，解决这个问题便需要下面的<code>DI(依赖注入)</code></p></div><h2 id="DI入门案例"><a href="#DI入门案例" class="headerlink" title="DI入门案例"></a>DI入门案例</h2><div class="note info no-icon flat"><p>需求：基于IOC入门案例，建立好service和dao之间的依赖关系，使用DI完成Dao层的注入</p></div><ol><li>删除service层new对象的行为<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><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_">BookServiceImpl</span> <span class="keyword">implements</span> <span class="title class_">BookService</span> &#123;</span><br><span class="line">    <span class="comment">//private BookDao bookDao = new BookDaoImpl();</span></span><br><span class="line">    <span class="keyword">private</span> BookDao bookDao;</span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">save</span><span class="params">()</span> &#123;</span><br><span class="line">        System.out.println(<span class="string">&quot;book service save ...&quot;</span>);</span><br><span class="line">        bookDao.save();</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></li><li>在业务层提供需要注入的属性的set方法<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><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">public</span> <span class="keyword">class</span> <span class="title class_">BookServiceImpl</span> <span class="keyword">implements</span> <span class="title class_">BookService</span> &#123;</span><br><span class="line">    <span class="keyword">private</span> BookDao bookDao;</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">()</span> &#123;</span><br><span class="line">        System.out.println(<span class="string">&quot;book service save ...&quot;</span>);</span><br><span class="line">        bookDao.save();</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_">setBookDao</span><span class="params">(BookDao bookDao)</span> &#123;</span><br><span class="line">        <span class="built_in">this</span>.bookDao = bookDao;</span><br><span class="line">        <span class="comment">// 添加该语句检验set方法是否被调用</span></span><br><span class="line">        System.out.println(<span class="string">&quot;set方法被调用啦&quot;</span>);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></li><li>在配置文件中使用<code>&lt;property&gt;</code>标签完成依赖注入<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="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">beans</span> <span class="attr">xmlns</span>=<span class="string">&quot;http://www.springframework.org/schema/beans&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://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd&quot;</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">bean</span> <span class="attr">id</span>=<span class="string">&quot;bookDao&quot;</span> <span class="attr">class</span>=<span class="string">&quot;com.blog.dao.impl.BookDaoImpl&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">bean</span> <span class="attr">id</span>=<span class="string">&quot;bookService&quot;</span> <span class="attr">class</span>=<span class="string">&quot;com.blog.service.impl.BookServiceImpl&quot;</span>&gt;</span></span><br><span class="line">        <span class="comment">&lt;!--配置server与dao的关系--&gt;</span></span><br><span class="line">        <span class="comment">&lt;!--</span></span><br><span class="line"><span class="comment">            property标签表示配置当前bean的属性</span></span><br><span class="line"><span class="comment">            name属性表示配置哪一个具体的属性(这里是配置bookService的bookDao属性)</span></span><br><span class="line"><span class="comment">            ref属性表示参照哪一个bean(需要当前配置文件中配置的bean的id)</span></span><br><span class="line"><span class="comment">        --&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;bookDao&quot;</span> <span class="attr">ref</span>=<span class="string">&quot;bookDao&quot;</span>&gt;</span><span class="tag">&lt;/<span class="name">property</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;/<span class="name">bean</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;/<span class="name">beans</span>&gt;</span></span><br></pre></td></tr></table></figure><div class="note info no-icon flat"><p>在property标签中使用了两个bookDao</p><ul><li><strong>name=”bookDao”，这里是Service层中的属性名</strong>，用是让Spring的IOC容器在获取到名称后，将首字母大写，前面加set找对应的setBookDao()方法进行对象注入</li><li><strong>ref=”bookDao”，这里是Dao层中的bean的id</strong>，用来参照当前配置文件中配置的bean的id，将其注入到Service层的bookDao属性中</li></ul></div><div class="img-wrap"><div class="img-bg"><img class="img" src="https://raw.githubusercontent.com/silvan2077/markdown_pic/main/Picgo/20251006205842.png"/></div></div></li><li>运行程序<br>测试结果：<blockquote><p>set方法被调用啦<br>book service save …<br>book dao save …</p></blockquote></li></ol><h1 id="IOC相关内容"><a href="#IOC相关内容" class="headerlink" title="IOC相关内容"></a>IOC相关内容</h1><h2 id="Bean基础配置"><a href="#Bean基础配置" class="headerlink" title="Bean基础配置"></a>Bean基础配置</h2><ul><li>接下来主要说明<code>Bean基础配置</code>，<code>Bean别名配置</code>以及<code>Bean的作用范围配置</code><h3 id="id和class"><a href="#id和class" class="headerlink" title="id和class"></a>id和class</h3>bean标签的功能、使用方式以及id和class属性的作用，通过下面这张图简单描述<div class="img-wrap"><div class="img-bg"><img class="img" src="https://raw.githubusercontent.com/silvan2077/markdown_pic/main/Picgo/20251006210426.png"/></div></div></li></ul><div class="note danger no-icon flat"><p><strong>id和class属性都是必须的，id属性是bean的标识符，class属性是bean的类全路径</strong></p></div><h3 id="name"><a href="#name" class="headerlink" title="name"></a>name</h3><p>name属性的作用和id属性一样，可以用来给bean起别名，<strong>name属性可以省略</strong></p><ol><li>配置别名<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="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">beans</span> <span class="attr">xmlns</span>=<span class="string">&quot;http://www.springframework.org/schema/beans&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://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd&quot;</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">bean</span> <span class="attr">id</span>=<span class="string">&quot;bookDao&quot;</span> <span class="attr">class</span>=<span class="string">&quot;com.blog.dao.impl.BookDaoImpl&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">bean</span> <span class="attr">id</span>=<span class="string">&quot;bookService&quot;</span> <span class="attr">name</span>=<span class="string">&quot;service1 service2 service3&quot;</span> <span class="attr">class</span>=<span class="string">&quot;com.blog.service.impl.BookServiceImpl&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;bookDao&quot;</span> <span class="attr">ref</span>=<span class="string">&quot;bookDao&quot;</span>&gt;</span><span class="tag">&lt;/<span class="name">property</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;/<span class="name">bean</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;/<span class="name">beans</span>&gt;</span></span><br></pre></td></tr></table></figure></li><li>根据别名来获取Bean对象<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><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_">App</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">ApplicationContext</span> <span class="variable">context</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">ClassPathXmlApplicationContext</span>(<span class="string">&quot;applicationContext.xml&quot;</span>);</span><br><span class="line">        <span class="comment">//此处根据bean标签的id属性和name属性的任意一个值来获取bean对象</span></span><br><span class="line">        <span class="type">BookService</span> <span class="variable">bookService</span> <span class="operator">=</span> (BookService) context.getBean(<span class="string">&quot;service2&quot;</span>);</span><br><span class="line">        bookService.save();</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></li><li>运行程序<br>测试结果：<blockquote><p>set方法被调用啦<br>book service save …<br>book dao save …</p></blockquote></li></ol><div class="note warning no-icon flat"><ul><li>Bean依赖注入的ref属性的值也可以是name里的别名，但是<strong>建议使用Id值来注入</strong></li><li>在调用getBean方法时，传入一个不存在的名称，就会报错<code>NoSuchBeanDefinitionException</code>，此时就要检查是哪一边的名称写错了</li></ul></div><h3 id="scope"><a href="#scope" class="headerlink" title="scope"></a>scope</h3><p>关于bean的作用范围是bean属性配置的一个重点内容。<br>bean的scope有两个取值：</p><ul><li>singleton：单例（默认）</li><li>prototype：非单例</li><li><h4 id="验证对象是否为单例"><a href="#验证对象是否为单例" class="headerlink" title="验证对象是否为单例"></a>验证对象是否为单例</h4><div class="note info no-icon flat"><p>验证思路：获取同一个Bean两次，打印这两次的地址，如果地址相同则说明是单例</p></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></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">App</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">ApplicationContext</span> <span class="variable">context</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">ClassPathXmlApplicationContext</span>(<span class="string">&quot;applicationContext.xml&quot;</span>);</span><br><span class="line">        <span class="comment">//我这里使用了别名，其实还是同一个bean</span></span><br><span class="line">        <span class="type">BookService</span> <span class="variable">bookService2</span> <span class="operator">=</span> (BookService) context.getBean(<span class="string">&quot;service2&quot;</span>);</span><br><span class="line">        <span class="type">BookService</span> <span class="variable">bookService3</span> <span class="operator">=</span> (BookService) context.getBean(<span class="string">&quot;service3&quot;</span>);</span><br><span class="line">        System.out.println(bookService2);</span><br><span class="line">        System.out.println(bookService3);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>输出结果发现地址值一样，则证明默认创建的为单例对象<blockquote><p>com.blog.service.impl.BookServiceImpl@25bbe1b6<br>com.blog.service.impl.BookServiceImpl@25bbe1b6</p></blockquote></li></ul><h4 id="配置Bean为非单例"><a href="#配置Bean为非单例" class="headerlink" title="配置Bean为非单例"></a>配置Bean为非单例</h4><p>正常情况下，Spring为我们创建的对象都是单例的，如果想要创建非单例的对象，需要我们在配置文件中配置scope属性</p><ul><li>在Spring的配置文件中，修改<code>&lt;bean&gt;</code>的scope属性<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="tag">&lt;<span class="name">bean</span> <span class="attr">id</span>=<span class="string">&quot;bookService&quot;</span> <span class="attr">name</span>=<span class="string">&quot;service1 service2 service3&quot;</span> <span class="attr">class</span>=<span class="string">&quot;com.blog.service.impl.BookServiceImpl&quot;</span> <span class="attr">scope</span>=<span class="string">&quot;prototype&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;bookDao&quot;</span> <span class="attr">ref</span>=<span class="string">&quot;bookDao&quot;</span>&gt;</span><span class="tag">&lt;/<span class="name">property</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;/<span class="name">bean</span>&gt;</span></span><br></pre></td></tr></table></figure>此时重新验证，便会发现地址值不一致。</li></ul><h4 id="Scope总结"><a href="#Scope总结" class="headerlink" title="Scope总结"></a>Scope总结</h4><ul><li><strong>为什么Spring创建Bean默认为单例？</strong><ul><li>Bean默认是单例意味着Spring的IOC容器只会存有该类的一个对象</li><li>对象只有一个有效避免了对象的频繁创建和销毁，达到了Bean对象的复用，性能更高</li></ul></li><li><strong>Bean对象是单例的，是否会产生线程安全问题？</strong><ul><li>如果对象是有状态对象，即该对象有成员变量可以用来存储数据的，因为所有请求线程共用一个bean对象，所以会存在线程安全问题。</li><li>如果对象是无状态对象，即该对象没有成员变量没有进行数据存储的，因方法中的局部变量在方法调用完成后会被销毁，所以不会存在线程安全问题。</li></ul></li><li><strong>哪些bean对象适合交给容器进行管理?</strong><ul><li>表现层对象（controller）</li><li>业务层对象（service）</li><li>数据层对象（dao）</li><li>工具对象（util）</li></ul></li><li><strong>哪些bean对象不适合交给容器进行管理?</strong><ul><li><code>封装实例的域对象（domain，pojo）</code>，因为会引发线程安全问题，所以不适合。<h2 id="Bean的实例化"><a href="#Bean的实例化" class="headerlink" title="Bean的实例化"></a>Bean的实例化</h2></li></ul></li><li>我们了解了通过配置文件来实例化Bean对象，但是容器是如何为我们创建对象的呢？<ul><li>这需要研究一下<code>Bean的实例化过程</code>，这部分主要解决两部分内容<ul><li>Bean是如何创建的</li><li>实例化Bean的三种方式，构造方法，静态工厂和实例工厂<div class="note warning no-icon flat"><p>需要明确的事，Bean本质上就是一个对象，对象在new的时候是使用构造方法来创建的，所以创建Bean也是使用构造方法完成的</p></div><h3 id="构造方法实例化"><a href="#构造方法实例化" class="headerlink" title="构造方法实例化"></a>构造方法实例化</h3></li></ul></li></ul></li><li>在之前的Dao实现类中为无参构造添加一句话，方便观察结果。<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><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">public</span> <span class="keyword">class</span> <span class="title class_">BookDaoImpl</span> <span class="keyword">implements</span> <span class="title class_">BookDao</span> &#123;</span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">save</span><span class="params">()</span> &#123;</span><br><span class="line">        System.out.println(<span class="string">&quot;book dao save ...&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="title function_">BookDaoImpl</span><span class="params">()</span> &#123;</span><br><span class="line">        System.out.println(<span class="string">&quot;book dao constructor is running ...&quot;</span>);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>输出结果：<blockquote><p>book dao constructor is running …<br>com.blog.service.impl.BookServiceImpl@25bbe1b6</p></blockquote></li></ul><ul><li>将构造器私有化后，继续进行测试<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><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">public</span> <span class="keyword">class</span> <span class="title class_">BookDaoImpl</span> <span class="keyword">implements</span> <span class="title class_">BookDao</span> &#123;</span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">save</span><span class="params">()</span> &#123;</span><br><span class="line">        System.out.println(<span class="string">&quot;book dao save ...&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="title function_">BookDaoImpl</span><span class="params">()</span> &#123;</span><br><span class="line">        System.out.println(<span class="string">&quot;book dao constructor is running ...&quot;</span>);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></li></ul><p>运行程序，发现将构造器私有化，仍然能访问到构造方法来实例化Bean，这说明Spring底层通过反射调用构造方法来实例化Bean对象</p><blockquote><p>book dao constructor is running …<br>com.blog.service.impl.BookServiceImpl@25bbe1b6</p></blockquote><h3 id="静态工厂实例化"><a href="#静态工厂实例化" class="headerlink" title="静态工厂实例化"></a>静态工厂实例化</h3><ul><li><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></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">BookDaoFactory</span> &#123;</span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">static</span> BookDao <span class="title function_">getBookDao</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_">BookDaoImpl</span>();</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></li><li><p>修改App运行类，通过工厂来获得对象</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="keyword">public</span> <span class="keyword">class</span> <span class="title class_">App</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">BookDao</span> <span class="variable">bookDao</span> <span class="operator">=</span> BookDaoFactory.getBookDaoImpl();</span><br><span class="line">        bookDao.save();</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>运行结果：</p><blockquote><p>book dao save …</p></blockquote></li><li><p>这样只是通过静态工厂创建对象，但没有将对象交给Spring容器进行管理，如何交给Spring容器管理呢？</p></li></ul><p>具体实现步骤如下：</p><ol><li>在Spring配置文件中修改BookDao的Bean<figure class="highlight xml"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="tag">&lt;<span class="name">bean</span> <span class="attr">id</span>=<span class="string">&quot;bookDao&quot;</span> <span class="attr">class</span>=<span class="string">&quot;com.blog.factory.BookDaoFactory&quot;</span> <span class="attr">factory-method</span>=<span class="string">&quot;getBookDaoImpl&quot;</span>/&gt;</span></span><br></pre></td></tr></table></figure><blockquote><p>将class改为工厂类的全类名<br>添加factory-method属性，指定工厂创建类的方法名</p></blockquote></li><li>在App运行类，像之前一样从IOC容器中获得对象<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><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="keyword">public</span> <span class="keyword">class</span> <span class="title class_">App</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">ApplicationContext</span> <span class="variable">context</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">ClassPathXmlApplicationContext</span>(<span class="string">&quot;applicationContext.xml&quot;</span>);</span><br><span class="line">        <span class="type">BookDao</span> <span class="variable">bookDao</span> <span class="operator">=</span> (BookDao) context.getBean(<span class="string">&quot;bookDao&quot;</span>);</span><br><span class="line">        bookDao.save();</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></li><li>这样就能正常运行了，运行结果和之前一样，反而更加麻烦了，哪这样做的意义是什么呢？<blockquote><p>book dao save …</p></blockquote></li><li>在工厂的静态方法中，除了创建对象我们还可以在这里进行其他的业务操作<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><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="keyword">public</span> <span class="keyword">class</span> <span class="title class_">BookDaoFactory</span> &#123;</span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">static</span> BookDao <span class="title function_">getBookDaoImpl</span><span class="params">()</span> &#123;</span><br><span class="line">        System.out.println(<span class="string">&quot;book dao factory setup ...&quot;</span>);<span class="comment">//模拟必要的业务操作</span></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_">BookDaoImpl</span>();</span><br><span class="line">    &#125;</span><br><span class="line">&#125; </span><br></pre></td></tr></table></figure></li></ol><div class="note warning no-icon flat"><p>这是静态工厂实例化，这种方式一般用来兼容早期老系统，目前使用较少，<code>了解为主</code></p></div><h3 id="实例工厂和FactoryBean"><a href="#实例工厂和FactoryBean" class="headerlink" title="实例工厂和FactoryBean"></a>实例工厂和FactoryBean</h3><p>修改BookDaoFactory类,这里和静态工厂的工厂类不同的是此处不是静态方法，而是普通方法<br><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><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_">BookDaoFactory</span> &#123;</span><br><span class="line">    <span class="comment">//唯一的区别就是去掉的static</span></span><br><span class="line">    <span class="keyword">public</span> BookDao <span class="title function_">getBookDaoImpl</span><span class="params">()</span> &#123;</span><br><span class="line">        System.out.println(<span class="string">&quot;book dao factory setup ...&quot;</span>);<span class="comment">//模拟必要的业务操作</span></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_">BookDaoImpl</span>();</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><br>修改App运行类，通过工厂来获得对象<br><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><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_">App</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">BookDaoFactory</span> <span class="variable">bookDaoFactory</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">BookDaoFactory</span>();</span><br><span class="line">        <span class="type">BookDao</span> <span class="variable">bookDao</span> <span class="operator">=</span> bookDaoFactory.getBookDaoImpl();</span><br><span class="line">        bookDao.save();</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><br>运行结果：</p><blockquote><p>book dao factory setup … 这个是模拟业务逻辑的输出，无视掉就行<br>book dao save …</p></blockquote><p>将实例工厂交给Spring管理：</p><ol><li>在Spring配置文件中修改bookDao的bean<figure class="highlight xml"><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="tag">&lt;<span class="name">bean</span> <span class="attr">id</span>=<span class="string">&quot;bookDaoFactory&quot;</span> <span class="attr">class</span>=<span class="string">&quot;com.blog.factory.BookDaoFactory&quot;</span>/&gt;</span></span><br><span class="line"><span class="tag">&lt;<span class="name">bean</span> <span class="attr">id</span>=<span class="string">&quot;bookDao&quot;</span> <span class="attr">factory-bean</span>=<span class="string">&quot;bookDaoFactory&quot;</span> <span class="attr">factory-method</span>=<span class="string">&quot;getBookDaoImpl&quot;</span>/&gt;</span></span><br></pre></td></tr></table></figure><div class="note info no-icon flat"><p>实例化工厂运行的顺序是:</p><ul><li>创建实例化工厂对象,对应的是第一行配置</li><li>调用对象中的方法来创建bean，对应的是第二行配置<ul><li>factory-bean:工厂的实例对象</li><li>factory-method:工厂对象中的具体创建对象的方法名</li></ul></li></ul></div></li></ol><ul><li>配置后便可以在App中从Ioc容器中获取Bean对象<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><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="keyword">public</span> <span class="keyword">class</span> <span class="title class_">App</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">ApplicationContext</span> <span class="variable">context</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">ClassPathXmlApplicationContext</span>(<span class="string">&quot;applicationContext.xml&quot;</span>);</span><br><span class="line">        <span class="type">BookDao</span> <span class="variable">bookDao</span> <span class="operator">=</span> (BookDao) context.getBean(<span class="string">&quot;bookDao&quot;</span>);</span><br><span class="line">        bookDao.save();</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></li></ul><div class="note warning no-icon flat"><p>实例工厂的配置过程还是比较复杂的，且耦合度较高，所以Spring为了简化这种配置方式，提供了一种叫<code>FactoryBean</code>的接口来简化开发。</p></div><p><code>FactoryBean</code>具体使用步骤：</p><ol><li>创建一个BookDaoFactoryBean类，实现FactoryBean接口，并重写其中的方法<br>该接口是一个泛型接口，泛型参数为该工厂创建的Bean对象的类型<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><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_">BookDaoFactoryBean</span> <span class="keyword">implements</span> <span class="title class_">FactoryBean</span>&lt;BookDao&gt; &#123;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">public</span> BookDao <span class="title function_">getObject</span><span class="params">()</span> <span class="keyword">throws</span> Exception &#123;</span><br><span class="line">        <span class="keyword">return</span> <span class="keyword">new</span> <span class="title class_">BookDaoImpl</span>();</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">public</span> Class&lt;?&gt; getObjectType() &#123;</span><br><span class="line">        <span class="keyword">return</span> BookDao.class;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></li><li>在Spring的配置文件修改bookDao的bean，修改class属性为工厂的全类名<figure class="highlight xml"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="tag">&lt;<span class="name">bean</span> <span class="attr">id</span>=<span class="string">&quot;bookDao&quot;</span> <span class="attr">class</span>=<span class="string">&quot;com.blog.factory.BookDaoFactoryBean&quot;</span>&gt;</span><span class="tag">&lt;/<span class="name">bean</span>&gt;</span></span><br></pre></td></tr></table></figure></li><li>App运行类不用做任何修改，直接运行，结果如下<blockquote><p>book dao save …</p></blockquote></li></ol><div class="note danger no-icon flat"><p>这种方式在Spring整合其他框架时会用到，所以需要理解并掌握。</p></div><ul><li>查看源码会发现，FactoryBean接口提供了三个方法<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><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">// 该方法用于创建Bean对象并返回</span></span><br><span class="line">T <span class="title function_">getObject</span><span class="params">()</span> <span class="keyword">throws</span> Exception;</span><br><span class="line"></span><br><span class="line"><span class="comment">// 该方法用于返回Bean对象的类型</span></span><br><span class="line">Class&lt;?&gt; getObjectType();</span><br><span class="line"></span><br><span class="line"><span class="comment">// 该方法用于设置Bean对象是否为单例，默认为单例</span></span><br><span class="line"><span class="keyword">default</span> <span class="type">boolean</span> <span class="title function_">isSingleton</span><span class="params">()</span> &#123;</span><br><span class="line">    <span class="keyword">return</span> <span class="literal">true</span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></li><li>其中方法一和方法二必须重写，方法三因为有默认值，所以可以不重写</li></ul><h3 id="Bean的实例化小结"><a href="#Bean的实例化小结" class="headerlink" title="Bean的实例化小结"></a>Bean的实例化小结</h3><ul><li>Bean如何被创建<ul><li>Spring通过反射机制调用构造方法，因此即使构造方法为私有的，Spring依旧能正常创建Bean</li></ul></li><li>Spring的Ioc实例化对象的三种方式：<ul><li>无参构造方法(常用)</li><li>静态工厂(了解)</li><li>实例工厂(了解)<ul><li>FactoryBean(实用)</li></ul></li></ul></li></ul><div class="note warning no-icon flat"><p>无参构造方法系统默认会提供，但一旦重写了构造方法，系统就不会再提供默认的无参构造方法，因此在使用时需要注意，如果重写了构造方法，一定要把无参构造方法也写出来。</p></div><h2 id="Bean的生命周期"><a href="#Bean的生命周期" class="headerlink" title="Bean的生命周期"></a>Bean的生命周期</h2><p>对于Bean的生命周期，主要围绕<code>Bean生命周期控制</code>来学习</p><ul><li>什么是Bean的生命周期？<ul><li>Bean对象从创建到销毁的整个过程</li></ul></li><li>Bean生命周期控制是什么？<ul><li>Bean创建后到销毁前做的事情</li></ul></li><li>我们研究的是如何在Bean创建之后和销毁之前将我们需要执行的操作添加进去</li></ul><h3 id="生命周期设置"><a href="#生命周期设置" class="headerlink" title="生命周期设置"></a>生命周期设置</h3><p>具体的两个控制阶段：</p><ul><li>Bean创建之后，如果想要添加内容，比如用来初始化需要用到的资源</li><li>Bean销毁之前，如果想要添加内容，比如用来释放资源</li></ul><ol><li><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></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">BookDaoImpl</span> <span class="keyword">implements</span> <span class="title class_">BookDao</span> &#123;</span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">save</span><span class="params">()</span> &#123;</span><br><span class="line">        System.out.println(<span class="string">&quot;book dao save ...&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_">init</span><span class="params">()</span> &#123;</span><br><span class="line">        System.out.println(<span class="string">&quot;init ... &quot;</span>);</span><br><span class="line">    &#125;</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;destroy ... &quot;</span>);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></li><li><p>在配置文件中添加初始化和销毁方法</p><figure class="highlight xml"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="tag">&lt;<span class="name">bean</span> <span class="attr">id</span>=<span class="string">&quot;bookDao&quot;</span> <span class="attr">class</span>=<span class="string">&quot;com.blog.dao.impl.BookDaoImpl&quot;</span> <span class="attr">init-method</span>=<span class="string">&quot;init&quot;</span> <span class="attr">destroy-method</span>=<span class="string">&quot;destroy&quot;</span>&gt;</span><span class="tag">&lt;/<span class="name">bean</span>&gt;</span></span><br></pre></td></tr></table></figure><div class="note info no-icon flat"><p>init-method：指定初始化方法<br>destroy-method：指定销毁方法</p></div></li><li><p>运行程序</p><blockquote><p>init …<br>book dao save …</p></blockquote></li></ol><div class="note warning no-icon flat"><p><strong>从运行结果来看，init已经成功执行，但destroy方法没有执行，这是为什么？</strong></p><ul><li>因为Spring的IoC容器运行在JVM中，运行main方法后，开始启动JVM，Spring加载配置文件生成IOC容器，从容器获取bean对象，然后调用方法。main方法执行结束后，JVM就退出了，但此时IOC中的Bean还没销毁，所以就还没调用对应的destroy方法</li></ul></div><p>知道出现问题的原因，该如何解决这个问题呢？接着往下看</p><h3 id="Close关闭容器"><a href="#Close关闭容器" class="headerlink" title="Close关闭容器"></a>Close关闭容器</h3><ul><li>ApplicationContext中没有close方法，他的子类提供了close方法，所以需要将ApplicationContext更换为ClassPathXmlApplicationContext，然后调用<code>close()</code>方法关闭容器<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><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">public</span> <span class="keyword">class</span> <span class="title class_">App</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">//ApplicationContext context = new ClassPathXmlApplicationContext(&quot;applicationContext.xml&quot;);</span></span><br><span class="line">        <span class="type">ClassPathXmlApplicationContext</span> <span class="variable">context</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">ClassPathXmlApplicationContext</span>(<span class="string">&quot;applicationContext.xml&quot;</span>);</span><br><span class="line">        <span class="type">BookDao</span> <span class="variable">bookDao</span> <span class="operator">=</span> (BookDao) context.getBean(<span class="string">&quot;bookDao&quot;</span>);</span><br><span class="line">        bookDao.save();</span><br><span class="line">        context.close();</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></li><li>此时运行程序就能看到destroy正常输出<blockquote><p>init …<br>book dao save …<br>destroy …</p></blockquote></li></ul><h3 id="注册钩子关闭容器"><a href="#注册钩子关闭容器" class="headerlink" title="注册钩子关闭容器"></a>注册钩子关闭容器</h3><ul><li>在容器未关闭之前，提前设置好回调函数，让JVM在退出之前回调此函数来关闭容器</li><li>调用context的<code>registerShutdownHook()</code>方法<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><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_">App</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">ClassPathXmlApplicationContext</span> <span class="variable">context</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">ClassPathXmlApplicationContext</span>(<span class="string">&quot;applicationContext.xml&quot;</span>);</span><br><span class="line">        <span class="type">BookDao</span> <span class="variable">bookDao</span> <span class="operator">=</span> (BookDao) context.getBean(<span class="string">&quot;bookDao&quot;</span>);</span><br><span class="line">        bookDao.save();</span><br><span class="line">        context.registerShutdownHook();</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><div class="note warning no-icon flat"><p>注意：registerShutdownHook和close方法一样，在ApplicationContext中也没有</p></div></li><li><p>运行后，查询打印结果</p><blockquote><p>init …<br>book dao save …<br>destroy …</p></blockquote></li><li><p><strong>close和registerShutdownHook该选哪个?</strong></p><ul><li><strong>相同点:</strong> 这两种都能用来关闭容器</li><li><strong>不同点:</strong> close()是在调用的时候关闭容器，registerShutdownHook()是在JVM退出前调用关闭容器。</li><li>那么registerShutdownHook()方法可以在任意位置调用，下面的代码中将其放在了第二行，仍能正常输出，但要是将其换成close()方法，则会报错BeanFactory not initialized or already closedclosed<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><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_">App</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">ClassPathXmlApplicationContext</span> <span class="variable">context</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">ClassPathXmlApplicationContext</span>(<span class="string">&quot;applicationContext.xml&quot;</span>);</span><br><span class="line">        context.registerShutdownHook();</span><br><span class="line">        <span class="type">BookDao</span> <span class="variable">bookDao</span> <span class="operator">=</span> (BookDao) context.getBean(<span class="string">&quot;bookDao&quot;</span>);</span><br><span class="line">        bookDao.save();</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></li></ul></li><li><strong>开发中用哪个？</strong><ul><li>答案是两个都不用</li><li>该方式添加初始化和销毁方法即需要更改配置文件还需要编码，实现步骤较复杂，不建议使用</li><li>Spring提供了两个接口来实现生命周期的控制，好处是不需要再配置<code>init-method</code>和<code>destroy-method</code></li></ul></li></ul><h3 id="Spring提供的接口"><a href="#Spring提供的接口" class="headerlink" title="Spring提供的接口"></a>Spring提供的接口</h3><div class="note info no-icon flat"><p>需求：修改BookDaoImpl类，完成初始化和销毁方法的添加</p></div><div class="tabs" id="配置生命周期"><ul class="nav-tabs"><li class="tab active"><button type="button" data-href="#配置生命周期-1">实现接口</button></li><li class="tab"><button type="button" data-href="#配置生命周期-2">Bean配置</button></li><li class="tab"><button type="button" data-href="#配置生命周期-3">运行结果</button></li></ul><div class="tab-contents"><div class="tab-item-content active" id="配置生命周期-1"><p>修改BookServiceImpl类，添加两个接口<code>InitializingBean</code>,<code>DisposableBean</code>并实现接口中的两个方法<code>afterPropertiesSet</code>和<code>destroy</code><br><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><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_">BookServiceImpl</span> <span class="keyword">implements</span> <span class="title class_">BookService</span>, InitializingBean, DisposableBean &#123;</span><br><span class="line">    <span class="keyword">private</span> BookDao bookDao;</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">()</span> &#123;</span><br><span class="line">        System.out.println(<span class="string">&quot;book service save ...&quot;</span>);</span><br><span class="line">        bookDao.save();</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_">setBookDao</span><span class="params">(BookDao bookDao)</span> &#123;</span><br><span class="line">        System.out.println(<span class="string">&quot;set ... &quot;</span>);</span><br><span class="line">        <span class="built_in">this</span>.bookDao = bookDao;</span><br><span class="line">    &#125;</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_">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;service destroy ... &quot;</span>);</span><br><span class="line">    &#125;</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_">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;service init ... &quot;</span>);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></p><button type="button" class="tab-to-top" aria-label="scroll to top"><i class="fas fa-arrow-up"></i></button></div><div class="tab-item-content" id="配置生命周期-2"><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="tag">&lt;<span class="name">bean</span> <span class="attr">id</span>=<span class="string">&quot;bookService&quot;</span> <span class="attr">class</span>=<span class="string">&quot;com.blog.service.impl.BookServiceImpl&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;bookDao&quot;</span> <span class="attr">ref</span>=<span class="string">&quot;bookDao&quot;</span>&gt;</span><span class="tag">&lt;/<span class="name">property</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;/<span class="name">bean</span>&gt;</span></span><br></pre></td></tr></table></figure><button type="button" class="tab-to-top" aria-label="scroll to top"><i class="fas fa-arrow-up"></i></button></div><div class="tab-item-content" id="配置生命周期-3"><ul><li>init 是BookDao的初始化方法输出的</li><li>service init 是BookServiceImpl的初始化方法输出的</li><li>book dao save …是BookDao的保存方法输出的</li><li>service destroy 是BookServiceImpl的销毁方法输出的</li><li>destroy 是BookDao的销毁方法输出的</li></ul><blockquote><p>init …<br>service init …<br>book dao save …<br>service destroy …<br>destroy …</p></blockquote><button type="button" class="tab-to-top" aria-label="scroll to top"><i class="fas fa-arrow-up"></i></button></div></div></div><ul><li>小细节<ul><li>对于InitializingBean接口中的afterPropertiesSet方法，翻译过来为属性设置之后。</li><li>对于BookServiceImpl来说，bookDao是它的一个属性，所以会先注入bookDao属性再初始化BookServiceImpl</li></ul></li></ul><div class="note info no-icon flat"><p>没有调用bookService为什么输出结果中还会有service呢？<br>解惑：Ioc容器中所有的单例Bean的初始化和销毁都会在容器启动和关闭时运行，所以Service也会运行</p></div><h2 id="IOC相关内容小结"><a href="#IOC相关内容小结" class="headerlink" title="IOC相关内容小结"></a>IOC相关内容小结</h2><ul><li>Bean的基础配置<ul><li><code>&lt;id&gt;</code>标签和<code>&lt;class&gt;</code>标签必须添加且不能为空，<code>&lt;id&gt;</code>标签的值不能重复,<code>&lt;class&gt;</code>标签的值可以重复</li><li><code>&lt;name&gt;</code>标签可以用来取别名，多个别名时使用逗号，分号或空格进行分隔</li><li><code>&lt;scope&gt;</code>标签用来设置Bean的作用范围，默认值为单例，即获取同一个<code>&lt;id&gt;</code>的Bean对象，获取的对象都是同一个（即使类路径相同只有id不同，这两个也是独立的Bean对象）</li></ul></li><li>Bean的实例化<ul><li>Bean对象是Spring通过暴力反射为我们创建的，我们如果重写了构造方法，一定要把无参构造方法也写出来。</li><li>Spring的Ioc实例化对象的三种方式：<ul><li>无参构造方法(常用)</li><li>静态工厂(了解)</li><li>实例工厂(了解)<ul><li>FactoryBean(实用)</li></ul></li></ul></li></ul></li><li>Bean的生命周期<ul><li>Spring为Bean的生命周期控制提供了两种方式：<ul><li>配置文件中添加<code>init-method</code>和<code>destroy-method</code>属性</li><li>类实现<code>InitializingBean</code>和<code>DisposableBean</code>接口</li></ul></li><li>Bean的生命周期过程：<ul><li>初始化容器<ol><li>创建对象</li><li>执行构造方法</li><li>执行属性注入（set …）</li><li>执行Bean的初始化方法（service init …）</li></ol></li><li>使用Bean<ul><li>执行Bean的业务方法（book dao save …）</li></ul></li><li>销毁容器<ul><li>执行Bean的销毁方法（service destroy …）</li></ul></li></ul></li><li>关闭容器的两种方法：<ul><li>通过配置文件添加初始化和销毁方法需要设置关闭容器，否则<code>destroy()</code>方法不会正常执行</li><li>ConfigurableApplicationContext是ApplicationContext的子类，子类才有下面两种方法：<ul><li><code>close()</code></li><li><code>registerShutdownHook()</code></li></ul></li></ul></li></ul></li></ul><h1 id="DI相关内容"><a href="#DI相关内容" class="headerlink" title="DI相关内容"></a>DI相关内容</h1><p><code>DI(依赖注入)</code>通常用来为对象添加属性</p><ul><li><strong>向类中传递数据的方式有哪些？</strong><ul><li>构造方法</li><li>Set方法</li></ul></li><li><strong>Spring为我们提供了两种属性注入方式：</strong><ul><li><code>构造器注入</code><ul><li>简单类型</li><li>引用类型</li></ul></li><li><code>Setter方法注入</code><ul><li>简单类型</li><li>引用类型</li></ul></li></ul></li></ul><h2 id="Set注入"><a href="#Set注入" class="headerlink" title="Set注入"></a>Set注入</h2><ul><li>前面就用到了set注入，快速回顾一下</li><li>在Bean中定义引用类型的属性，并提供set方法<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><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="keyword">public</span> <span class="keyword">class</span> <span class="title class_">BookServiceImpl</span> <span class="keyword">implements</span> <span class="title class_">BookService</span> &#123;</span><br><span class="line">    <span class="keyword">private</span> BookDao bookDao;</span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">setBookDao</span><span class="params">(BookDao bookDao)</span> &#123;</span><br><span class="line">        <span class="built_in">this</span>.bookDao = bookDao;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></li><li>在配置文件中使用property标签为属性赋值<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="tag">&lt;<span class="name">bean</span> <span class="attr">id</span>=<span class="string">&quot;bookService&quot;</span> <span class="attr">class</span>=<span class="string">&quot;com.blog.service.impl.BookServiceImpl&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;bookDao&quot;</span> <span class="attr">ref</span>=<span class="string">&quot;bookDao&quot;</span>&gt;</span><span class="tag">&lt;/<span class="name">property</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;/<span class="name">bean</span>&gt;</span></span><br></pre></td></tr></table></figure><div class="note info no-icon flat"><p>回顾两个bookDao的含义：</p><ul><li>name后的作用是让Spring的Ioc容器获取名称后，根据获取到的名称推测对应的set方法，使用此set方法为属性赋值</li><li>ref后的作用是让Spring在Ioc容器找到id为bookDao的Bean，并赋给bookDao属性</li></ul></div></li></ul><h3 id="注入引用数据类型"><a href="#注入引用数据类型" class="headerlink" title="注入引用数据类型"></a>注入引用数据类型</h3><div class="note info no-icon flat"><p>需求：使用依赖注入为BookServiceImpl类中的BookDao属性赋值</p></div><div class="tabs" id="依赖注入1"><ul class="nav-tabs"><li class="tab active"><button type="button" data-href="#依赖注入1-1">BookDao</button></li><li class="tab"><button type="button" data-href="#依赖注入1-2">BookDaoImpl</button></li><li class="tab"><button type="button" data-href="#依赖注入1-3">BookService</button></li><li class="tab"><button type="button" data-href="#依赖注入1-4">BookServiceImpl</button></li></ul><div class="tab-contents"><div class="tab-item-content active" id="依赖注入1-1"><figure class="highlight java"><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="keyword">public</span> <span class="keyword">interface</span> <span class="title class_">BookDao</span> &#123;</span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">save</span><span class="params">()</span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><button type="button" class="tab-to-top" aria-label="scroll to top"><i class="fas fa-arrow-up"></i></button></div><div class="tab-item-content" id="依赖注入1-2"><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><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_">BookDaoImpl</span> <span class="keyword">implements</span> <span class="title class_">BookDao</span> &#123;</span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">save</span><span class="params">()</span> &#123;</span><br><span class="line">        System.out.println(<span class="string">&quot;book dao save ...&quot;</span>);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><button type="button" class="tab-to-top" aria-label="scroll to top"><i class="fas fa-arrow-up"></i></button></div><div class="tab-item-content" id="依赖注入1-3"><figure class="highlight java"><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="keyword">public</span> <span class="keyword">interface</span> <span class="title class_">BookService</span> &#123;</span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">save</span><span class="params">()</span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><button type="button" class="tab-to-top" aria-label="scroll to top"><i class="fas fa-arrow-up"></i></button></div><div class="tab-item-content" id="依赖注入1-4"><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><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_">BookServiceImpl</span> <span class="keyword">implements</span> <span class="title class_">BookService</span> &#123;</span><br><span class="line">    <span class="keyword">private</span> BookDao bookDao;</span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">setBookDao</span><span class="params">(BookDao bookDao)</span> &#123;</span><br><span class="line">        <span class="built_in">this</span>.bookDao = bookDao;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">save</span><span class="params">()</span> &#123;</span><br><span class="line">        System.out.println(<span class="string">&quot;book service save ...&quot;</span>);</span><br><span class="line">        bookDao.save();</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><button type="button" class="tab-to-top" aria-label="scroll to top"><i class="fas fa-arrow-up"></i></button></div></div></div><ul><li>配置文件如下：<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></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">beans</span> <span class="attr">xmlns</span>=<span class="string">&quot;http://www.springframework.org/schema/beans&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://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd&quot;</span>&gt;</span></span><br><span class="line"></span><br><span class="line">    <span class="tag">&lt;<span class="name">bean</span> <span class="attr">id</span>=<span class="string">&quot;bookDao&quot;</span> <span class="attr">class</span>=<span class="string">&quot;com.blog.dao.impl.BookDaoImpl&quot;</span>&gt;</span><span class="tag">&lt;/<span class="name">bean</span>&gt;</span></span><br><span class="line"></span><br><span class="line">    <span class="tag">&lt;<span class="name">bean</span> <span class="attr">id</span>=<span class="string">&quot;bookService&quot;</span> <span class="attr">class</span>=<span class="string">&quot;com.blog.service.impl.BookServiceImpl&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;bookDao&quot;</span> <span class="attr">ref</span>=<span class="string">&quot;bookDao&quot;</span>&gt;</span><span class="tag">&lt;/<span class="name">property</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;/<span class="name">bean</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;/<span class="name">beans</span>&gt;</span></span><br></pre></td></tr></table></figure></li><li>运行App类，加载Ioc容器并获取service对象<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><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="keyword">public</span> <span class="keyword">class</span> <span class="title class_">App</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">ClassPathXmlApplicationContext</span> <span class="variable">context</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">ClassPathXmlApplicationContext</span>(<span class="string">&quot;applicationContext.xml&quot;</span>);</span><br><span class="line">        <span class="type">BookService</span> <span class="variable">bookService</span> <span class="operator">=</span> (BookService) context.getBean(<span class="string">&quot;bookService&quot;</span>);</span><br><span class="line">        bookService.save();</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="note info no-icon flat"><p>需求：为BookDaoImpl注入一些简单数据。</p></div><strong>思考：</strong></li><li>引用类型使用的是<code>&lt;property name=&quot;&quot; ref=&quot;&quot;/&gt;</code>,简单数据类型还是使用ref吗?</li><li><p>ref是指向Spring的IOC容器中的另一个bean对象的，对于简单数据类型，没有对应的bean对象，该如何配置呢?</p><ul><li>使用value来配置<code>&lt;property name=&quot;&quot; value=&quot;&quot;/&gt;</code></li></ul></li><li><p><code>步骤一：</code>定义属性并提供set方法</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_">BookDaoImpl</span> <span class="keyword">implements</span> <span class="title class_">BookDao</span> &#123;</span><br><span class="line">    <span class="keyword">private</span> String dataBaseName;</span><br><span class="line">    <span class="keyword">private</span> <span class="type">int</span> connectionCount;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">setDataBaseName</span><span class="params">(String dataBaseName)</span> &#123;</span><br><span class="line">        <span class="built_in">this</span>.dataBaseName = dataBaseName;</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_">setConnectionCount</span><span class="params">(<span class="type">int</span> connectionCount)</span> &#123;</span><br><span class="line">        <span class="built_in">this</span>.connectionCount = connectionCount;</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_">save</span><span class="params">()</span> &#123;</span><br><span class="line">        System.out.println(<span class="string">&quot;book dao save ...&quot;</span> + dataBaseName + <span class="string">&quot;,&quot;</span> + connectionCount);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></li><li><p><code>步骤二：</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></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">beans</span> <span class="attr">xmlns</span>=<span class="string">&quot;http://www.springframework.org/schema/beans&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://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd&quot;</span>&gt;</span></span><br><span class="line"></span><br><span class="line">    <span class="tag">&lt;<span class="name">bean</span> <span class="attr">id</span>=<span class="string">&quot;bookDao&quot;</span> <span class="attr">class</span>=<span class="string">&quot;com.blog.dao.impl.BookDaoImpl&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;dataBaseName&quot;</span> <span class="attr">value</span>=<span class="string">&quot;mysql&quot;</span>&gt;</span><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;connectionCount&quot;</span> <span class="attr">value</span>=<span class="string">&quot;100&quot;</span>&gt;</span><span class="tag">&lt;/<span class="name">property</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;/<span class="name">bean</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;/<span class="name">beans</span>&gt;</span></span><br></pre></td></tr></table></figure><div class="note warning no-icon flat"><p>value:后面跟的是简单数据类型，对于参数类型，Spring在注入的时候会自动转换，但是不能写一个错误的类型，例如<code>connectionCount</code>是<code>int</code>类型，你却给他传一个abc，这样的话，spring在将abc转换成int类型的时候就会报错。</p></div></li><li>步骤三：运行程序，查看结果<blockquote><p>book dao save …mysql,100</p></blockquote></li></ul><h2 id="构造器注入"><a href="#构造器注入" class="headerlink" title="构造器注入"></a>构造器注入</h2><h3 id="环境准备"><a href="#环境准备" class="headerlink" title="环境准备"></a>环境准备</h3><ul><li><p>修改BookDao、BookDaoImpl、UserDao、UserDaoImpl、BookService和BookServiceImpl类</p><div class="tabs" id="构造器注入"><ul class="nav-tabs"><li class="tab active"><button type="button" data-href="#构造器注入-1">BookDao</button></li><li class="tab"><button type="button" data-href="#构造器注入-2">UserDao</button></li><li class="tab"><button type="button" data-href="#构造器注入-3">UserDaoImpl</button></li><li class="tab"><button type="button" data-href="#构造器注入-4">BookService</button></li><li class="tab"><button type="button" data-href="#构造器注入-5">BookServiceImpl</button></li></ul><div class="tab-contents"><div class="tab-item-content active" id="构造器注入-1"><figure class="highlight java"><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="keyword">public</span> <span class="keyword">interface</span> <span class="title class_">BookDao</span> &#123;</span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">save</span><span class="params">()</span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><!-- enatab --><!-- tab BookDaoImpl --><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><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">public</span> <span class="keyword">class</span> <span class="title class_">BookDaoImpl</span> <span class="keyword">implements</span> <span class="title class_">BookDao</span> &#123;</span><br><span class="line">    </span><br><span class="line">    <span class="keyword">private</span> String databaseName;</span><br><span class="line">    <span class="keyword">private</span> <span class="type">int</span> connectionNum;</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">()</span> &#123;</span><br><span class="line">        System.out.println(<span class="string">&quot;book dao save ...&quot;</span>);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><button type="button" class="tab-to-top" aria-label="scroll to top"><i class="fas fa-arrow-up"></i></button></div><div class="tab-item-content" id="构造器注入-2"><figure class="highlight java"><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="keyword">public</span> <span class="keyword">interface</span> <span class="title class_">UserDao</span> &#123;</span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">save</span><span class="params">()</span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><button type="button" class="tab-to-top" aria-label="scroll to top"><i class="fas fa-arrow-up"></i></button></div><div class="tab-item-content" id="构造器注入-3"><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><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_">UserDaoImpl</span> <span class="keyword">implements</span> <span class="title class_">UserDao</span> &#123;</span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">save</span><span class="params">()</span> &#123;</span><br><span class="line">        System.out.println(<span class="string">&quot;user dao save ...&quot;</span>);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><button type="button" class="tab-to-top" aria-label="scroll to top"><i class="fas fa-arrow-up"></i></button></div><div class="tab-item-content" id="构造器注入-4"><figure class="highlight java"><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="keyword">public</span> <span class="keyword">interface</span> <span class="title class_">BookService</span> &#123;</span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">save</span><span class="params">()</span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><button type="button" class="tab-to-top" aria-label="scroll to top"><i class="fas fa-arrow-up"></i></button></div><div class="tab-item-content" id="构造器注入-5"><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><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_">BookServiceImpl</span> <span class="keyword">implements</span> <span class="title class_">BookService</span>&#123;</span><br><span class="line">    <span class="keyword">private</span> BookDao bookDao;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">setBookDao</span><span class="params">(BookDao bookDao)</span> &#123;</span><br><span class="line">        <span class="built_in">this</span>.bookDao = bookDao;</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_">save</span><span class="params">()</span> &#123;</span><br><span class="line">        System.out.println(<span class="string">&quot;book service save ...&quot;</span>);</span><br><span class="line">        bookDao.save();</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><button type="button" class="tab-to-top" aria-label="scroll to top"><i class="fas fa-arrow-up"></i></button></div></div></div></li><li><p>配置文件：</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="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">beans</span> <span class="attr">xmlns</span>=<span class="string">&quot;http://www.springframework.org/schema/beans&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://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd&quot;</span>&gt;</span></span><br><span class="line"></span><br><span class="line">    <span class="tag">&lt;<span class="name">bean</span> <span class="attr">id</span>=<span class="string">&quot;bookDao&quot;</span> <span class="attr">class</span>=<span class="string">&quot;com.blog.dao.impl.BookDaoImpl&quot;</span>/&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">bean</span> <span class="attr">id</span>=<span class="string">&quot;bookService&quot;</span> <span class="attr">class</span>=<span class="string">&quot;com.blog.service.impl.BookServiceImpl&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;bookDao&quot;</span> <span class="attr">ref</span>=<span class="string">&quot;bookDao&quot;</span>/&gt;</span></span><br><span class="line">    <span class="tag">&lt;/<span class="name">bean</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;/<span class="name">beans</span>&gt;</span></span><br></pre></td></tr></table></figure></li><li>运行类<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><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="keyword">public</span> <span class="keyword">class</span> <span class="title class_">App</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">ApplicationContext</span> <span class="variable">ctx</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">ClassPathXmlApplicationContext</span>(<span class="string">&quot;applicationContext.xml&quot;</span>);</span><br><span class="line">        <span class="type">BookService</span> <span class="variable">bookService</span> <span class="operator">=</span> (BookService) ctx.getBean(<span class="string">&quot;bookService&quot;</span>);</span><br><span class="line">        bookService.save();</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></li></ul><h3 id="构造器注入引用类型"><a href="#构造器注入引用类型" class="headerlink" title="构造器注入引用类型"></a>构造器注入引用类型</h3><div class="note info no-icon flat"><p>需求：将BookServiceImpl类中的bookDao修改成使用构造器的方式注入。</p><ol><li>将bookDao的setter方法删除掉</li><li>添加带有bookDao参数的构造方法</li><li>在applicationContext.xml中配置</li></ol></div><ul><li>步骤一：删除setter方法并提供构造方法<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><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_">BookServiceImpl</span> <span class="keyword">implements</span> <span class="title class_">BookService</span>&#123;</span><br><span class="line">    <span class="keyword">private</span> BookDao bookDao;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">public</span> <span class="title function_">BookServiceImpl</span><span class="params">(BookDao bookDao)</span> &#123;</span><br><span class="line">        <span class="built_in">this</span>.bookDao = bookDao;</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_">save</span><span class="params">()</span> &#123;</span><br><span class="line">        System.out.println(<span class="string">&quot;book service save ...&quot;</span>);</span><br><span class="line">        bookDao.save();</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></li><li>步骤二：配置文件中进行配置构造方式注入<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="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">beans</span> <span class="attr">xmlns</span>=<span class="string">&quot;http://www.springframework.org/schema/beans&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://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd&quot;</span>&gt;</span></span><br><span class="line"></span><br><span class="line">    <span class="tag">&lt;<span class="name">bean</span> <span class="attr">id</span>=<span class="string">&quot;bookDao&quot;</span> <span class="attr">class</span>=<span class="string">&quot;com.blog.dao.impl.BookDaoImpl&quot;</span>/&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">bean</span> <span class="attr">id</span>=<span class="string">&quot;bookService&quot;</span> <span class="attr">class</span>=<span class="string">&quot;com.blog.service.impl.BookServiceImpl&quot;</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">constructor-arg</span> <span class="attr">name</span>=<span class="string">&quot;bookDao&quot;</span> <span class="attr">ref</span>=<span class="string">&quot;bookDao&quot;</span>/&gt;</span></span><br><span class="line">    <span class="tag">&lt;/<span class="name">bean</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;/<span class="name">beans</span>&gt;</span></span><br></pre></td></tr></table></figure><div class="note info no-icon flat"><p>说明：在标签<code>&lt;constructor-arg&gt;</code>中</p><ul><li>name属性对应的值为构造函数中方法<code>形参的参数名</code>，必须要保持一致。</li><li>ref属性指向的是spring的IOC容器中其他bean对象。</li></ul></div></li><li>步骤三：运行程序，查看结果<br>运行结果：<blockquote><p>book service save …<br>book dao save …</p></blockquote></li></ul><h3 id="构造器注入多个参数"><a href="#构造器注入多个参数" class="headerlink" title="构造器注入多个参数"></a>构造器注入多个参数</h3><div class="note info no-icon flat"><p>需求:在<code>BookDaoImpl</code>中，使用构造函数注入<code>databaseName</code>和<code>connectionNum</code>两个参数。<br>参考引用数据类型的注入，我们可以推出具体的步骤为:</p><ol><li>提供一个包含这两个参数的构造方法</li><li>在applicationContext.xml中进行注入配置</li></ol></div><ul><li>步骤一：添加多个简单属性并提供构造方法<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><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">public</span> <span class="keyword">class</span> <span class="title class_">BookDaoImpl</span> <span class="keyword">implements</span> <span class="title class_">BookDao</span> &#123;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">private</span> String databaseName;</span><br><span class="line">    <span class="keyword">private</span> <span class="type">int</span> connectionNum;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">public</span> <span class="title function_">BookDaoImpl</span><span class="params">(String databaseName, <span class="type">int</span> connectionNum)</span> &#123;</span><br><span class="line">        <span class="built_in">this</span>.databaseName = databaseName;</span><br><span class="line">        <span class="built_in">this</span>.connectionNum = connectionNum;</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_">save</span><span class="params">()</span> &#123;</span><br><span class="line">        System.out.println(<span class="string">&quot;book dao save ...&quot;</span> + databaseName + <span class="string">&quot;,&quot;</span> + connectionNum);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></li><li>步骤二：修改该配置文件<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="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">beans</span> <span class="attr">xmlns</span>=<span class="string">&quot;http://www.springframework.org/schema/beans&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://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd&quot;</span>&gt;</span></span><br><span class="line"></span><br><span class="line">    <span class="tag">&lt;<span class="name">bean</span> <span class="attr">id</span>=<span class="string">&quot;bookDao&quot;</span> <span class="attr">class</span>=<span class="string">&quot;com.blog.dao.impl.BookDaoImpl&quot;</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">constructor-arg</span> <span class="attr">name</span>=<span class="string">&quot;databaseName&quot;</span> <span class="attr">value</span>=<span class="string">&quot;mysql&quot;</span>&gt;</span><span class="tag">&lt;/<span class="name">constructor-arg</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">constructor-arg</span> <span class="attr">name</span>=<span class="string">&quot;connectionNum&quot;</span> <span class="attr">value</span>=<span class="string">&quot;100&quot;</span>&gt;</span><span class="tag">&lt;/<span class="name">constructor-arg</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;/<span class="name">bean</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;/<span class="name">beans</span>&gt;</span></span><br></pre></td></tr></table></figure></li><li>步骤三：运行程序：<blockquote><p>book service save …<br>book dao save …mysql,100</p></blockquote></li></ul><h3 id="存在的问题"><a href="#存在的问题" class="headerlink" title="存在的问题"></a>存在的问题</h3><ul><li><code>&lt;constructor-arg&gt;</code>标签内的name，必须与构造函数中的参数名一致，这两块存在紧耦合。</li><li>解决该问题，需要提前说明一点，即这个参数名发生变化的情况并不多，所以上面的还是比较主流的配置方式，下面介绍的配置方式了解为主。</li><li><strong>方式一：删除name属性，添加type属性，根据类型注入</strong><ul><li>这种方式可以解决构造函数形参名发生变化带来的耦合问题</li><li>但是如果构造方法参数中有类型相同的参数，这种方式就不太好实现了<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">bean</span> <span class="attr">id</span>=<span class="string">&quot;bookDao&quot;</span> <span class="attr">class</span>=<span class="string">&quot;com.blog.dao.impl.BookDaoImpl&quot;</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">constructor-arg</span> <span class="attr">type</span>=<span class="string">&quot;java.lang.String&quot;</span> <span class="attr">value</span>=<span class="string">&quot;mysql&quot;</span>&gt;</span><span class="tag">&lt;/<span class="name">constructor-arg</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">constructor-arg</span> <span class="attr">type</span>=<span class="string">&quot;int&quot;</span> <span class="attr">value</span>=<span class="string">&quot;9421&quot;</span>&gt;</span><span class="tag">&lt;/<span class="name">constructor-arg</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;/<span class="name">bean</span>&gt;</span></span><br></pre></td></tr></table></figure></li></ul></li><li><strong>方式二：删除type属性，添加index属性，根据索引下标注入，下标从0开始</strong><ul><li>该方式能解决参数类型重复和参数名称问题</li><li>但这又要求参数的顺序不能变，带来了另外的耦合问题<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">bean</span> <span class="attr">id</span>=<span class="string">&quot;bookDao&quot;</span> <span class="attr">class</span>=<span class="string">&quot;com.blog.dao.impl.BookDaoImpl&quot;</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">constructor-arg</span> <span class="attr">index</span>=<span class="string">&quot;0&quot;</span> <span class="attr">value</span>=<span class="string">&quot;mysql&quot;</span>&gt;</span><span class="tag">&lt;/<span class="name">constructor-arg</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">constructor-arg</span> <span class="attr">index</span>=<span class="string">&quot;1&quot;</span> <span class="attr">value</span>=<span class="string">&quot;9421&quot;</span>&gt;</span><span class="tag">&lt;/<span class="name">constructor-arg</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;/<span class="name">bean</span>&gt;</span></span><br></pre></td></tr></table></figure></li></ul></li></ul><p><strong>如何选择使用哪种注入方式？</strong></p><ol><li>强制依赖时，使用构造器注入，使用set注入有概率不进行注入导致null对象出现<ul><li>强制依赖指对象创建时必须要注入参数</li></ul></li><li>可选依赖使用set注入，灵活性强<ul><li>可选依赖指对象创建时，不一定需要注入参数</li></ul></li><li>Spring倡导使用构造器注入，第三方框架也大多采用构造器注入，因为这种方法较严谨</li><li>如果有必要，可以同时使用两种注入方法，强制依赖时使用构造器注入，可选依赖时使用set注入</li><li>实际开发需要根据实际情况来，如果是第三方程序没有提供set方法则只能使用构造器注入</li><li><span class='p red'>自己开发模块时推荐使用set注入</span></li></ol><div class="note warning no-icon flat"><p>一句话小结：自己开发时推荐优先使用set注入，使用第三方框架时，根据第三方程序提供的构造函数和set方法，进行选择</p></div><h3 id="构造器注入小结"><a href="#构造器注入小结" class="headerlink" title="构造器注入小结"></a>构造器注入小结</h3><ul><li><strong>Set注入</strong><ul><li>简单类型<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="tag">&lt;<span class="name">bean</span> <span class="attr">...</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;&quot;</span> <span class="attr">value</span>=<span class="string">&quot;&quot;</span>/&gt;</span></span><br><span class="line"><span class="tag">&lt;/<span class="name">bean</span>&gt;</span></span><br></pre></td></tr></table></figure></li><li>引用类型<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="tag">&lt;<span class="name">bean</span> <span class="attr">...</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;&quot;</span> <span class="attr">ref</span>=<span class="string">&quot;&quot;</span>/&gt;</span></span><br><span class="line"><span class="tag">&lt;/<span class="name">bean</span>&gt;</span></span><br></pre></td></tr></table></figure></li></ul></li><li><strong>构造器注入</strong><ul><li>简单类型<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="tag">&lt;<span class="name">bean</span> <span class="attr">...</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">constructor-arg</span> <span class="attr">name</span>=<span class="string">&quot;&quot;</span> <span class="attr">index</span>=<span class="string">&quot;&quot;</span> <span class="attr">type</span>=<span class="string">&quot;&quot;</span> <span class="attr">value</span>=<span class="string">&quot;&quot;</span>/&gt;</span></span><br><span class="line"><span class="tag">&lt;/<span class="name">bean</span>&gt;</span></span><br></pre></td></tr></table></figure></li><li>引用类型<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="tag">&lt;<span class="name">bean</span> <span class="attr">...</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">constructor-arg</span> <span class="attr">name</span>=<span class="string">&quot;&quot;</span> <span class="attr">index</span>=<span class="string">&quot;&quot;</span> <span class="attr">type</span>=<span class="string">&quot;&quot;</span> <span class="attr">ref</span>=<span class="string">&quot;&quot;</span>/&gt;</span></span><br><span class="line"><span class="tag">&lt;/<span class="name">bean</span>&gt;</span></span><br></pre></td></tr></table></figure></li><li>根据类型注入<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">bean</span> <span class="attr">id</span>=<span class="string">&quot;bookDao&quot;</span> <span class="attr">class</span>=<span class="string">&quot;com.blog.dao.impl.BookDaoImpl&quot;</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">constructor-arg</span> <span class="attr">type</span>=<span class="string">&quot;java.lang.String&quot;</span> <span class="attr">value</span>=<span class="string">&quot;mysql&quot;</span>&gt;</span><span class="tag">&lt;/<span class="name">constructor-arg</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">constructor-arg</span> <span class="attr">type</span>=<span class="string">&quot;int&quot;</span> <span class="attr">value</span>=<span class="string">&quot;9421&quot;</span>&gt;</span><span class="tag">&lt;/<span class="name">constructor-arg</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;/<span class="name">bean</span>&gt;</span></span><br></pre></td></tr></table></figure></li><li>根据索引下标注入<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">bean</span> <span class="attr">id</span>=<span class="string">&quot;bookDao&quot;</span> <span class="attr">class</span>=<span class="string">&quot;com.blog.dao.impl.BookDaoImpl&quot;</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">constructor-arg</span> <span class="attr">index</span>=<span class="string">&quot;0&quot;</span> <span class="attr">value</span>=<span class="string">&quot;mysql&quot;</span>&gt;</span><span class="tag">&lt;/<span class="name">constructor-arg</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">constructor-arg</span> <span class="attr">index</span>=<span class="string">&quot;1&quot;</span> <span class="attr">value</span>=<span class="string">&quot;9421&quot;</span>&gt;</span><span class="tag">&lt;/<span class="name">constructor-arg</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;/<span class="name">bean</span>&gt;</span></span><br></pre></td></tr></table></figure></li></ul></li><li><strong>依赖注入的方式选择</strong><ul><li>自己开发模块时推荐使用set注入</li><li>第三方技术根据实际情况选择</li></ul></li></ul><h2 id="自动配置"><a href="#自动配置" class="headerlink" title="自动配置"></a>自动配置</h2><ul><li>无论构造器注入还是set注入，都需要修改配置文件，相对还是很麻烦，对此Spring为我们提供了<code>自动配置</code>功能</li><li>Ioc容器根据Bean所依赖的资源在容器中自动查找并注入到Bean的过程称为<code>自动装配</code></li><li>自动装配的方式：<ul><li>按类型</li><li>按名称</li><li>按构造方法</li><li>不启用自动装配（忽略）</li></ul></li></ul><h3 id="环境准备-1"><a href="#环境准备-1" class="headerlink" title="环境准备"></a>环境准备</h3><p>修改<code>BookDao</code>、<code>BookDaoImpl</code>、<code>BookService</code>和<code>BookServiceImpl</code>类<br><div class="tabs" id="自动装配1"><ul class="nav-tabs"><li class="tab active"><button type="button" data-href="#自动装配1-1">BookDao</button></li><li class="tab"><button type="button" data-href="#自动装配1-2">BookDaoImpl</button></li><li class="tab"><button type="button" data-href="#自动装配1-3">BookService</button></li><li class="tab"><button type="button" data-href="#自动装配1-4">BookServiceImpl</button></li></ul><div class="tab-contents"><div class="tab-item-content active" id="自动装配1-1"><figure class="highlight java"><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="keyword">public</span> <span class="keyword">interface</span> <span class="title class_">BookDao</span> &#123;</span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">save</span><span class="params">()</span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><button type="button" class="tab-to-top" aria-label="scroll to top"><i class="fas fa-arrow-up"></i></button></div><div class="tab-item-content" id="自动装配1-2"><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><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">public</span> <span class="keyword">class</span> <span class="title class_">BookDaoImpl</span> <span class="keyword">implements</span> <span class="title class_">BookDao</span> &#123;</span><br><span class="line">    </span><br><span class="line">    <span class="keyword">private</span> String databaseName;</span><br><span class="line">    <span class="keyword">private</span> <span class="type">int</span> connectionNum;</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">()</span> &#123;</span><br><span class="line">        System.out.println(<span class="string">&quot;book dao save ...&quot;</span>);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><button type="button" class="tab-to-top" aria-label="scroll to top"><i class="fas fa-arrow-up"></i></button></div><div class="tab-item-content" id="自动装配1-3"><figure class="highlight java"><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="keyword">public</span> <span class="keyword">interface</span> <span class="title class_">BookService</span> &#123;</span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">save</span><span class="params">()</span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><button type="button" class="tab-to-top" aria-label="scroll to top"><i class="fas fa-arrow-up"></i></button></div><div class="tab-item-content" id="自动装配1-4"><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><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_">BookServiceImpl</span> <span class="keyword">implements</span> <span class="title class_">BookService</span>&#123;</span><br><span class="line">    <span class="keyword">private</span> BookDao bookDao;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">setBookDao</span><span class="params">(BookDao bookDao)</span> &#123;</span><br><span class="line">        <span class="built_in">this</span>.bookDao = bookDao;</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_">save</span><span class="params">()</span> &#123;</span><br><span class="line">        System.out.println(<span class="string">&quot;book service save ...&quot;</span>);</span><br><span class="line">        bookDao.save();</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><button type="button" class="tab-to-top" aria-label="scroll to top"><i class="fas fa-arrow-up"></i></button></div></div></div><br>配置文件：<br><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="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">beans</span> <span class="attr">xmlns</span>=<span class="string">&quot;http://www.springframework.org/schema/beans&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://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd&quot;</span>&gt;</span></span><br><span class="line"></span><br><span class="line">    <span class="tag">&lt;<span class="name">bean</span> <span class="attr">id</span>=<span class="string">&quot;bookDao&quot;</span> <span class="attr">class</span>=<span class="string">&quot;com.blog.dao.impl.BookDaoImpl&quot;</span>/&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">bean</span> <span class="attr">id</span>=<span class="string">&quot;bookService&quot;</span> <span class="attr">class</span>=<span class="string">&quot;com.blog.service.impl.BookServiceImpl&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;bookDao&quot;</span> <span class="attr">ref</span>=<span class="string">&quot;bookDao&quot;</span>/&gt;</span></span><br><span class="line">    <span class="tag">&lt;/<span class="name">bean</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;/<span class="name">beans</span>&gt;</span></span><br></pre></td></tr></table></figure><br>运行类：<br><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><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="keyword">public</span> <span class="keyword">class</span> <span class="title class_">App</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">ApplicationContext</span> <span class="variable">ctx</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">ClassPathXmlApplicationContext</span>(<span class="string">&quot;applicationContext.xml&quot;</span>);</span><br><span class="line">        <span class="type">BookService</span> <span class="variable">bookService</span> <span class="operator">=</span> (BookService) ctx.getBean(<span class="string">&quot;bookService&quot;</span>);</span><br><span class="line">        bookService.save();</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></p><h3 id="启用自动装配"><a href="#启用自动装配" class="headerlink" title="启用自动装配"></a>启用自动装配</h3><ul><li>启用自动装配仅仅需要修改配置文件即可<ul><li>将手动装配的<code>&lt;property&gt;</code>标签删除</li><li>在<code>&lt;bean&gt;</code>标签中添加autowire属性</li><li>通过更改autowire属性为byName或者byType来控制根据名称装配还是根据类型装配<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="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">beans</span> <span class="attr">xmlns</span>=<span class="string">&quot;http://www.springframework.org/schema/beans&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://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd&quot;</span>&gt;</span></span><br><span class="line"></span><br><span class="line"><span class="comment">&lt;!--    &lt;bean id=&quot;bookDao&quot; class=&quot;com.blog.dao.impl.BookDaoImpl&quot;/&gt;--&gt;</span></span><br><span class="line"><span class="comment">&lt;!--    既然是按类型注入了，那么id写不写都无所谓了--&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">bean</span> <span class="attr">class</span>=<span class="string">&quot;com.blog.dao.impl.BookDaoImpl&quot;</span>/&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">bean</span> <span class="attr">id</span>=<span class="string">&quot;bookService&quot;</span> <span class="attr">class</span>=<span class="string">&quot;com.blog.service.impl.BookServiceImpl&quot;</span> <span class="attr">autowire</span>=<span class="string">&quot;byType&quot;</span>/&gt;</span></span><br><span class="line"><span class="tag">&lt;/<span class="name">beans</span>&gt;</span></span><br></pre></td></tr></table></figure></li></ul></li><li>再次运行程序，结果如下，说明自动装配成功<blockquote><p>book service save …<br>book dao save …</p></blockquote></li></ul><div class="note warning no-icon flat"><p>注意事项：</p><ul><li>在Spring中，自动装配默认使用<code>根据类型</code>的<code>set注入</code>，因此注入属性的类中对应的set方法必须存在</li><li>被注入的对象需要在Spring的Ioc容器管理</li><li>根据类型注入，如果Ioc中找到多个类就会报<code>NoUniqueBeanDefinitionException</code>异常</li></ul></div><h3 id="自动装配小结"><a href="#自动装配小结" class="headerlink" title="自动装配小结"></a>自动装配小结</h3><ul><li>如果按照名称去找对应的bean对象，找不到则注入Null</li><li>当某一个类型在IOC容器中有多个对象，按照名称注入只找其指定名称对应的bean对象，不会报错</li><li>两种方式介绍完后，以后用的更多的是<code>根据类型</code>注入。</li><li>对于依赖注入，需要注意一些其他的配置特征:<ol><li>自动装配用于引用类型依赖注入，不能对简单类型进行操作</li><li>使用按类型装配时（byType）必须保障容器中相同类型的bean唯一，推荐使用</li><li>使用按名称装配时（byName）必须保障容器中具有指定名称的bean，因变量名与配置耦合，不推荐使用</li><li><strong>自动装配优先级低于setter注入与构造器注入，同时出现时自动装配配置失效</strong><h2 id="集合注入"><a href="#集合注入" class="headerlink" title="集合注入"></a>集合注入</h2>集合中既可以装简单数据类型也可以装引用数据类型，对于集合，在Spring中该如何注入呢?</li></ol></li><li>常见的集合类型<ul><li>数组</li><li>List</li><li>Set</li><li>Map</li><li>Properties</li></ul></li></ul><h3 id="环境准备-2"><a href="#环境准备-2" class="headerlink" title="环境准备"></a>环境准备</h3><ul><li>修改BookDaoImpl类<div class="tabs" id=""><ul class="nav-tabs"><li class="tab active"><button type="button" data-href="#-1">BookDao</button></li><li class="tab"><button type="button" data-href="#-2">BookDaoImpl</button></li></ul><div class="tab-contents"><div class="tab-item-content active" id="-1"><figure class="highlight java"><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="keyword">public</span> <span class="keyword">interface</span> <span class="title class_">BookDao</span> &#123;</span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">save</span><span class="params">()</span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><button type="button" class="tab-to-top" aria-label="scroll to top"><i class="fas fa-arrow-up"></i></button></div><div class="tab-item-content" id="-2"><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><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">public</span> <span class="keyword">class</span> <span class="title class_">BookDaoImpl</span> <span class="keyword">implements</span> <span class="title class_">BookDao</span> &#123;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">private</span> <span class="type">int</span>[] array;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">private</span> List&lt;String&gt; list;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">private</span> Set&lt;String&gt; set;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">private</span> Map&lt;String,String&gt; map;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">private</span> Properties properties;</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">()</span> &#123;</span><br><span class="line">        System.out.println(<span class="string">&quot;book dao save ...&quot;</span>);</span><br><span class="line"></span><br><span class="line">        System.out.println(<span class="string">&quot;遍历数组:&quot;</span> + Arrays.toString(array));</span><br><span class="line"></span><br><span class="line">        System.out.println(<span class="string">&quot;遍历List&quot;</span> + list);</span><br><span class="line"></span><br><span class="line">        System.out.println(<span class="string">&quot;遍历Set&quot;</span> + set);</span><br><span class="line"></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">        System.out.println(<span class="string">&quot;遍历Properties&quot;</span> + properties);</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_">setArray</span><span class="params">(<span class="type">int</span>[] array)</span> &#123;</span><br><span class="line">        <span class="built_in">this</span>.array = array;</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_">setList</span><span class="params">(List&lt;String&gt; list)</span> &#123;</span><br><span class="line">        <span class="built_in">this</span>.list = list;</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_">setSet</span><span class="params">(Set&lt;String&gt; set)</span> &#123;</span><br><span class="line">        <span class="built_in">this</span>.set = set;</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_">setMap</span><span class="params">(Map&lt;String, String&gt; map)</span> &#123;</span><br><span class="line">        <span class="built_in">this</span>.map = map;</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_">setProperties</span><span class="params">(Properties properties)</span> &#123;</span><br><span class="line">        <span class="built_in">this</span>.properties = properties;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><button type="button" class="tab-to-top" aria-label="scroll to top"><i class="fas fa-arrow-up"></i></button></div></div></div></li><li>修改配置文件<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="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">beans</span> <span class="attr">xmlns</span>=<span class="string">&quot;http://www.springframework.org/schema/beans&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://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd&quot;</span>&gt;</span></span><br><span class="line"></span><br><span class="line">    <span class="tag">&lt;<span class="name">bean</span> <span class="attr">id</span>=<span class="string">&quot;bookDao&quot;</span> <span class="attr">class</span>=<span class="string">&quot;com.blog.dao.impl.BookDaoImpl&quot;</span>/&gt;</span></span><br><span class="line"><span class="tag">&lt;/<span class="name">beans</span>&gt;</span></span><br></pre></td></tr></table></figure></li><li>修改App运行类<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><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="keyword">public</span> <span class="keyword">class</span> <span class="title class_">App</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">ApplicationContext</span> <span class="variable">ctx</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">ClassPathXmlApplicationContext</span>(<span class="string">&quot;applicationContext.xml&quot;</span>);</span><br><span class="line">        <span class="type">BookDao</span> <span class="variable">bookDao</span> <span class="operator">=</span> (BookDao) ctx.getBean(<span class="string">&quot;bookDao&quot;</span>);</span><br><span class="line">        bookDao.save();</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></li><li>接下来学习集合注入均在上面环境基础上完成，都是在bookDao的<code>&lt;bean&gt;</code>标签上使用<code>&lt;property</code>进行注入</li></ul><h3 id="注入数组类型"><a href="#注入数组类型" class="headerlink" title="注入数组类型"></a>注入数组类型</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="tag">&lt;<span class="name">property</span> <span class="attr">name</span>=<span class="string">&quot;array&quot;</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">array</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">value</span>&gt;</span>100<span class="tag">&lt;/<span class="name">value</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">value</span>&gt;</span>200<span class="tag">&lt;/<span class="name">value</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">value</span>&gt;</span>300<span class="tag">&lt;/<span class="name">value</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;/<span class="name">array</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;/<span class="name">property</span>&gt;</span></span><br></pre></td></tr></table></figure><h3 id="注入List类型"><a href="#注入List类型" class="headerlink" title="注入List类型"></a>注入List类型</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="tag">&lt;<span class="name">property</span> <span class="attr">name</span>=<span class="string">&quot;list&quot;</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">list</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">value</span>&gt;</span>张三<span class="tag">&lt;/<span class="name">value</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">value</span>&gt;</span>ABC<span class="tag">&lt;/<span class="name">value</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">value</span>&gt;</span>123<span class="tag">&lt;/<span class="name">value</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;/<span class="name">list</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;/<span class="name">property</span>&gt;</span></span><br></pre></td></tr></table></figure><h3 id="注入Set类型"><a href="#注入Set类型" class="headerlink" title="注入Set类型"></a>注入Set类型</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></pre></td><td class="code"><pre><span class="line"><span class="tag">&lt;<span class="name">property</span> <span class="attr">name</span>=<span class="string">&quot;set&quot;</span>&gt;</span></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">value</span>&gt;</span>100<span class="tag">&lt;/<span class="name">value</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">value</span>&gt;</span>200<span class="tag">&lt;/<span class="name">value</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">value</span>&gt;</span>ABC<span class="tag">&lt;/<span class="name">value</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">value</span>&gt;</span>ABC<span class="tag">&lt;/<span class="name">value</span>&gt;</span></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">property</span>&gt;</span></span><br></pre></td></tr></table></figure><h3 id="注入Map类型"><a href="#注入Map类型" class="headerlink" title="注入Map类型"></a>注入Map类型</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="tag">&lt;<span class="name">property</span> <span class="attr">name</span>=<span class="string">&quot;map&quot;</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">map</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">entry</span> <span class="attr">key</span>=<span class="string">&quot;探路者&quot;</span> <span class="attr">value</span>=<span class="string">&quot;马文&quot;</span>/&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">entry</span> <span class="attr">key</span>=<span class="string">&quot;次元游记兵&quot;</span> <span class="attr">value</span>=<span class="string">&quot;恶灵&quot;</span>/&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">entry</span> <span class="attr">key</span>=<span class="string">&quot;易位窃贼&quot;</span> <span class="attr">value</span>=<span class="string">&quot;罗芭&quot;</span>/&gt;</span></span><br><span class="line">    <span class="tag">&lt;/<span class="name">map</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;/<span class="name">property</span>&gt;</span></span><br></pre></td></tr></table></figure><h3 id="注入Properties类型"><a href="#注入Properties类型" class="headerlink" title="注入Properties类型"></a>注入Properties类型</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="tag">&lt;<span class="name">property</span> <span class="attr">name</span>=<span class="string">&quot;properties&quot;</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">props</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">prop</span> <span class="attr">key</span>=<span class="string">&quot;暴雷&quot;</span>&gt;</span>沃尔特·菲茨罗伊<span class="tag">&lt;/<span class="name">prop</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">prop</span> <span class="attr">key</span>=<span class="string">&quot;寻血猎犬&quot;</span>&gt;</span>布洛特·亨德尔<span class="tag">&lt;/<span class="name">prop</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">prop</span> <span class="attr">key</span>=<span class="string">&quot;命脉&quot;</span>&gt;</span>阿杰·切<span class="tag">&lt;/<span class="name">prop</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;/<span class="name">props</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;/<span class="name">property</span>&gt;</span></span><br></pre></td></tr></table></figure><div class="note info no-icon flat"><p>说明：</p><ul><li>property标签使用set方式注入，构造方式注入<code>constructor-arg</code>标签内部也可以写<code>&lt;array&gt;</code>、<code>&lt;List&gt;</code>、<code>Set</code>、<code>Map</code>、<code>Props</code>标签</li><li>List底层也是使用数组实现的，所以<code>list</code>和<code>array</code>标签可以混用</li><li>集合中添加引用类型，只需要把<code>&lt;value&gt;</code>标签改为<code>&lt;ref&gt;</code>标签即可，但这种方式使用较少(引用类型默认为单例)</li></ul></div><h2 id="DI小结"><a href="#DI小结" class="headerlink" title="DI小结"></a>DI小结</h2><ul><li><strong>配置文件注入</strong><ul><li>构造器注入(<code>&lt;constructor-arg&gt;</code>)<ul><li>根据类型注入</li><li>根据名称注入</li><li>根据下标注入</li></ul></li><li>setter注入(<code>property</code>)<ul><li>基础数据类型：<code>&lt;value&gt;</code></li><li>引用数据类型：<code>&lt;ref&gt;</code></li></ul></li></ul></li><li><strong>注入方式选择</strong><ul><li>自己开发模块时推荐使用set注入</li><li>第三方技术根据实际情况选择</li></ul></li><li><strong>自动装配</strong><ul><li>在配置文件中添加autowire属性，值为byType或byName<ul><li>byType：根据类型自动装配</li><li>byName：根据名称自动装配</li></ul></li></ul></li><li><strong>集合注入</strong><ul><li>数组:<code>&lt;array&gt;</code></li><li>List:<code>&lt;list&gt;</code></li><li>Set:<code>&lt;set&gt;</code></li><li>Map:<code>&lt;map&gt;</code></li><li>Properties:<code>&lt;props&gt;</code></li></ul></li></ul><h1 id="Ioc-DI管理第三方Bean"><a href="#Ioc-DI管理第三方Bean" class="headerlink" title="Ioc/DI管理第三方Bean"></a>Ioc/DI管理第三方Bean</h1><ul><li>前面的内容都是管理自己写的类，现在学习如何管理第三方的类</li></ul><h2 id="案例：数据源对象管理"><a href="#案例：数据源对象管理" class="headerlink" title="案例：数据源对象管理"></a>案例：数据源对象管理</h2><div class="note info no-icon flat"><p>该案例使用<code>Druid</code>数据源来配置学习</p></div><h3 id="环境准备-3"><a href="#环境准备-3" class="headerlink" title="环境准备"></a>环境准备</h3><ol><li>创建Maven项目</li><li>添加需要的依赖<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="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<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-context<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.2.10.RELEASE<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></pre></td></tr></table></figure></li><li>resources目录下新建spring的配置文件<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="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">beans</span> <span class="attr">xmlns</span>=<span class="string">&quot;http://www.springframework.org/schema/beans&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://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd&quot;</span>&gt;</span></span><br><span class="line"></span><br><span class="line"><span class="tag">&lt;/<span class="name">beans</span>&gt;</span></span><br></pre></td></tr></table></figure></li><li><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></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">App</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">ApplicationContext</span> <span class="variable">ctx</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">ClassPathXmlApplicationContext</span>(<span class="string">&quot;applicationContext.xml&quot;</span>);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h3 id="实现Druid管理"><a href="#实现Druid管理" class="headerlink" title="实现Druid管理"></a>实现Druid管理</h3><div class="note info no-icon flat"><p>需求分析：使用Spring的Ioc容器管理Druid连接池对象</p><ol><li>使用第三方技术，需要先在pom.xml文件添加依赖</li><li>在配置文件中将第三方类做成一个bean，让Ioc容器管理</li><li>数据库连接四要素：驱动、连接、用户名和密码，需要为这些属性赋值</li><li>从Ioc容器中获取bean对象，打印到控制台</li></ol></div></li><li><p>导入<code>Druid</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></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.1.16<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></li><li><p>配置第三方bean</p></li></ol><p>此时第三方bean使用set注入还是构造器注入便需要根据第三方类为我们提供的内容来判断<br>通过查看源码发现DruidDataSource只提供了两个构造器，显然不能使用构造方法注入</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="keyword">public</span> <span class="title function_">DruidDataSource</span><span class="params">()</span></span><br><span class="line"><span class="keyword">public</span> <span class="title function_">DruidDataSource</span><span class="params">(<span class="type">boolean</span> fairLock)</span> </span><br></pre></td></tr></table></figure><p>确定使用set注入后，在配置文件中添加<code>DruidDataSource</code>的配置<br><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></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">beans</span> <span class="attr">xmlns</span>=<span class="string">&quot;http://www.springframework.org/schema/beans&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;</span></span></span><br><span class="line"><span class="string"><span class="tag">            http://www.springframework.org/schema/beans</span></span></span><br><span class="line"><span class="string"><span class="tag">            http://www.springframework.org/schema/beans/spring-beans.xsd&quot;</span>&gt;</span></span><br><span class="line">    <span class="comment">&lt;!--管理DruidDataSource对象--&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">bean</span> <span class="attr">class</span>=<span class="string">&quot;com.alibaba.druid.pool.DruidDataSource&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;driverClassName&quot;</span> <span class="attr">value</span>=<span class="string">&quot;com.mysql.jdbc.Driver&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;jdbc:mysql://localhost:13306/spring_db&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;root&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;yourPassword&quot;</span>/&gt;</span></span><br><span class="line">    <span class="tag">&lt;/<span class="name">bean</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;/<span class="name">beans</span>&gt;</span></span><br></pre></td></tr></table></figure></p><ol><li>从Ioc容器中获取bean对象<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><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="keyword">public</span> <span class="keyword">class</span> <span class="title class_">App</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">ApplicationContext</span> <span class="variable">ctx</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">ClassPathXmlApplicationContext</span>(<span class="string">&quot;applicationContext.xml&quot;</span>);</span><br><span class="line">       <span class="type">DruidDataSource</span> <span class="variable">dataSource</span> <span class="operator">=</span> context.getBean(DruidDataSource.class);</span><br><span class="line">       System.out.println(dataSource);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></li><li>运行程序<br>打印以下结果说明第三方Bean成功交给Ioc容器管理<blockquote><p>{<br>CreateTime:”2022-09-01 10:15:19”,<br>ActiveCount:0,<br>PoolingCount:0,<br>CreateCount:0,<br>DestroyCount:0,<br>CloseCount:0,<br>ConnectCount:0,<br>Connections:[<br>]<br>}</p></blockquote></li></ol><h2 id="加载properties文件"><a href="#加载properties文件" class="headerlink" title="加载properties文件"></a>加载properties文件</h2><ul><li>我们完成Druid数据源配置后又发现一些问题：<ul><li>数据源中用到一些固定的常量（如数据库连接四要素），而将这些值直接放到Spring的配置文件中不利于后期维护，因此选择将这些固定的常量提取到外部的properties配置文件中</li><li>提取到properties文件的数据如何在Spring框架中读取这是接下来要解决的问题</li></ul></li></ul><h3 id="Druid属性优化"><a href="#Druid属性优化" class="headerlink" title="Druid属性优化"></a>Druid属性优化</h3><div class="note info no-icon flat"><p>需求：将数据库连接四要素数据提取到properties配置文件，Spring加载文件中的配置信息并根据信息完成属性注入</p><ol><li>新建properties文件</li><li>将数据库连接需要的信息配置到文件中</li><li>在Spring配置文件中加载properties文件</li><li>使用加载到的值实现属性注入</li></ol></div><ol><li>准备properties配置文件<br>新建jdbc.properties文件并添加对应的属性键值对<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></pre></td><td class="code"><pre><span class="line"><span class="attr">jdbc.driverClass</span>=<span class="string">com.mysql.jdbc.Driver</span></span><br><span class="line"><span class="attr">jdbc.url</span>=<span class="string">jdbc:mysql://localhost:13306/spring_db</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">password</span></span><br></pre></td></tr></table></figure></li><li>开启<code>context</code>命名空间<br>修改Spring配置文件，开启<code>context</code>命名空间<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></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">beans</span> <span class="attr">xmlns</span>=<span class="string">&quot;http://www.springframework.org/schema/beans&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">xmlns:context</span>=<span class="string">&quot;http://www.springframework.org/schema/context&quot;</span></span></span><br><span class="line"><span class="tag">       <span class="attr">xsi:schemaLocation</span>=<span class="string">&quot;</span></span></span><br><span class="line"><span class="string"><span class="tag">            http://www.springframework.org/schema/beans</span></span></span><br><span class="line"><span class="string"><span class="tag">            http://www.springframework.org/schema/beans/spring-beans.xsd</span></span></span><br><span class="line"><span class="string"><span class="tag">            http://www.springframework.org/schema/context</span></span></span><br><span class="line"><span class="string"><span class="tag">            http://www.springframework.org/schema/context/spring-context.xsd</span></span></span><br><span class="line"><span class="string"><span class="tag">        &quot;</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;/<span class="name">beans</span>&gt;</span></span><br></pre></td></tr></table></figure></li><li>加载properties文件<br>在配置文件中使用以下标签来加载文件<figure class="highlight xml"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="tag">&lt;<span class="name">context:property-placeholder</span> <span class="attr">location</span>=<span class="string">&quot;jdbc.properties&quot;</span>/&gt;</span></span><br></pre></td></tr></table></figure></li><li>完成属性注入<br>使用<code>$&#123;&#125;</code>占位符来引用properties文件中的值<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="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">beans</span> <span class="attr">xmlns</span>=<span class="string">&quot;http://www.springframework.org/schema/beans&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">xmlns:context</span>=<span class="string">&quot;http://www.springframework.org/schema/context&quot;</span></span></span><br><span class="line"><span class="tag">       <span class="attr">xsi:schemaLocation</span>=<span class="string">&quot;</span></span></span><br><span class="line"><span class="string"><span class="tag">            http://www.springframework.org/schema/beans</span></span></span><br><span class="line"><span class="string"><span class="tag">            http://www.springframework.org/schema/beans/spring-beans.xsd</span></span></span><br><span class="line"><span class="string"><span class="tag">            http://www.springframework.org/schema/context</span></span></span><br><span class="line"><span class="string"><span class="tag">            http://www.springframework.org/schema/context/spring-context.xsd</span></span></span><br><span class="line"><span class="string"><span class="tag">        &quot;</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">context:property-placeholder</span> <span class="attr">location</span>=<span class="string">&quot;jdbc.properties&quot;</span>/&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">bean</span> <span class="attr">id</span>=<span class="string">&quot;dataSource&quot;</span> <span class="attr">class</span>=<span class="string">&quot;com.alibaba.druid.pool.DruidDataSource&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;driverClassName&quot;</span> <span class="attr">value</span>=<span class="string">&quot;$&#123;jdbc.driverClass&#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">bean</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;/<span class="name">beans</span>&gt;</span></span><br></pre></td></tr></table></figure></li></ol><ul><li>此时就已经将properties文件中的值成功注入到了DruidDataSource对象中</li></ul><p><code>注意事项：</code></p><ul><li>问题一：键值对的key为<code>username</code>等系统内置属性引发问题<ul><li>在properties中配置键值对的时候，如果key设置为<code>username</code>时，在运行后控制台打印该值发现打印的不是<code>root</code>，而是自己电脑的用户名</li><li>原因：<code>&lt;context:property-placeholder/&gt;</code>标签会加载系统的环境变量，且环境变量会优先加载，下面的代码可以输出系统环境变量<figure class="highlight java"><table><tr><td class="gutter"><pre><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="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">    Map&lt;String, String&gt; env = System.getenv();</span><br><span class="line">    System.out.println(env);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></li><li>打印的结果是USERNAME=XXX[自己电脑用户名]</li><li>解决方案：将ystem-properties-mode设置为NEVER，表示不加载系统环境变量，这样就可以解决上面的问题了，当然还有一个解决方案就是给属性加上前缀，避免使用username作为属性的key。<figure class="highlight xml"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="tag">&lt;<span class="name">context:property-placeholder</span> <span class="attr">location</span>=<span class="string">&quot;jdbc.properties&quot;</span> <span class="attr">system-properties-mode</span>=<span class="string">&quot;NEVER&quot;</span>/&gt;</span></span><br></pre></td></tr></table></figure></li></ul></li><li>问题二：多个properties文件加载,该如何配置？<ul><li>修改applicationContext.xml<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></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">context:property-placeholder</span> <span class="attr">location</span>=<span class="string">&quot;jdbc.properties,jdbc2.properties&quot;</span> <span class="attr">system-properties-mode</span>=<span class="string">&quot;NEVER&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">context:property-placeholder</span> <span class="attr">location</span>=<span class="string">&quot;*.properties&quot;</span> <span class="attr">system-properties-mode</span>=<span class="string">&quot;NEVER&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">context:property-placeholder</span> <span class="attr">location</span>=<span class="string">&quot;classpath:*.properties&quot;</span> <span class="attr">system-properties-mode</span>=<span class="string">&quot;NEVER&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">context:property-placeholder</span> <span class="attr">location</span>=<span class="string">&quot;classpath*:*.properties&quot;</span> <span class="attr">system-properties-mode</span>=<span class="string">&quot;NEVER&quot;</span>/&gt;</span></span><br></pre></td></tr></table></figure><div class="note info no-icon flat"><ul><li>说明：<ul><li>方式一：可以实现，如果配置文件多的话，每个都需要配置</li><li>方式二：<code>*.properties</code>代表所有以properties结尾的文件都会被加载，可以解决方式一的问题，但是不标准</li><li>方式三：标准的写法，<code>classpath:</code>代表的是从根路径下开始查找，但是只能查询当前项目的根路径</li><li>方式四：不仅可以加载当前项目还可以加载当前项目所依赖的所有项目的根路径下的properties配置文件</li></ul></li></ul></div><h3 id="小结"><a href="#小结" class="headerlink" title="小结"></a>小结</h3></li></ul></li><li>如何开启<code>context</code>命名空间<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="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">beans</span> <span class="attr">xmlns</span>=<span class="string">&quot;http://www.springframework.org/schema/beans&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">xmlns:context</span>=<span class="string">&quot;http://www.springframework.org/schema/context&quot;</span></span></span><br><span class="line"><span class="tag">        <span class="attr">xsi:schemaLocation</span>=<span class="string">&quot;</span></span></span><br><span class="line"><span class="string"><span class="tag">            http://www.springframework.org/schema/beans</span></span></span><br><span class="line"><span class="string"><span class="tag">            http://www.springframework.org/schema/beans/spring-beans.xsd</span></span></span><br><span class="line"><span class="string"><span class="tag">            http://www.springframework.org/schema/context</span></span></span><br><span class="line"><span class="string"><span class="tag">            http://www.springframework.org/schema/context/spring-context.xsd</span></span></span><br><span class="line"><span class="string"><span class="tag">        &quot;</span>&gt;</span></span><br></pre></td></tr></table></figure></li></ul><ul><li><p>如何加载properties配置文件</p><figure class="highlight xml"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="tag">&lt;<span class="name">context:property-placeholder</span> <span class="attr">location</span>=<span class="string">&quot;&quot;</span> <span class="attr">system-properties-mode</span>=<span class="string">&quot;NEVER&quot;</span>/&gt;</span></span><br></pre></td></tr></table></figure></li><li><p>如何在applicationContext.xml引入properties配置文件中的值</p><ul><li>使用占位符：<code>$&#123;key&#125;</code></li></ul></li></ul><h1 id="核心容器"><a href="#核心容器" class="headerlink" title="核心容器"></a>核心容器</h1><p>此处学习的核心容器，可以简单的理解为<code>ApplicationContext</code>，即前面App应用类创建的对象，接下来从结果问题来入手学习该容器的内容：</p><ul><li>如何创建容器</li><li>创建好容器如何获得Bean对象</li><li>容器类的层次结构是什么</li><li>BeanFactory是什么</li></ul><h2 id="环境准备-4"><a href="#环境准备-4" class="headerlink" title="环境准备"></a>环境准备</h2><ul><li>创建Maven项目</li><li>添加Spring的依赖<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="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<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-context<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.2.10.RELEASE<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></pre></td></tr></table></figure></li><li>新建Spring配置文件<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="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">beans</span> <span class="attr">xmlns</span>=<span class="string">&quot;http://www.springframework.org/schema/beans&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;</span></span></span><br><span class="line"><span class="string"><span class="tag">            http://www.springframework.org/schema/beans</span></span></span><br><span class="line"><span class="string"><span class="tag">            http://www.springframework.org/schema/beans/spring-beans.xsd</span></span></span><br><span class="line"><span class="string"><span class="tag">        &quot;</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">bean</span> <span class="attr">id</span>=<span class="string">&quot;bookDao&quot;</span> <span class="attr">class</span>=<span class="string">&quot;com.blog.dao.impl.BookDaoImpl&quot;</span>/&gt;</span></span><br><span class="line"><span class="tag">&lt;/<span class="name">beans</span>&gt;</span></span><br></pre></td></tr></table></figure></li><li>添加BookDao和BookDaoImpl类<div class="tabs" id="核心容器"><ul class="nav-tabs"><li class="tab active"><button type="button" data-href="#核心容器-1">BookDao</button></li><li class="tab"><button type="button" data-href="#核心容器-2">BookDaoImpl</button></li></ul><div class="tab-contents"><div class="tab-item-content active" id="核心容器-1"><figure class="highlight java"><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="keyword">public</span> <span class="keyword">interface</span> <span class="title class_">BookDao</span> &#123;</span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">save</span><span class="params">()</span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><button type="button" class="tab-to-top" aria-label="scroll to top"><i class="fas fa-arrow-up"></i></button></div><div class="tab-item-content" id="核心容器-2"><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><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_">BookDaoImpl</span> <span class="keyword">implements</span> <span class="title class_">BookDao</span> &#123;</span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">save</span><span class="params">()</span> &#123;</span><br><span class="line">        System.out.println(<span class="string">&quot;book dao save ...&quot;</span> );</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><button type="button" class="tab-to-top" aria-label="scroll to top"><i class="fas fa-arrow-up"></i></button></div></div></div></li><li>创建运行类<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><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="keyword">public</span> <span class="keyword">class</span> <span class="title class_">App</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">ApplicationContext</span> <span class="variable">ctx</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">ClassPathXmlApplicationContext</span>(<span class="string">&quot;applicationContext.xml&quot;</span>);</span><br><span class="line">        <span class="type">BookDao</span> <span class="variable">bookDao</span> <span class="operator">=</span> (BookDao) ctx.getBean(<span class="string">&quot;bookDao&quot;</span>);</span><br><span class="line">        bookDao.save();</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h2 id="容器"><a href="#容器" class="headerlink" title="容器"></a>容器</h2><h3 id="容器的创建方式"><a href="#容器的创建方式" class="headerlink" title="容器的创建方式"></a>容器的创建方式</h3></li><li>案例中创建<code>ApplicationContext</code>方式如下</li><li>这种方式翻译为：类路径下的XML配置文件<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="type">ApplicationContext</span> <span class="variable">ctx</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">ClassPathXmlApplicationContext</span>(<span class="string">&quot;applicationContext.xml&quot;</span>);</span><br></pre></td></tr></table></figure></li><li>Spring还提供了一种绝对路径的创建方式<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="type">ApplicationContext</span> <span class="variable">ctx</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">FileSystemXmlApplicationContext</span>(<span class="string">&quot;D:\xxx/xxx\applicationContext.xml&quot;</span>);</span><br></pre></td></tr></table></figure></li><li>这种方式能实现，但项目位置一旦发生变化代码就要跟着改，增加耦合度，所以不推荐使用<h3 id="获取Bean的三种方式"><a href="#获取Bean的三种方式" class="headerlink" title="获取Bean的三种方式"></a>获取Bean的三种方式</h3></li><li>方式一：获得bean后强转</li><li>该方式存在的问题在于每次获取Bean后需要进行强转操作<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="type">BookDao</span> <span class="variable">bookDao</span> <span class="operator">=</span> (BookDao) ctx.getBean(<span class="string">&quot;bookDao&quot;</span>);</span><br></pre></td></tr></table></figure></li><li>方式二：指定获取Bean的类型</li><li>该方式在调用方法时需要指定Bean的类型，避免了强转操作<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="type">BookDao</span> <span class="variable">bookDao</span> <span class="operator">=</span> ctx.getBean(<span class="string">&quot;bookDao&quot;</span>, BookDao.class);</span><br></pre></td></tr></table></figure></li><li>方式三：直接传Bean的类型<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="type">BookDao</span> <span class="variable">bookDao</span> <span class="operator">=</span> ctx.getBean(BookDao.class);</span><br></pre></td></tr></table></figure></li><li>该方式类似按类型注入。必须确保Ioc容器中该类型对应的Bean对象只能有一个</li></ul><h3 id="BeanFactory"><a href="#BeanFactory" class="headerlink" title="BeanFactory"></a>BeanFactory</h3><p>容器的最上级父接口就是<code>BeanFactory</code>，使用<code>BeanFactory</code>也可以创建Ioc容器<br><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><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_">AppForBeanFactory</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">Resource</span> <span class="variable">resources</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">ClassPathResource</span>(<span class="string">&quot;applicationContext.xml&quot;</span>);</span><br><span class="line">        <span class="type">BeanFactory</span> <span class="variable">bf</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">XmlBeanFactory</span>(resources);</span><br><span class="line">        <span class="type">BookDao</span> <span class="variable">bookDao</span> <span class="operator">=</span> bf.getBean(BookDao.class);</span><br><span class="line">        bookDao.save();</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></p><p>为了更好的看出<code>BeanFactory</code>和<code>ApplicationContext</code>之间的区别，在BookDaoImpl添加如下构造函数<br><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><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_">BookDaoImpl</span> <span class="keyword">implements</span> <span class="title class_">BookDao</span> &#123;</span><br><span class="line">    <span class="keyword">public</span> <span class="title function_">BookDaoImpl</span><span class="params">()</span> &#123;</span><br><span class="line">        System.out.println(<span class="string">&quot;constructor&quot;</span>);</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">save</span><span class="params">()</span> &#123;</span><br><span class="line">        System.out.println(<span class="string">&quot;book dao save ...&quot;</span> );</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></p><p>如果不去获取bean对象，打印会发现：</p><ul><li>BeanFactory是延迟加载，只有在获取bean对象的时候才会去创建</li><li>ApplicationContext是立即加载，容器加载的时候就会创建bean对象</li><li>ApplicationContext要想成为延迟加载，只需要将lazy-init设为true<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="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">beans</span> <span class="attr">xmlns</span>=<span class="string">&quot;http://www.springframework.org/schema/beans&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;</span></span></span><br><span class="line"><span class="string"><span class="tag">            http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd&quot;</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">bean</span> <span class="attr">id</span>=<span class="string">&quot;bookDao&quot;</span> <span class="attr">class</span>=<span class="string">&quot;com.blog.dao.impl.BookDaoImpl&quot;</span>  <span class="attr">lazy-init</span>=<span class="string">&quot;true&quot;</span>/&gt;</span></span><br><span class="line"><span class="tag">&lt;/<span class="name">beans</span>&gt;</span></span><br></pre></td></tr></table></figure></li></ul><h2 id="核心容器总结"><a href="#核心容器总结" class="headerlink" title="核心容器总结"></a>核心容器总结</h2><h3 id="容器相关"><a href="#容器相关" class="headerlink" title="容器相关"></a>容器相关</h3><ul><li><code>BeanFactory</code>是IoC容器的顶层接口，初始化BeanFactory对象时，Bean对象不会被加载，只有获取Bean对象时才会创建</li><li><code>ApplicationContext</code>接口是Spring容器的核心接口，初始化时bean立即加载</li><li>ApplicationContext接口提供基础的bean操作相关方法，通过其他接口扩展其功能</li><li>ApplicationContext接口常用初始化类<ul><li>ClassPathXmlApplicationContext(常用)</li><li>FileSystemXmlApplicationContext<h3 id="bean相关"><a href="#bean相关" class="headerlink" title="bean相关"></a>bean相关</h3><div class="img-wrap"><div class="img-bg"><img class="img" src="https://raw.githubusercontent.com/silvan2077/markdown_pic/main/Picgo/20251007191225.png"/></div></div><h3 id="依赖注入"><a href="#依赖注入" class="headerlink" title="依赖注入"></a>依赖注入</h3><div class="img-wrap"><div class="img-bg"><img class="img" src="https://raw.githubusercontent.com/silvan2077/markdown_pic/main/Picgo/20251007191317.png"/></div></div></li></ul></li></ul><h1 id="Ioc-DI注解开发"><a href="#Ioc-DI注解开发" class="headerlink" title="Ioc/DI注解开发"></a>Ioc/DI注解开发</h1><p>Spring的Ioc/DI对应的使用配置文件开发使用起来还是比较复杂的，所以Spring为我们提供了注解开发，Spring对注解支持的版本历程：</p><ul><li>2.0版开始支持注解</li><li>2.5版注解功能趋于完善</li><li>3.0版支持纯注解开发</li></ul><p>关于注解开发，主要学习两部分内容<code>注解开发定义Bean</code>和<code>纯注解开发</code>。<br>注解开发定义bean用的是2.5版提供的注解，纯注解开发用的是3.0版提供的注解。</p><h2 id="环境准备-5"><a href="#环境准备-5" class="headerlink" title="环境准备"></a>环境准备</h2><ul><li>创建一个Maven项目</li><li>添加Spring的依赖<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="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<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-context<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.2.10.RELEASE<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></pre></td></tr></table></figure></li><li>新建Spring配置文件<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="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">beans</span> <span class="attr">xmlns</span>=<span class="string">&quot;http://www.springframework.org/schema/beans&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;</span></span></span><br><span class="line"><span class="string"><span class="tag">            http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd&quot;</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">bean</span> <span class="attr">id</span>=<span class="string">&quot;bookDao&quot;</span> <span class="attr">class</span>=<span class="string">&quot;com.blog.dao.impl.BookDaoImpl&quot;</span>/&gt;</span></span><br><span class="line"><span class="tag">&lt;/<span class="name">beans</span>&gt;</span></span><br></pre></td></tr></table></figure></li><li>添加BookDao和BookDaoImpl类<div class="tabs" id="核心容器"><ul class="nav-tabs"><li class="tab active"><button type="button" data-href="#核心容器-1">BookDao</button></li><li class="tab"><button type="button" data-href="#核心容器-2">BookDaoImpl</button></li></ul><div class="tab-contents"><div class="tab-item-content active" id="核心容器-1"><figure class="highlight java"><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="keyword">public</span> <span class="keyword">interface</span> <span class="title class_">BookDao</span> &#123;</span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">save</span><span class="params">()</span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><button type="button" class="tab-to-top" aria-label="scroll to top"><i class="fas fa-arrow-up"></i></button></div><div class="tab-item-content" id="核心容器-2"><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><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_">BookDaoImpl</span> <span class="keyword">implements</span> <span class="title class_">BookDao</span> &#123;</span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">save</span><span class="params">()</span> &#123;</span><br><span class="line">        System.out.println(<span class="string">&quot;book dao save ...&quot;</span> );</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><button type="button" class="tab-to-top" aria-label="scroll to top"><i class="fas fa-arrow-up"></i></button></div></div></div></li><li>创建运行类<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><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="keyword">public</span> <span class="keyword">class</span> <span class="title class_">App</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">ApplicationContext</span> <span class="variable">ctx</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">ClassPathXmlApplicationContext</span>(<span class="string">&quot;applicationContext.xml&quot;</span>);</span><br><span class="line">        <span class="type">BookDao</span> <span class="variable">bookDao</span> <span class="operator">=</span> (BookDao) ctx.getBean(<span class="string">&quot;bookDao&quot;</span>);</span><br><span class="line">        bookDao.save();</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h2 id="注解开发定义Bean"><a href="#注解开发定义Bean" class="headerlink" title="注解开发定义Bean"></a>注解开发定义Bean</h2></li></ul><ol><li>删除原有的XML配置<br>将配置文件中的<code>&lt;bean&gt;</code>标签删除掉<figure class="highlight xml"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="tag">&lt;<span class="name">bean</span> <span class="attr">id</span>=<span class="string">&quot;bookDao&quot;</span> <span class="attr">class</span>=<span class="string">&quot;com.blog.dao.impl.BookDaoImpl&quot;</span>/&gt;</span></span><br></pre></td></tr></table></figure></li><li>在Dao上添加注解<br>在BookDaoImpl类上添加<code>@Component</code>注解<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><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="meta">@Component(&quot;bookDao&quot;)</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">BookDaoImpl</span> <span class="keyword">implements</span> <span class="title class_">BookDao</span> &#123;</span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">save</span><span class="params">()</span> &#123;</span><br><span class="line">        System.out.println(<span class="string">&quot;book dao save ...&quot;</span>);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><div class="note warning no-icon flat"><p><strong>注意：@Component注解不可以添加在接口上，因为接口是无法创建对象的。</strong></p></div></li><li>配置Spring的注解包扫描<br>仅仅在类上添加@Component注解是不够的，还需要配置Spring的注解包扫描，否则Spring容器不会扫描到该类,Spring没有扫描到自然不会添加到Ioc容器中管理。<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="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">beans</span> <span class="attr">xmlns</span>=<span class="string">&quot;http://www.springframework.org/schema/beans&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">xmlns:context</span>=<span class="string">&quot;http://www.springframework.org/schema/context&quot;</span></span></span><br><span class="line"><span class="tag">       <span class="attr">xsi:schemaLocation</span>=<span class="string">&quot;</span></span></span><br><span class="line"><span class="string"><span class="tag">            http://www.springframework.org/schema/beans</span></span></span><br><span class="line"><span class="string"><span class="tag">            http://www.springframework.org/schema/beans/spring-beans.xsd</span></span></span><br><span class="line"><span class="string"><span class="tag">            http://www.springframework.org/schema/context</span></span></span><br><span class="line"><span class="string"><span class="tag">            http://www.springframework.org/schema/context/spring-context.xsd</span></span></span><br><span class="line"><span class="string"><span class="tag">        &quot;</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">context:component-scan</span> <span class="attr">base-package</span>=<span class="string">&quot;com.blog&quot;</span>/&gt;</span></span><br><span class="line"><span class="tag">&lt;/<span class="name">beans</span>&gt;</span></span><br></pre></td></tr></table></figure><div class="note info no-icon flat"><p>说明：component-scan</p><ul><li>component:组件,Spring将管理的bean视作自己的一个组件</li><li>scan:扫描</li><li>base-package指定Spring框架扫描的包路径，它会扫描指定包及其子包中的所有类上的注解。</li><li>包路径越多如:com.blog.dao.impl，扫描的范围越小速度越快</li><li>包路径越少如:com.blog,扫描的范围越大速度越慢</li><li>一般扫描到项目的组织名称即Maven的<code>groupId</code>下如:com.blog即可。</li></ul></div></li><li>运行程序<blockquote><p>book dao save …</p></blockquote></li><li>service上添加注解<br>在BookServiceImpl类上也添加<code>@Component</code>交给Spring框架管理<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><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="meta">@Component</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">BookServiceImpl</span> <span class="keyword">implements</span> <span class="title class_">BookService</span> &#123;</span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">save</span><span class="params">()</span> &#123;</span><br><span class="line">        System.out.println(<span class="string">&quot;book service save ...&quot;</span>);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></li><li>运行程序<br>在App类中从IOC容器中获取BookServiceImpl对应的bean对象<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><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="keyword">public</span> <span class="keyword">class</span> <span class="title class_">App</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">ClassPathXmlApplicationContext</span> <span class="variable">context</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">ClassPathXmlApplicationContext</span>(<span class="string">&quot;applicationContext.xml&quot;</span>);</span><br><span class="line">        <span class="comment">//按照名称获取bean</span></span><br><span class="line">        <span class="type">BookDao</span> <span class="variable">bookDao</span> <span class="operator">=</span> (BookDao) context.getBean(<span class="string">&quot;bookDao&quot;</span>);</span><br><span class="line">        <span class="comment">//按照类型获取bean</span></span><br><span class="line">        <span class="type">BookService</span> <span class="variable">bookService</span> <span class="operator">=</span> context.getBean(BookService.class);</span><br><span class="line">        bookDao.save();</span><br><span class="line">        bookService.save();</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>执行结果：<blockquote><p>book dao save …<br>book service save …</p><div class="note info no-icon flat"><p>说明:</p><ul><li>BookServiceImpl类没有起名称，所以在App中是按照类型来获取bean对象</li><li><code>@Component</code>注解如果不起名称，会有一个默认值就是当前类名首字母小写，所以也可以按照名称获取，如<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="type">BookService</span> <span class="variable">bookService</span> <span class="operator">=</span> (BookService) context.getBean(<span class="string">&quot;bookServiceImpl&quot;</span>);</span><br></pre></td></tr></table></figure></li><li>对于<code>@Component</code>注解，还衍生出了其他三个注解<code>@Controller</code>、<code>@Service</code>、<code>@Repository</code></li><li>通过查看源码会发现：这三个注解和@Component注解的作用是一样的，为什么要衍生出这三个呢?<ul><li>这是方便我们后期在编写类的时候能很好的区分出这个类是属于表现层、业务层还是数据层的类。</li></ul></li></ul></div><h2 id="纯注解开发"><a href="#纯注解开发" class="headerlink" title="纯注解开发"></a>纯注解开发</h2><p>上面已经可以使用注解来配置Bean，但仍然需要使用配置文件并添加包扫描，Spring在3.0版本推出了纯注解开发，使用Java类来代替配置文件。</p><div class="note info no-icon flat"><p>纯注解开发包括：</p><ul><li>使用<code>@Configuration</code>注解，代替配置文件</li><li>使用<code>@ComponentScan</code>注解，代替包扫描</li><li>使用<code>@Component</code>注解，代替Bean定义</li></ul></div><h3 id="实现步骤"><a href="#实现步骤" class="headerlink" title="实现步骤"></a>实现步骤</h3></blockquote></li><li>创建配置类<br>创建SpringConfig专门用作配置类<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="keyword">public</span> <span class="keyword">class</span> <span class="title class_">SpringConfig</span> &#123;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></li><li>标识该类为配置类<br>在配置类上添加<code>@Configuration</code>注解，标识为配置类，用来替代<code>applicationContext.xml</code><figure class="highlight java"><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="meta">@Configuration</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">SpringConfig</span> &#123;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></li><li>使用包扫描注解<br>原本配置文件中就需要添加包扫描，现在使用注解<code>@ComponentScan</code>替换<code>&lt;context:component-scan base-package=&quot;&quot;/&gt;</code><figure class="highlight java"><table><tr><td class="gutter"><pre><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="meta">@Configuration</span></span><br><span class="line"><span class="meta">@ComponentScan(&quot;com.blog&quot;)</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">SpringConfig</span> &#123;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></li><li>创建运行类并执行<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><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">public</span> <span class="keyword">class</span> <span class="title class_">AppForAnnotation</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">AnnotationConfigApplicationContext</span> <span class="variable">context</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">AnnotationConfigApplicationContext</span>(SpringConfig.class);</span><br><span class="line">        <span class="type">BookDao</span> <span class="variable">bookDao</span> <span class="operator">=</span> (BookDao) context.getBean(<span class="string">&quot;bookDao&quot;</span>);</span><br><span class="line">        bookDao.save();</span><br><span class="line">        <span class="type">BookService</span> <span class="variable">bookService</span> <span class="operator">=</span> context.getBean(BookService.class);</span><br><span class="line">        bookService.save();</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></li><li>执行结果<br>可以看到两个对象可以被成功获取<blockquote><p>book dao save …<br>book service save …</p></blockquote></li></ol><h3 id="小结-1"><a href="#小结-1" class="headerlink" title="小结"></a>小结</h3><p>纯注解开发的主要内容包括：</p><ul><li><code>@Component</code>注解可以用来定义bean</li><li><code>@Configuration</code>注解用于设定当前类为配置类</li><li><code>@ComponentScan</code>注解用于设定扫描路径，该注解只能添加一次，多个数据需要使用数组格式<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">@ComponentScan(&#123;com.blog.service&quot;,&quot;com.blog.dao&quot;&#125;)</span></span><br></pre></td></tr></table></figure></li><li>读取Spring核心配置文件初始化容器对象切换为Java配置类初始化容器对象<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="type">AnnotationConfigApplicationContext</span> <span class="variable">context</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">AnnotationConfigApplicationContext</span>(SpringConfig.class);</span><br></pre></td></tr></table></figure></li></ul><div class="tabs" id="纯注解开发小结1"><ul class="nav-tabs"><li class="tab active"><button type="button" data-href="#纯注解开发小结1-1"><i class="Configuration" style="text-align: center;"></i></button></li><li class="tab"><button type="button" data-href="#纯注解开发小结1-2"><i class="ComponentScan" style="text-align: center;"></i></button></li></ul><div class="tab-contents"><div class="tab-item-content active" id="纯注解开发小结1-1"><div class="table-container"><table><thead><tr><th style="text-align:center">名称</th><th style="text-align:center">@Configuration</th></tr></thead><tbody><tr><td style="text-align:center">类型</td><td style="text-align:center">类注解</td></tr><tr><td style="text-align:center">位置</td><td style="text-align:center">类定义上方</td></tr><tr><td style="text-align:center">作用</td><td style="text-align:center">设置该类为spring配置类</td></tr><tr><td style="text-align:center">属性</td><td style="text-align:center">value（默认）：定义bean的id</td></tr></tbody></table></div><button type="button" class="tab-to-top" aria-label="scroll to top"><i class="fas fa-arrow-up"></i></button></div><div class="tab-item-content" id="纯注解开发小结1-2"><div class="table-container"><table><thead><tr><th style="text-align:center">名称</th><th style="text-align:center">@ComponentScan</th></tr></thead><tbody><tr><td style="text-align:center">类型</td><td style="text-align:center">类注解</td></tr><tr><td style="text-align:center">位置</td><td style="text-align:center">类定义上方</td></tr><tr><td style="text-align:center">作用</td><td style="text-align:center">设置spring配置类扫描路径，用于加载使用注解格式定义的bean</td></tr><tr><td style="text-align:center">属性</td><td style="text-align:center">value（默认）：扫描路径，此路径可以逐层向下扫描</td></tr></tbody></table></div><button type="button" class="tab-to-top" aria-label="scroll to top"><i class="fas fa-arrow-up"></i></button></div></div></div><div class="note info no-icon flat"><ul><li>记住<code>@Component</code>、<code>@Controller</code>、<code>@Service</code>、<code>@Repository</code>这四个注解</li><li>applicationContext.xml中<code>&lt;context:component-san/&gt;</code>的作用是指定扫描包路径，注解为<code>@ComponentScan</code></li><li><code>@Configuration</code>标识该类为配置类，使用类替换<code>applicationContext.xml</code>文件</li><li><code>ClassPathXmlApplicationContext</code>是加载XML配置文件</li><li><code>AnnotationConfigApplicationContext</code>是加载配置类</li></ul></div><h2 id="注解开发Bean的作用范围和生命周期"><a href="#注解开发Bean的作用范围和生命周期" class="headerlink" title="注解开发Bean的作用范围和生命周期"></a>注解开发Bean的作用范围和生命周期</h2><ul><li>使用注解能够实现Bean的管理，根据前面学习的内容，将<code>Bean作用范围(Scope)</code>和<code>Bean生命周期(init和destroy)</code>也替换为注解实现</li></ul><h3 id="Bean的作用范围"><a href="#Bean的作用范围" class="headerlink" title="Bean的作用范围"></a>Bean的作用范围</h3><ul><li>前面提到Spring为我们创建的Bean默认是单例，使用注解创建也不例外，如果想创建多例Bean，只需要在类上添加<code>@scope</code>注解即可<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><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="meta">@Component(&quot;bookDao&quot;)</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_">BookDaoImpl</span> <span class="keyword">implements</span> <span class="title class_">BookDao</span> &#123;</span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">save</span><span class="params">()</span> &#123;</span><br><span class="line">        System.out.println(<span class="string">&quot;book dao save ...&quot;</span>);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>知识点：<code>@scope</code></li></ul><div class="table-container"><table><thead><tr><th style="text-align:center">名称</th><th style="text-align:center">@Scope</th></tr></thead><tbody><tr><td style="text-align:center">类型</td><td style="text-align:center">类注解</td></tr><tr><td style="text-align:center">位置</td><td style="text-align:center">类定义上方</td></tr><tr><td style="text-align:center">作用</td><td style="text-align:center">设置该类创建对象的作用范围，可用于设置创建出的bean是否为单例对象</td></tr><tr><td style="text-align:center">属性</td><td style="text-align:center">value（默认）：定义bean作用范围，默认值singleton（单例），可选值prototype（非单例）</td></tr></tbody></table></div><h3 id="Bean的生命周期-1"><a href="#Bean的生命周期-1" class="headerlink" title="Bean的生命周期"></a>Bean的生命周期</h3><ul><li>在BookDaoImpl中添加两个方法，init和destroy(方法名任意)<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><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="meta">@Component(&quot;bookDao&quot;)</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">BookDaoImpl</span> <span class="keyword">implements</span> <span class="title class_">BookDao</span> &#123;</span><br><span class="line">    </span><br><span class="line">    <span class="keyword">public</span> <span class="title function_">BookDaoImpl</span><span class="params">()</span> &#123;</span><br><span class="line">        System.out.println(<span class="string">&quot;construct ... &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_">save</span><span class="params">()</span> &#123;</span><br><span class="line">        System.out.println(<span class="string">&quot;book dao save ...&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_">init</span><span class="params">()</span> &#123;</span><br><span class="line">        System.out.println(<span class="string">&quot;init ... &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_">destroy</span><span class="params">()</span> &#123;</span><br><span class="line">        System.out.println(<span class="string">&quot;destroy ... &quot;</span>);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></li><li>使用注解来标注初始化方法和销毁方法只需要在对应的方法上添加<code>@PostConstruct</code>和<code>@PreDestroy</code>注解<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><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="meta">@Component(&quot;bookDao&quot;)</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">BookDaoImpl</span> <span class="keyword">implements</span> <span class="title class_">BookDao</span> &#123;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">public</span> <span class="title function_">BookDaoImpl</span><span class="params">()</span> &#123;</span><br><span class="line">        System.out.println(<span class="string">&quot;construct ... &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_">save</span><span class="params">()</span> &#123;</span><br><span class="line">        System.out.println(<span class="string">&quot;book dao save ...&quot;</span>);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@PostConstruct</span>  <span class="comment">// 在构造方法之后执行，替换 init-method</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;init ... &quot;</span>);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@PreDestroy</span> <span class="comment">// 在销毁方法之前执行,替换 destroy-method</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;destroy ... &quot;</span>);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><div class="note warning no-icon flat"><p>需要注意的是<code>destroy</code>依旧只有在容器关闭时才执行，所以需要手动调用<code>close方法</code>或<code>registerShutdownHook方法</code>来关闭容器</p></div></li></ul><div class="note danger no-icon flat"><p>JDK8版本以上，如果找不到<code>@PostConstruct</code>和<code>@PreDestroy</code>注解，需要导入下面的jar包<br><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>javax.annotation<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>javax.annotation-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>1.3.2<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><br>找不到的原因是，从JDK9以后jdk中的javax.annotation包被移除了，这两个注解刚好就在这个包中。</p></div><div class="tabs" id="bean的生命周期注解"><ul class="nav-tabs"><li class="tab active"><button type="button" data-href="#bean的生命周期注解-1"><i class="PostConstruct" style="text-align: center;"></i></button></li><li class="tab"><button type="button" data-href="#bean的生命周期注解-2"><i class="PreDestroy" style="text-align: center;"></i></button></li></ul><div class="tab-contents"><div class="tab-item-content active" id="bean的生命周期注解-1"><div class="table-container"><table><thead><tr><th style="text-align:center">名称</th><th style="text-align:center">@PostConstruct</th></tr></thead><tbody><tr><td style="text-align:center">类型</td><td style="text-align:center">方法注解</td></tr><tr><td style="text-align:center">位置</td><td style="text-align:center">方法上</td></tr><tr><td style="text-align:center">作用</td><td style="text-align:center">设置该方法为初始化方法</td></tr><tr><td style="text-align:center">属性</td><td style="text-align:center">无</td></tr></tbody></table></div><button type="button" class="tab-to-top" aria-label="scroll to top"><i class="fas fa-arrow-up"></i></button></div><div class="tab-item-content" id="bean的生命周期注解-2"><div class="table-container"><table><thead><tr><th style="text-align:center">名称</th><th style="text-align:center">@PreDestroy</th></tr></thead><tbody><tr><td style="text-align:center">类型</td><td style="text-align:center">方法注解</td></tr><tr><td style="text-align:center">位置</td><td style="text-align:center">方法上</td></tr><tr><td style="text-align:center">作用</td><td style="text-align:center">设置该方法为销毁方法</td></tr><tr><td style="text-align:center">属性</td><td style="text-align:center">无</td></tr></tbody></table></div><button type="button" class="tab-to-top" aria-label="scroll to top"><i class="fas fa-arrow-up"></i></button></div></div></div><h3 id="小结-2"><a href="#小结-2" class="headerlink" title="小结"></a>小结</h3><div class="note info no-icon flat"><p>配置文件中的bean标签中的</p><ul><li><code>id</code>对应<code>@Component(&quot;&quot;)</code>，<code>@Controller(&quot;&quot;)</code>，<code>@Service(&quot;&quot;)</code>，<code>@Repository(&quot;&quot;)</code></li><li><code>scope</code>对应<code>@scope()</code></li><li><code>init-method</code>对应<code>@PostConstruct</code></li><li><code>destroy-method</code>对应<code>@PreDestroy</code></li></ul></div><h2 id="注解开发依赖注入"><a href="#注解开发依赖注入" class="headerlink" title="注解开发依赖注入"></a>注解开发依赖注入</h2><p>Spring为了使用注解简化开发，并没有提供构造函数注入、setter注入对应的注解，只提供了自动装配的注解实现。</p><h3 id="环境准备-6"><a href="#环境准备-6" class="headerlink" title="环境准备"></a>环境准备</h3><ul><li>创建Maven项目</li><li>添加Spring依赖</li><li>添加配置类<code>SpringConfig</code><figure class="highlight java"><table><tr><td class="gutter"><pre><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="meta">@Configuration</span></span><br><span class="line"><span class="meta">@ComponentScan(&quot;com.blog&quot;)</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">SpringConfig</span> &#123;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></li><li>添加BookDao、BookDaoImpl、BookService、BookServiceImpl类<div class="tabs" id="注解开发依赖注入"><ul class="nav-tabs"><li class="tab active"><button type="button" data-href="#注解开发依赖注入-1">BookDao</button></li><li class="tab"><button type="button" data-href="#注解开发依赖注入-2">BookDaoImpl</button></li><li class="tab"><button type="button" data-href="#注解开发依赖注入-3">BookService</button></li><li class="tab"><button type="button" data-href="#注解开发依赖注入-4">BookServiceImpl</button></li></ul><div class="tab-contents"><div class="tab-item-content active" id="注解开发依赖注入-1"><figure class="highlight java"><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="keyword">public</span> <span class="keyword">interface</span> <span class="title class_">BookDao</span> &#123;</span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">save</span><span class="params">()</span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><button type="button" class="tab-to-top" aria-label="scroll to top"><i class="fas fa-arrow-up"></i></button></div><div class="tab-item-content" id="注解开发依赖注入-2"><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><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="meta">@Repository</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">BookDaoImpl</span> <span class="keyword">implements</span> <span class="title class_">BookDao</span> &#123;</span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">save</span><span class="params">()</span> &#123;</span><br><span class="line">        System.out.println(<span class="string">&quot;book dao save ...&quot;</span> );</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><button type="button" class="tab-to-top" aria-label="scroll to top"><i class="fas fa-arrow-up"></i></button></div><div class="tab-item-content" id="注解开发依赖注入-3"><figure class="highlight java"><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="keyword">public</span> <span class="keyword">interface</span> <span class="title class_">BookService</span> &#123;</span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">save</span><span class="params">()</span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><button type="button" class="tab-to-top" aria-label="scroll to top"><i class="fas fa-arrow-up"></i></button></div><div class="tab-item-content" id="注解开发依赖注入-4"><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><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="meta">@Service</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">BookServiceImpl</span> <span class="keyword">implements</span> <span class="title class_">BookService</span> &#123;</span><br><span class="line">    <span class="keyword">private</span> BookDao bookDao;</span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">setBookDao</span><span class="params">(BookDao bookDao)</span> &#123;</span><br><span class="line">        <span class="built_in">this</span>.bookDao = bookDao;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">save</span><span class="params">()</span> &#123;</span><br><span class="line">        System.out.println(<span class="string">&quot;book service save ...&quot;</span>);</span><br><span class="line">        bookDao.save();</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><button type="button" class="tab-to-top" aria-label="scroll to top"><i class="fas fa-arrow-up"></i></button></div></div></div></li><li>创建运行类App<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><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="keyword">public</span> <span class="keyword">class</span> <span class="title class_">App</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">AnnotationConfigApplicationContext</span> <span class="variable">ctx</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">AnnotationConfigApplicationContext</span>(SpringConfig.class);</span><br><span class="line">        <span class="type">BookService</span> <span class="variable">bookService</span> <span class="operator">=</span> ctx.getBean(BookService.class);</span><br><span class="line">        bookService.save();</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></li></ul><div class="note info no-icon flat"><p>此时这些环境准备好后，直接运行依旧会爆出异常，因为还没有提供配置为BookService中的BookDao赋值，所以BookDao对象为null</p></div><h3 id="按类型注入-注解"><a href="#按类型注入-注解" class="headerlink" title="按类型注入(注解)"></a>按类型注入(注解)</h3><ul><li>在BookServiceImpl类的bookDao属性上添加@Autowired注解<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><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="keyword">public</span> <span class="keyword">class</span> <span class="title class_">BookServiceImpl</span> <span class="keyword">implements</span> <span class="title class_">BookService</span> &#123;</span><br><span class="line">    <span class="meta">@Autowired</span></span><br><span class="line">    <span class="keyword">private</span> BookDao bookDao;</span><br><span class="line"></span><br><span class="line"><span class="comment">//    public void setBookDao(BookDao bookDao) &#123;</span></span><br><span class="line"><span class="comment">//        this.bookDao = bookDao;</span></span><br><span class="line"><span class="comment">//    &#125;</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">()</span> &#123;</span><br><span class="line">        System.out.println(<span class="string">&quot;book service save ...&quot;</span>);</span><br><span class="line">        bookDao.save();</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><div class="note warning no-icon flat"><p>注意：</p><ul><li>@Autowired可以写在属性上，也可也写在setter方法上，<strong>最简单的处理方式是写在属性上并将setter方法删除掉</strong></li><li>为什么setter方法可以删除呢?<ul><li>自动装配基于反射设计创建对象并通过<code>暴力反射</code>为私有属性进行设值</li><li>普通反射只能获取public修饰的内容,暴力反射除了获取public修饰的内容还可以获取private修改的内容，所以此处无需提供setter方法</li></ul></li></ul></div></li><li><code>Autowired</code>按照类型注入，当对应BookDao接口有多个实现类时，比如添加BookDaoImpl2<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><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="meta">@Repository</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">BookDaoImpl2</span> <span class="keyword">implements</span> <span class="title class_">BookDao</span> &#123;</span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">save</span><span class="params">()</span> &#123;</span><br><span class="line">        System.out.println(<span class="string">&quot;book dao save ...2&quot;</span>);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></li><li>此时再次运行App，就会报错<code>NoUniqueBeanDefinitionException</code>。</li><li>根据之前的学习，此时应当按照名称注入</li><li>先给两个Dao类分别起个名称<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><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">@Repository(&quot;bookDao&quot;)</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">BookDaoImpl</span> <span class="keyword">implements</span> <span class="title class_">BookDao</span> &#123;</span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">save</span><span class="params">()</span> &#123;</span><br><span class="line">        System.out.println(<span class="string">&quot;book dao save ...&quot;</span> );</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br><span class="line"><span class="meta">@Repository(&quot;bookDao2&quot;)</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">BookDaoImpl2</span> <span class="keyword">implements</span> <span class="title class_">BookDao</span> &#123;</span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">save</span><span class="params">()</span> &#123;</span><br><span class="line">        System.out.println(<span class="string">&quot;book dao save ...2&quot;</span> );</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></li><li>突然发现此时就能注入成功，这是为什么?</li></ul><div class="note danger no-icon flat"><p>关键点：<code>@Autowired</code>的装配规则</p><ol><li><strong>先按类型（byType）装配</strong><br>Spring 会查找容器中所有实现了 <code>BookDao</code> 接口的 Bean。</li><li><strong>如果找到多个候选 Bean（类型相同）</strong><br>此时 Spring 会进入第二步——<strong>按名称（byName）匹配</strong>。<ul><li>Spring 会拿被注入属性的名称（这里是 <code>userServiceImplB</code>）</li><li>去和 Bean 的 <strong>id / name</strong> 进行匹配。</li></ul></li><li><strong>匹配成功则注入成功</strong><br>如果有一个 Bean 的名字和属性名一致，就会被选中注入。</li><li><strong>如果还是没有匹配成功</strong><br>则会抛出异常：<code>NoUniqueBeanDefinitionException</code></li></ol></div><h3 id="按名称注入-注解"><a href="#按名称注入-注解" class="headerlink" title="按名称注入(注解)"></a>按名称注入(注解)</h3><p>当根据类型在容器中找到多个bean,注入参数的属性名又和容器中bean的名称不一致，这个时候该如何解决，就需要使用到<code>@Qualifier</code>来指定注入哪个名称的bean对象。<code>@Qualifier</code>注解后的值就是需要注入的bean的名称。<br><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><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="meta">@Service</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">BookServiceImpl</span> <span class="keyword">implements</span> <span class="title class_">BookService</span> &#123;</span><br><span class="line">    <span class="meta">@Autowired</span></span><br><span class="line">    <span class="meta">@Qualifier(&quot;bookDao1&quot;)</span></span><br><span class="line">    <span class="keyword">private</span> BookDao bookDao;</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">()</span> &#123;</span><br><span class="line">        System.out.println(<span class="string">&quot;book service save ...&quot;</span>);</span><br><span class="line">        bookDao.save();</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><br><div class="note warning no-icon flat"><p>注意:@Qualifier不能独立使用，必须和@Autowired一起使用</p></div></p><h3 id="简单类型注入"><a href="#简单类型注入" class="headerlink" title="简单类型注入"></a>简单类型注入</h3><ul><li>Spring提供的自动装配只能对引用类型进行装配，不过Spring也为我们提供了简单类型装配的注解——<code>@Value</code>。</li><li><p>使用<code>@Value</code>注解为name属性赋值</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">@Repository</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">BookDaoImpl</span> <span class="keyword">implements</span> <span class="title class_">BookDao</span> &#123;</span><br><span class="line">    <span class="meta">@Value(&quot;Stephen&quot;)</span></span><br><span class="line">    <span class="keyword">private</span> String name;</span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">save</span><span class="params">()</span> &#123;</span><br><span class="line">        System.out.println(<span class="string">&quot;book dao save ...&quot;</span> + name);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></li><li><p>看到这个注解的使用方式，可以会有疑问，都直接在属性上标了那为什么不直接赋值呢？因为可以在注解中使用占位符读取外部的properties配置文件</p></li></ul><h3 id="读取properties配置文件"><a href="#读取properties配置文件" class="headerlink" title="读取properties配置文件"></a>读取properties配置文件</h3><p><code>@Value</code>一般会被用在从properties配置文件中读取内容进行使用，具体如何实现?</p><ul><li><code>步骤一：</code>准备一个properties文件<figure class="highlight properties"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="attr">name</span>=<span class="string">Stephen</span></span><br></pre></td></tr></table></figure></li></ul><ul><li><p><code>步骤二：</code>使用注解加载properties配置文件<br>在配置类上添加<code>@PropertySource</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></pre></td><td class="code"><pre><span class="line"><span class="meta">@Configuration</span></span><br><span class="line"><span class="meta">@ComponentScan(&quot;com.blog&quot;)</span></span><br><span class="line"><span class="meta">@PropertySource(&quot;jdbc.properties&quot;)</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">SpringConfig</span> &#123;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></li><li><p><code>步骤三：</code>使用@Value读取配置文件中的内容      </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">@Repository</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">BookDaoImpl</span> <span class="keyword">implements</span> <span class="title class_">BookDao</span> &#123;</span><br><span class="line">    <span class="meta">@Value(&quot;$&#123;name&#125;&quot;)</span></span><br><span class="line">    <span class="keyword">private</span> String name;</span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">save</span><span class="params">()</span> &#123;</span><br><span class="line">        System.out.println(<span class="string">&quot;book dao save ...&quot;</span> + name);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></li><li><p><code>步骤四：</code>运行程序<br>运行App类，查看运行结果，说明配置文件中的内容已经被加载</p></li></ul><blockquote><p>book service save …<br>book dao save …Stephen<br><div class="note warning no-icon flat"><p><strong>注意:</strong></p><ul><li>如果读取的properties配置文件有多个，可以使用<code>@PropertySource</code>来指定多个<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">@PropertySource(&#123;&quot;jdbc.properties&quot;,&quot;xxx.properties&quot;&#125;)</span></span><br></pre></td></tr></table></figure><code>@PropertySource</code>注解属性中不支持使用通配符<code>*</code>,运行会报错<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">@PropertySource(&#123;&quot;*.properties&quot;&#125;)</span></span><br></pre></td></tr></table></figure><code>@PropertySource</code>注解属性中可以把<code>classpath:</code>加上,代表从当前项目的根路径找文件<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">@PropertySource(&#123;&quot;classpath:jdbc.properties&quot;&#125;)</span></span><br></pre></td></tr></table></figure></li></ul></div><br><div class="tabs" id="开发依赖注入"><ul class="nav-tabs"><li class="tab active"><button type="button" data-href="#开发依赖注入-1"><i class="Autowired" style="text-align: center;"></i></button></li><li class="tab"><button type="button" data-href="#开发依赖注入-2"><i class="Qualifier" style="text-align: center;"></i></button></li><li class="tab"><button type="button" data-href="#开发依赖注入-3"><i class="Value" style="text-align: center;"></i></button></li><li class="tab"><button type="button" data-href="#开发依赖注入-4"><i class="PropertySource" style="text-align: center;"></i></button></li></ul><div class="tab-contents"><div class="tab-item-content active" id="开发依赖注入-1"><div class="table-container"><table><thead><tr><th style="text-align:center">名称</th><th style="text-align:center">@Autowired</th></tr></thead><tbody><tr><td style="text-align:center">类型</td><td style="text-align:center">属性注解 或 方法注解（了解） 或 方法形参注解（了解）</td></tr><tr><td style="text-align:center">位置</td><td style="text-align:center">属性定义上方 或 标准set方法上方 或 类set方法上方 或 方法形参前面</td></tr><tr><td style="text-align:center">作用</td><td style="text-align:center">为引用类型属性设置值</td></tr><tr><td style="text-align:center">属性</td><td style="text-align:center">required：true/false，定义该属性是否允许为null</td></tr></tbody></table></div><button type="button" class="tab-to-top" aria-label="scroll to top"><i class="fas fa-arrow-up"></i></button></div><div class="tab-item-content" id="开发依赖注入-2"><div class="table-container"><table><thead><tr><th style="text-align:center">名称</th><th style="text-align:center">@Qualifier</th></tr></thead><tbody><tr><td style="text-align:center">类型</td><td style="text-align:center">属性注解 或 方法注解（了解）</td></tr><tr><td style="text-align:center">位置</td><td style="text-align:center">属性定义上方 或 标准set方法上方 或 类set方法上方</td></tr><tr><td style="text-align:center">作用</td><td style="text-align:center">为引用类型属性指定注入的beanId</td></tr><tr><td style="text-align:center">属性</td><td style="text-align:center">value（默认）：设置注入的beanId</td></tr></tbody></table></div><button type="button" class="tab-to-top" aria-label="scroll to top"><i class="fas fa-arrow-up"></i></button></div><div class="tab-item-content" id="开发依赖注入-3"><div class="table-container"><table><thead><tr><th style="text-align:center">名称</th><th style="text-align:center">@Value</th></tr></thead><tbody><tr><td style="text-align:center">类型</td><td style="text-align:center">属性注解 或 方法注解（了解）</td></tr><tr><td style="text-align:center">位置</td><td style="text-align:center">属性定义上方 或 标准set方法上方 或 类set方法上方</td></tr><tr><td style="text-align:center">作用</td><td style="text-align:center">为 基本数据类型 或 字符串类型 属性设置值</td></tr><tr><td style="text-align:center">属性</td><td style="text-align:center">value（默认）：要注入的属性值</td></tr></tbody></table></div><button type="button" class="tab-to-top" aria-label="scroll to top"><i class="fas fa-arrow-up"></i></button></div><div class="tab-item-content" id="开发依赖注入-4"><div class="table-container"><table><thead><tr><th style="text-align:center">名称</th><th style="text-align:center">@PropertySource</th></tr></thead><tbody><tr><td style="text-align:center">类型</td><td style="text-align:center">类注解</td></tr><tr><td style="text-align:center">位置</td><td style="text-align:center">类定义上方</td></tr><tr><td style="text-align:center">作用</td><td style="text-align:center">加载properties文件中的属性值</td></tr><tr><td style="text-align:center">属性</td><td style="text-align:center">value（默认）：设置加载的properties文件对应的文件名或文件名组成的数组</td></tr></tbody></table></div><button type="button" class="tab-to-top" aria-label="scroll to top"><i class="fas fa-arrow-up"></i></button></div></div></div></p></blockquote><h1 id="IOC-DI使用注解管理第三方Bean"><a href="#IOC-DI使用注解管理第三方Bean" class="headerlink" title="IOC/DI使用注解管理第三方Bean"></a>IOC/DI使用注解管理第三方Bean</h1><ul><li>自己开发的类可以通过添加注解快速管理，但第三方类我们无法在类中添加注解，此时该如何解决？此时Spring为我们提供了一个全新的注解——<code>@Bean</code><h2 id="环境准备-7"><a href="#环境准备-7" class="headerlink" title="环境准备"></a>环境准备</h2></li><li>创建Maven项目</li><li>添加Spring依赖<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="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<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-context<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.2.10.RELEASE<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></pre></td></tr></table></figure></li><li>添加配置类<code>SpringConfig</code><figure class="highlight java"><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="meta">@Configuration</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">SpringConfig</span> &#123;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></li><li>添加BookDao、BookDaoImpl类<div class="tabs" id="注解管理"><ul class="nav-tabs"><li class="tab active"><button type="button" data-href="#注解管理-1">BookDao</button></li><li class="tab"><button type="button" data-href="#注解管理-2">BookDaoImpl</button></li></ul><div class="tab-contents"><div class="tab-item-content active" id="注解管理-1"><figure class="highlight java"><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="keyword">public</span> <span class="keyword">interface</span> <span class="title class_">BookDao</span> &#123;</span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">save</span><span class="params">()</span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><button type="button" class="tab-to-top" aria-label="scroll to top"><i class="fas fa-arrow-up"></i></button></div><div class="tab-item-content" id="注解管理-2"><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><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="meta">@Repository</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">BookDaoImpl</span> <span class="keyword">implements</span> <span class="title class_">BookDao</span> &#123;</span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">save</span><span class="params">()</span> &#123;</span><br><span class="line">        System.out.println(<span class="string">&quot;book dao save ...&quot;</span> );</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><button type="button" class="tab-to-top" aria-label="scroll to top"><i class="fas fa-arrow-up"></i></button></div></div></div></li><li><p>创建运行类App</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></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">App</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">AnnotationConfigApplicationContext</span> <span class="variable">ctx</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">AnnotationConfigApplicationContext</span>(SpringConfig.class);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h2 id="管理第三方Bean"><a href="#管理第三方Bean" class="headerlink" title="管理第三方Bean"></a>管理第三方Bean</h2><div class="note info no-icon flat"><p>在上述搭建的环境中，完成对<code>Druid</code>数据源的管理</p></div></li><li><p><code>步骤一：</code>导入对应的jar包</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.1.16<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></li></ul><ul><li><p><code>步骤二：</code>在配置类中添加一个方法<br>注意该方法的返回值就是要创建的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></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_">SpringConfig</span> &#123;</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">DruidDataSource</span> <span class="variable">dataSource</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">DruidDataSource</span>();</span><br><span class="line">        dataSource.setDriverClassName(<span class="string">&quot;com.mysql.jdbc.Driver&quot;</span>);</span><br><span class="line">        dataSource.setUrl(<span class="string">&quot;jdbc:mysql://localhost:13306/spring_db&quot;</span>);</span><br><span class="line">        dataSource.setUsername(<span class="string">&quot;root&quot;</span>);</span><br><span class="line">        dataSource.setPassword(<span class="string">&quot;PASSWORD&quot;</span>);</span><br><span class="line">        <span class="keyword">return</span> dataSource;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></li><li><p><code>步骤三：</code>在方法上添加<code>@Bean</code>注解<br><code>@Bean</code>注解的作用是将方法的返回值作为一个Spring管理的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></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_">SpringConfig</span> &#123;</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">DruidDataSource</span> <span class="variable">dataSource</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">DruidDataSource</span>();</span><br><span class="line">        dataSource.setDriverClassName(<span class="string">&quot;com.mysql.jdbc.Driver&quot;</span>);</span><br><span class="line">        dataSource.setUrl(<span class="string">&quot;jdbc:mysql://localhost:13306/spring_db&quot;</span>);</span><br><span class="line">        dataSource.setUsername(<span class="string">&quot;root&quot;</span>);</span><br><span class="line">        dataSource.setPassword(<span class="string">&quot;PASSWORD&quot;</span>);</span><br><span class="line">        <span class="keyword">return</span> dataSource;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></li><li><code>步骤四：</code>从IOC容器中获取对象并打印<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><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="keyword">public</span> <span class="keyword">class</span> <span class="title class_">App</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">AnnotationConfigApplicationContext</span> <span class="variable">ctx</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">AnnotationConfigApplicationContext</span>(SpringConfig.class);</span><br><span class="line">        <span class="type">DataSource</span> <span class="variable">dataSource</span> <span class="operator">=</span> ctx.getBean(DataSource.class);</span><br><span class="line">        System.out.println(dataSource);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></li></ul><p>输出如下</p><blockquote><p>{<br>CreateTime:”2022-09-02 10:36:33”,<br>ActiveCount:0,<br>PoolingCount:0,<br>CreateCount:0,<br>DestroyCount:0,<br>CloseCount:0,<br>ConnectCount:0,<br>Connections:[<br>]<br>}</p></blockquote><ul><li>至此使用<code>@Bean</code>来管理第三方bean的案例就已经完成。</li><li>如果有多个bean要被Spring管理，直接在配置类中多写几个方法，方法上添加@Bean注解即可。</li></ul><h2 id="引入外部配置类"><a href="#引入外部配置类" class="headerlink" title="引入外部配置类"></a>引入外部配置类</h2><p>如果把所有的第三方bean都配置到Spring的配置类<code>SpringConfig</code>中，虽然可以，但是不利于代码阅读和分类管理，所有我们就想能不能按照类别将这些bean配置到不同的配置类中?</p><p>那么对于数据源的bean，我们可以把它的配置单独放倒一个<code>JdbcConfig</code>类中<br><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><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="keyword">public</span> <span class="keyword">class</span> <span class="title class_">JdbcConfig</span> &#123;</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">DruidDataSource</span> <span class="variable">dataSource</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">DruidDataSource</span>();</span><br><span class="line">        dataSource.setDriverClassName(<span class="string">&quot;com.mysql.jdbc.Driver&quot;</span>);</span><br><span class="line">        dataSource.setUrl(<span class="string">&quot;jdbc:mysql://localhost:13306/spring_db&quot;</span>);</span><br><span class="line">        dataSource.setUsername(<span class="string">&quot;root&quot;</span>);</span><br><span class="line">        dataSource.setPassword(<span class="string">&quot;PASSWORD&quot;</span>);</span><br><span class="line">        <span class="keyword">return</span> dataSource;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></p><p>那现在又有了一个新问题，这些配置类如何能被Spring配置类加载到，并创建DataSource对象在IOC容器中?<br>针对这个问题，有两个解决方案：</p><h3 id="使用包扫描引入"><a href="#使用包扫描引入" class="headerlink" title="使用包扫描引入"></a>使用包扫描引入</h3><ul><li><code>步骤一：</code>在Spring的配置类上添加包扫描<br>注意要将JdbcConfig类放在包扫描的地址下<figure class="highlight java"><table><tr><td class="gutter"><pre><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="meta">@Configuration</span></span><br><span class="line"><span class="meta">@ComponentScan(&quot;com.blog.config&quot;)</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">SpringConfig</span> &#123;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></li><li><code>步骤二：</code>在JdbcConfig上添加<code>@Configuration</code>注解<br>JdbcConfig类要放入到<code>com.blog.config</code>包下，这样才能被Spring的配置类扫描到<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><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">@Configuration</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">JdbcConfig</span> &#123;</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">DruidDataSource</span> <span class="variable">ds</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">DruidDataSource</span>();</span><br><span class="line">        ds.setDriverClassName(<span class="string">&quot;com.mysql.jdbc.Driver&quot;</span>);</span><br><span class="line">        ds.setUrl(<span class="string">&quot;jdbc:mysql://localhost:3306/spring_db&quot;</span>);</span><br><span class="line">        ds.setUsername(<span class="string">&quot;root&quot;</span>);</span><br><span class="line">        ds.setPassword(<span class="string">&quot;root&quot;</span>);</span><br><span class="line">        <span class="keyword">return</span> ds;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></li><li><code>步骤三：</code>运行程序<br>仍然可以获取到bean对象并输出到控制台<blockquote><p>{<br>CreateTime:”2022-09-02 10:52:50”,<br>ActiveCount:0,<br>PoolingCount:0,<br>CreateCount:0,<br>DestroyCount:0,<br>CloseCount:0,<br>ConnectCount:0,<br>Connections:[<br>]<br>}</p></blockquote></li></ul><p>这种方式虽然能够扫描到，但是不能很快的知晓都引入了哪些配置类(因为把包下的所有配置类都扫描了)，所以这种方式不推荐使用。</p><h3 id="使用-Import引入"><a href="#使用-Import引入" class="headerlink" title="使用@Import引入"></a>使用@Import引入</h3><p>方案一实现起来有点小复杂，Spring早就想到了这一点，于是又给我们提供了第二种方案。<br>这种方案可以不用加<code>@Configuration</code>注解，但是必须在Spring配置类上使用<code>@Import</code>注解手动引入需要加载的配置类</p><ul><li><p><code>步骤一：</code>去除JdbcConfig类上的注解</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="keyword">public</span> <span class="keyword">class</span> <span class="title class_">JdbcConfig</span> &#123;</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">DruidDataSource</span> <span class="variable">dataSource</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">DruidDataSource</span>();</span><br><span class="line">        dataSource.setDriverClassName(<span class="string">&quot;com.mysql.jdbc.Driver&quot;</span>);</span><br><span class="line">        dataSource.setUrl(<span class="string">&quot;jdbc:mysql://localhost:13306/spring_db&quot;</span>);</span><br><span class="line">        dataSource.setUsername(<span class="string">&quot;root&quot;</span>);</span><br><span class="line">        dataSource.setPassword(<span class="string">&quot;PASSWORD&quot;</span>);</span><br><span class="line">        <span class="keyword">return</span> dataSource;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></li><li><p><code>步骤二：</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></pre></td><td class="code"><pre><span class="line"><span class="meta">@Configuration</span></span><br><span class="line"><span class="meta">@Import(JdbcConfig.class)</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">SpringConfig</span> &#123;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></li></ul><div class="note warning no-icon flat"><p><strong>注意:</strong></p><ul><li>扫描注解可以移除</li><li>@Import参数需要的是一个数组，可以引入多个配置类。</li><li>@Import注解在配置类中只能写一次</li></ul></div><ul><li><code>步骤三：</code>运行程序<br>依然能获取到bean对象并打印控制台<blockquote><p>{<br>CreateTime:”2022-09-02 11:02:12”,<br>ActiveCount:0,<br>PoolingCount:0,<br>CreateCount:0,<br>DestroyCount:0,<br>CloseCount:0,<br>ConnectCount:0,<br>Connections:[<br>]<br>}</p></blockquote></li></ul><div class="tabs" id="import"><ul class="nav-tabs"><li class="tab active"><button type="button" data-href="#import-1"><i class="Bean" style="text-align: center;"></i></button></li><li class="tab"><button type="button" data-href="#import-2"><i class="Import" style="text-align: center;"></i></button></li></ul><div class="tab-contents"><div class="tab-item-content active" id="import-1"><div class="table-container"><table><thead><tr><th style="text-align:center">名称</th><th style="text-align:center">@Bean</th></tr></thead><tbody><tr><td style="text-align:center">类型</td><td style="text-align:center">方法注解</td></tr><tr><td style="text-align:center">位置</td><td style="text-align:center">方法定义上方</td></tr><tr><td style="text-align:center">作用</td><td style="text-align:center">设置该方法的返回值作为spring管理的bean</td></tr><tr><td style="text-align:center">属性</td><td style="text-align:center">value（默认）：定义bean的id</td></tr></tbody></table></div><button type="button" class="tab-to-top" aria-label="scroll to top"><i class="fas fa-arrow-up"></i></button></div><div class="tab-item-content" id="import-2"><div class="table-container"><table><thead><tr><th style="text-align:center">名称</th><th style="text-align:center">@Import</th></tr></thead><tbody><tr><td style="text-align:center">类型</td><td style="text-align:center">类注解</td></tr><tr><td style="text-align:center">位置</td><td style="text-align:center">类定义上方</td></tr><tr><td style="text-align:center">作用</td><td style="text-align:center">导入配置类</td></tr><tr><td style="text-align:center">属性</td><td style="text-align:center">value（默认）：定义导入的配置类类名， 当配置类有多个时使用数组格式一次性导入多个配置类</td></tr></tbody></table></div><button type="button" class="tab-to-top" aria-label="scroll to top"><i class="fas fa-arrow-up"></i></button></div></div></div><h3 id="为第三方Bean注入资源"><a href="#为第三方Bean注入资源" class="headerlink" title="为第三方Bean注入资源"></a>为第三方Bean注入资源</h3><p>前面学习到使用<code>@Value</code>搭配<code>$&#123;&#125;</code>来注入<code>简单数据</code>,以及使用<code>@Autowired</code>来注入<code>引用数据</code>，对于第三方bean，如何为它注入属性呢?</p><h4 id="简单数据类型"><a href="#简单数据类型" class="headerlink" title="简单数据类型"></a>简单数据类型</h4><ul><li>注入简单数据类型的方法跟自己定义的bean一样，使用<code>@Value</code>搭配<code>$&#123;&#125;</code>来注入即可<div class="tabs" id="第三方注入简单数据类型"><ul class="nav-tabs"><li class="tab active"><button type="button" data-href="#第三方注入简单数据类型-1">配置文件</button></li><li class="tab"><button type="button" data-href="#第三方注入简单数据类型-2">JdbcConfig</button></li></ul><div class="tab-contents"><div class="tab-item-content active" id="第三方注入简单数据类型-1"><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></pre></td><td class="code"><pre><span class="line"><span class="attr">jdbc.driver</span>=<span class="string">com.mysql.jdbc.Driver</span></span><br><span class="line"><span class="attr">jdbc.url</span>=<span class="string">jdbc:mysql://localhost:13306/spring_db</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">PASSWORD.</span></span><br></pre></td></tr></table></figure><button type="button" class="tab-to-top" aria-label="scroll to top"><i class="fas fa-arrow-up"></i></button></div><div class="tab-item-content" id="第三方注入简单数据类型-2"><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><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="meta">@PropertySource(&quot;jdbc.properties&quot;)</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">JdbcConfig</span> &#123;</span><br><span class="line">    <span class="meta">@Value(&quot;$&#123;jdbc.driver&#125;&quot;)</span></span><br><span class="line">    <span class="keyword">private</span> String driver;</span><br><span class="line">    <span class="meta">@Value(&quot;$&#123;jdbc.url&#125;&quot;)</span></span><br><span class="line">    <span class="keyword">private</span> String url;</span><br><span class="line">    <span class="meta">@Value(&quot;$&#123;jdbc.username&#125;&quot;)</span></span><br><span class="line">    <span class="keyword">private</span> String username;</span><br><span class="line">    <span class="meta">@Value(&quot;$&#123;jdbc.password&#125;&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="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">DruidDataSource</span> <span class="variable">dataSource</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">DruidDataSource</span>();</span><br><span class="line">        dataSource.setDriverClassName(driver);</span><br><span class="line">        dataSource.setUrl(url);</span><br><span class="line">        dataSource.setUsername(username);</span><br><span class="line">        dataSource.setPassword(password);</span><br><span class="line">        <span class="keyword">return</span> dataSource;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><button type="button" class="tab-to-top" aria-label="scroll to top"><i class="fas fa-arrow-up"></i></button></div></div></div><h4 id="引用数据类型"><a href="#引用数据类型" class="headerlink" title="引用数据类型"></a>引用数据类型</h4><div class="note info no-icon flat"><p>需求：假设在构建DataSource对象时，需要用到BookDao对象，该如何注入BookDao对象呢?</p></div></li><li>对第三方注入引用数据类型的对象则更加方便，只需要为方法添加需要注入的Bean作为参数即可，容器会<code>根据类型</code>自动装配<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><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">@Bean</span></span><br><span class="line"><span class="keyword">public</span> DataSource <span class="title function_">dataSource</span><span class="params">(BookDao bookDao)</span> &#123;</span><br><span class="line">    bookDao.save();</span><br><span class="line">    <span class="type">DruidDataSource</span> <span class="variable">dataSource</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">DruidDataSource</span>();</span><br><span class="line">    dataSource.setDriverClassName(driver);</span><br><span class="line">    dataSource.setUrl(url);</span><br><span class="line">    dataSource.setUsername(username);</span><br><span class="line">    dataSource.setPassword(password);</span><br><span class="line">    <span class="keyword">return</span> dataSource;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h4 id="注解开发总结"><a href="#注解开发总结" class="headerlink" title="注解开发总结"></a>注解开发总结</h4><div class="img-wrap"><div class="img-bg"><img class="img" src="https://raw.githubusercontent.com/silvan2077/markdown_pic/main/Picgo/20251009153205.png"/></div></div><h1 id="Spring整合"><a href="#Spring整合" class="headerlink" title="Spring整合"></a>Spring整合</h1><h2 id="Spring整合MyBatis"><a href="#Spring整合MyBatis" class="headerlink" title="Spring整合MyBatis"></a>Spring整合MyBatis</h2><h3 id="准备环境"><a href="#准备环境" class="headerlink" title="准备环境"></a>准备环境</h3></li></ul><ul><li><code>步骤一：</code>准备数据库表<br>MyBatis是用来操作数据库表的，所以先来创建库和表</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></pre></td><td class="code"><pre><span class="line">create database spring_db character set utf8;</span><br><span class="line">use spring_db;</span><br><span class="line">create table <span class="title function_">tbl_account</span><span class="params">(</span></span><br><span class="line"><span class="params">    id <span class="type">int</span> primary key auto_increment,</span></span><br><span class="line"><span class="params">    name varchar(<span class="number">35</span>)</span>,</span><br><span class="line">    money <span class="type">double</span></span><br><span class="line">);  </span><br><span class="line">INSERT INTO <span class="title function_">tbl_account</span><span class="params">(`name`,money)</span> VALUES</span><br><span class="line">(<span class="string">&#x27;Tom&#x27;</span>,<span class="number">2800</span>),</span><br><span class="line">(<span class="string">&#x27;Jerry&#x27;</span>,<span class="number">3000</span>),</span><br><span class="line">(<span class="string">&#x27;Jhon&#x27;</span>,<span class="number">3100</span>);</span><br></pre></td></tr></table></figure><ul><li><p><code>步骤二：</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></pre></td><td class="code"><pre><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>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-java<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.1.46<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.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>3.5.6<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>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.1.16<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.springframework<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-context<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.2.10.RELEASE<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></pre></td></tr></table></figure></li><li><p><code>步骤三：</code><br>根据表创建模型类</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></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">Account</span> &#123;</span><br><span class="line">    <span class="keyword">private</span> Integer id;</span><br><span class="line">    <span class="keyword">private</span> String name;</span><br><span class="line">    <span class="keyword">private</span> Double money;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">public</span> <span class="title function_">Account</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_">Account</span><span class="params">(Integer id, String name, <span class="type">double</span> money)</span> &#123;</span><br><span class="line">        <span class="built_in">this</span>.id = id;</span><br><span class="line">        <span class="built_in">this</span>.name = name;</span><br><span class="line">        <span class="built_in">this</span>.money = money;</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_">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">(Integer 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_">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">double</span> <span class="title function_">getMoney</span><span class="params">()</span> &#123;</span><br><span class="line">        <span class="keyword">return</span> money;</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_">setMoney</span><span class="params">(<span class="type">double</span> money)</span> &#123;</span><br><span class="line">        <span class="built_in">this</span>.money = money;</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;Account&#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;, name=&#x27;&quot;</span> + name + <span class="string">&#x27;\&#x27;&#x27;</span> +</span><br><span class="line">                <span class="string">&quot;, money=&quot;</span> + money +</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></li><li><p><code>步骤四：</code>创建Dao接口（在之前是Mapper接口，且要配置一个对应的xml文件，不过这里没涉及到复杂的sql语句，所以没配置xml文件，采用注解开发）</p></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></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">interface</span> <span class="title class_">AccountDao</span> &#123;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Insert(&quot;insert into tbl_account(name, money) VALUES (#&#123;name&#125;, #&#123;money&#125;)&quot;)</span></span><br><span class="line">    <span class="keyword">void</span> <span class="title function_">save</span><span class="params">(Account account)</span>;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Delete(&quot;delete from tbl_account where id = #&#123;id&#125;&quot;)</span></span><br><span class="line">    <span class="keyword">void</span> <span class="title function_">delete</span><span class="params">(Integer id)</span>;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Update(&quot;update tbl_account set `name` = #&#123;name&#125;, money = #&#123;money&#125;&quot;)</span></span><br><span class="line">    <span class="keyword">void</span> <span class="title function_">update</span><span class="params">(Account account)</span>;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Select(&quot;select * from tbl_account&quot;)</span></span><br><span class="line">    List&lt;Account&gt; <span class="title function_">findAll</span><span class="params">()</span>;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Select(&quot;select * from tbl_account where id = #&#123;id&#125;&quot;)</span></span><br><span class="line">    Account <span class="title function_">findById</span><span class="params">(Integer id)</span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><ul><li><p><code>步骤五：</code>创建Service接口和实现类</p></li><li><p>AccountService</p></li></ul><div class="tabs" id=""><ul class="nav-tabs"><li class="tab active"><button type="button" data-href="#-1">AccountService</button></li><li class="tab"><button type="button" data-href="#-2">AccountServiceImpl</button></li></ul><div class="tab-contents"><div class="tab-item-content active" id="-1"><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><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="keyword">public</span> <span class="keyword">interface</span> <span class="title class_">AccountService</span> &#123;</span><br><span class="line">    <span class="keyword">void</span> <span class="title function_">save</span><span class="params">(Account account)</span>;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">void</span> <span class="title function_">delete</span><span class="params">(Integer id)</span>;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">void</span> <span class="title function_">update</span><span class="params">(Account account)</span>;</span><br><span class="line"></span><br><span class="line">    List&lt;Account&gt; <span class="title function_">findAll</span><span class="params">()</span>;</span><br><span class="line"></span><br><span class="line">    Account <span class="title function_">findById</span><span class="params">(Integer id)</span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><button type="button" class="tab-to-top" aria-label="scroll to top"><i class="fas fa-arrow-up"></i></button></div><div class="tab-item-content" id="-2"><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><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="meta">@Service</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">AccountServiceImpl</span> <span class="keyword">implements</span> <span class="title class_">AccountService</span> &#123;</span><br><span class="line">    <span class="meta">@Autowired</span></span><br><span class="line">    <span class="keyword">private</span> AccountDao accountDao;</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">(Account account)</span> &#123;</span><br><span class="line">        accountDao.save(account);</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_">delete</span><span class="params">(Integer id)</span> &#123;</span><br><span class="line">        accountDao.delete(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_">update</span><span class="params">(Account account)</span> &#123;</span><br><span class="line">        accountDao.update(account);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">public</span> List&lt;Account&gt; <span class="title function_">findAll</span><span class="params">()</span> &#123;</span><br><span class="line">        <span class="keyword">return</span> accountDao.findAll();</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">public</span> Account <span class="title function_">findById</span><span class="params">(Integer id)</span> &#123;</span><br><span class="line">        <span class="keyword">return</span> accountDao.findById(id);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><button type="button" class="tab-to-top" aria-label="scroll to top"><i class="fas fa-arrow-up"></i></button></div></div></div><ul><li><p><code>步骤六：</code>添加jdbc.properties文件</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></pre></td><td class="code"><pre><span class="line"><span class="attr">jdbc.driver</span>=<span class="string">com.mysql.jdbc.Driver</span></span><br><span class="line"><span class="attr">jdbc.url</span>=<span class="string">jdbc:mysql://localhost:13306/spring_db</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">PASSWORD.</span></span><br></pre></td></tr></table></figure></li><li><p><code>步骤七：</code>添加Mybatis核心配置文件</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></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></span><br><span class="line"><span class="meta">        <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 class="comment">&lt;!--读取外部properties配置文件--&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 class="tag">&lt;/<span class="name">properties</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">typeAliases</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.blog.domain&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 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;mysql&quot;</span>&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;mysql&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 class="tag">&lt;/<span class="name">transactionManager</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 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;url&quot;</span> <span class="attr">value</span>=<span class="string">&quot;$&#123;jdbc.url&#125;&quot;</span>&gt;</span><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;username&quot;</span> <span class="attr">value</span>=<span class="string">&quot;$&#123;jdbc.username&#125;&quot;</span>&gt;</span><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;password&quot;</span> <span class="attr">value</span>=<span class="string">&quot;$&#123;jdbc.password&#125;&quot;</span>&gt;</span><span class="tag">&lt;/<span class="name">property</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 class="comment">&lt;!--映射文件扫描包路径--&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="tag">&lt;<span class="name">package</span> <span class="attr">name</span>=<span class="string">&quot;com.blog.dao&quot;</span>&gt;</span><span class="tag">&lt;/<span class="name">package</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 class="tag">&lt;/<span class="name">configuration</span>&gt;</span></span><br></pre></td></tr></table></figure></li><li><p><code>步骤八：</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></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">App</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> IOException &#123;</span><br><span class="line">        <span class="comment">// 1. 创建SqlSessionFactoryBuilder对象</span></span><br><span class="line">        <span class="type">SqlSessionFactoryBuilder</span> <span class="variable">sqlSessionFactoryBuilder</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">SqlSessionFactoryBuilder</span>();</span><br><span class="line">        <span class="comment">// 2. 加载mybatis-config.xml配置文件</span></span><br><span class="line">        <span class="type">InputStream</span> <span class="variable">inputStream</span> <span class="operator">=</span> Resources.getResourceAsStream(<span class="string">&quot;mybatis-config.xml&quot;</span>);</span><br><span class="line">        <span class="comment">// 3. 创建SqlSessionFactory对象</span></span><br><span class="line">        <span class="type">SqlSessionFactory</span> <span class="variable">factory</span> <span class="operator">=</span> sqlSessionFactoryBuilder.build(inputStream);</span><br><span class="line">        <span class="comment">// 4. 获取SqlSession</span></span><br><span class="line">        <span class="type">SqlSession</span> <span class="variable">sqlSession</span> <span class="operator">=</span> factory.openSession();</span><br><span class="line">        <span class="comment">// 5. 获取mapper</span></span><br><span class="line">        <span class="type">AccountDao</span> <span class="variable">mapper</span> <span class="operator">=</span> sqlSession.getMapper(AccountDao.class);</span><br><span class="line">        <span class="comment">//6. 执行方法进行查询</span></span><br><span class="line">        <span class="type">Account</span> <span class="variable">account</span> <span class="operator">=</span> mapper.findById(<span class="number">2</span>);</span><br><span class="line">        System.out.println(account);</span><br><span class="line">        <span class="comment">//7. 释放资源</span></span><br><span class="line">        sqlSession.close();</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></li><li><p><code>步骤九：</code>运行程序，结果如下</p><blockquote><p>Account{id=2, name=’Jerry’, money=3000.0}</p></blockquote></li></ul><h3 id="思路分析"><a href="#思路分析" class="headerlink" title="思路分析"></a>思路分析</h3><p>Mybatis的基础环境我们已经准备好了，接下来就得分析在上述的内容中，哪些对象可以交给Spring来管理?</p><ul><li>Mybatis程序核心对象分析<br>从图中可以获取到，真正需要交给Spring管理的是SqlSessionFactory<div class="img-wrap"><div class="img-bg"><img class="img" src="https://raw.githubusercontent.com/silvan2077/markdown_pic/main/Picgo/20251009154522.png"/></div></div></li><li>整合Mybatis，就是将Mybatis用到的内容交给Spring管理，分析下配置文件<div class="img-wrap"><div class="img-bg"><img class="img" src="https://raw.githubusercontent.com/silvan2077/markdown_pic/main/Picgo/20251009154638.png"/></div></div></li></ul><div class="note info no-icon flat"><p>说明:</p><ul><li>第一部分读取外部properties配置文件，Spring有提供具体的解决方案<code>@PropertySource</code>,需要交给Spring</li><li>第二部分起别名包扫描，为SqlSessionFactory服务的，需要交给Spring</li><li>第三部分主要用于做连接池，Spring之前我们已经整合了Druid连接池，这块也需要交给Spring</li><li>前面三部分一起都是为了创建SqlSession对象用的，那么用Spring管理SqlSession对象吗?回忆下SqlSession是由SqlSessionFactory创建出来的，所以只需要将SqlSessionFactory交给Spring管理即可。</li><li>第四部分是Mapper接口和映射文件[如果使用注解就没有该映射文件]，这个是在获取到SqlSession以后执行具体操作的时候用，所以它和SqlSessionFactory创建的时机都不在同一个时间，可能需要单独管理。</li></ul></div><h3 id="整合步骤"><a href="#整合步骤" class="headerlink" title="整合步骤"></a>整合步骤</h3><p>整合Spring与Mybatis大体需要做两件事，</p><ul><li>第一件：Spring要管理MyBatis中的SqlSessionFactory</li><li>第二件：Spring要管理Mapper接口的扫描</li></ul><p>那我们下面就开始来整合</p><ul><li><p><code>步骤一：</code>项目中导入整合需要的jar包</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="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-spring<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.3.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">dependency</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">groupId</span>&gt;</span>org.springframework<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-jdbc<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.2.10.RELEASE<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></li><li><p><code>步骤二：</code>创建Spring的主配置类</p></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="meta">@Configuration</span></span><br><span class="line"><span class="comment">//包扫描，主要扫描的是项目中的AccountServiceImpl类</span></span><br><span class="line"><span class="meta">@ComponentScan(&quot;com.blog&quot;)</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">SpringConfig</span> &#123;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><ul><li><code>步骤三：</code><br>创建数据源的配置类<br>在配置类中完成数据源的创建</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></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">JdbcConfig</span> &#123;</span><br><span class="line">    <span class="meta">@Value(&quot;$&#123;jdbc.driver&#125;&quot;)</span></span><br><span class="line">    <span class="keyword">private</span> String driver;</span><br><span class="line">    <span class="meta">@Value(&quot;$&#123;jdbc.url&#125;&quot;)</span></span><br><span class="line">    <span class="keyword">private</span> String url;</span><br><span class="line">    <span class="meta">@Value(&quot;$&#123;jdbc.username&#125;&quot;)</span></span><br><span class="line">    <span class="keyword">private</span> String username;</span><br><span class="line">    <span class="meta">@Value(&quot;$&#123;jdbc.password&#125;&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="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">DruidDataSource</span> <span class="variable">dataSource</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">DruidDataSource</span>();</span><br><span class="line">        dataSource.setDriverClassName(driver);</span><br><span class="line">        dataSource.setUrl(url);</span><br><span class="line">        dataSource.setUsername(username);</span><br><span class="line">        dataSource.setPassword(password);</span><br><span class="line">        <span class="keyword">return</span> dataSource;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><ul><li><code>步骤四：</code>主配置类中读properties并引入数据源配置类</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="meta">@Configuration</span></span><br><span class="line"><span class="meta">@ComponentScan(&quot;com.blog&quot;)</span></span><br><span class="line"><span class="meta">@PropertySource(&quot;jdbc.properties&quot;)</span></span><br><span class="line"><span class="meta">@Import(JdbcConfig.class)</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">SpringConfig</span> &#123;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><ul><li><code>步骤五：</code>创建Mybatis配置类并配置SqlSessionFactory</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></pre></td><td class="code"><pre><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> SqlSessionFactoryBean <span class="title function_">sqlSessionFactory</span><span class="params">(DataSource dataSource)</span> &#123;</span><br><span class="line">        <span class="comment">//定义bean，SqlSessionFactoryBean，用于产生SqlSessionFactory对象</span></span><br><span class="line">        <span class="type">SqlSessionFactoryBean</span> <span class="variable">sqlSessionFactory</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">SqlSessionFactoryBean</span>();</span><br><span class="line">        <span class="comment">//设置模型类的别名扫描</span></span><br><span class="line">        sqlSessionFactory.setTypeAliasesPackage(<span class="string">&quot;com.blog.domain&quot;</span>);</span><br><span class="line">        <span class="comment">//设置数据源</span></span><br><span class="line">        sqlSessionFactory.setDataSource(dataSource);</span><br><span class="line">        <span class="keyword">return</span> sqlSessionFactory;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="comment">//定义bean，返回MapperScannerConfigurer对象</span></span><br><span class="line">    <span class="meta">@Bean</span></span><br><span class="line">    <span class="keyword">public</span> MapperScannerConfigurer <span class="title function_">mapperScannerConfigurer</span><span class="params">()</span> &#123;</span><br><span class="line">        <span class="type">MapperScannerConfigurer</span> <span class="variable">msc</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">MapperScannerConfigurer</span>();</span><br><span class="line">        msc.setBasePackage(<span class="string">&quot;com.blog.dao&quot;</span>);</span><br><span class="line">        <span class="keyword">return</span> msc;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>说明：</p><ul><li><p>使用SqlSessionFactoryBean封装SqlSessionFactory需要的环境信息<br><a href="https://pic.imgdb.cn/item/6314160016f2c2beb1d5ae4f.jpg"><img src="data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7" alt="img"></a></p></li><li><p>SqlSessionFactoryBean是前面我们讲解FactoryBean的一个子类，在该类中将SqlSessionFactory的创建进行了封装，简化对象的创建，我们只需要将其需要的内容设置即可。</p></li><li><p>方法中有一个参数为dataSource,当前Spring容器中已经创建了Druid数据源，类型刚好是DataSource类型，此时在初始化SqlSessionFactoryBean这个对象的时候，发现需要使用DataSource对象，而容器中刚好有这么一个对象，就自动加载了DruidDataSource对象。</p></li><li><p><code>sqlSessionFactory.setTypeAliasesPackage(&quot;com.blog.domain&quot;);</code>，替换掉配置文件中的</p></li></ul><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="tag">&lt;<span class="name">typeAliases</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.blog.domain&quot;</span>/&gt;</span></span><br><span class="line"><span class="tag">&lt;/<span class="name">typeAliases</span>&gt;</span></span><br></pre></td></tr></table></figure><ul><li><code>sqlSessionFactory.setDataSource(dataSource);</code>，替换掉配置文件中的</li></ul><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></pre></td><td class="code"><pre><span class="line"><span class="tag">&lt;<span class="name">environments</span> <span class="attr">default</span>=<span class="string">&quot;mysql&quot;</span>&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;mysql&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 class="tag">&lt;/<span class="name">transactionManager</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 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;url&quot;</span> <span class="attr">value</span>=<span class="string">&quot;$&#123;jdbc.url&#125;&quot;</span>&gt;</span><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;username&quot;</span> <span class="attr">value</span>=<span class="string">&quot;$&#123;jdbc.username&#125;&quot;</span>&gt;</span><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;password&quot;</span> <span class="attr">value</span>=<span class="string">&quot;$&#123;jdbc.password&#125;&quot;</span>&gt;</span><span class="tag">&lt;/<span class="name">property</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></pre></td></tr></table></figure><ul><li><p>使用MapperScannerConfigurer加载Dao接口，创建代理对象保存到IOC容器中<br><a href="https://pic.imgdb.cn/item/631416a116f2c2beb1d64489.jpg"><img src="data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7" alt="img"></a></p></li><li><p>这个MapperScannerConfigurer对象也是MyBatis提供的专用于整合的jar包中的类，用来处理原始配置文件中的mappers相关配置，加载数据层的Mapper接口类</p></li><li><p>MapperScannerConfigurer有一个核心属性basePackage，就是用来设置所扫描的包路径</p></li><li><p><code>步骤六：</code>主配置类中引入Mybatis配置类</p></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="meta">@Configuration</span></span><br><span class="line"><span class="meta">@ComponentScan(&quot;com.blog&quot;)</span></span><br><span class="line"><span class="meta">@PropertySource(&quot;jdbc.properties&quot;)</span></span><br><span class="line"><span class="meta">@Import(&#123;JdbcConfig.class, MyBatisConfig.class&#125;)</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">SpringConfig</span> &#123;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><ul><li><code>步骤七：</code>编写运行类<br>在运行类中，从IOC容器中获取Service对象，调用方法获取结果</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></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">App</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> IOException &#123;</span><br><span class="line">        <span class="type">AnnotationConfigApplicationContext</span> <span class="variable">context</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">AnnotationConfigApplicationContext</span>(SpringConfig.class);</span><br><span class="line">        <span class="type">AccountService</span> <span class="variable">accountService</span> <span class="operator">=</span> context.getBean(AccountService.class);</span><br><span class="line">        <span class="type">Account</span> <span class="variable">account</span> <span class="operator">=</span> accountService.findById(<span class="number">1</span>);</span><br><span class="line">        System.out.println(account);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><ul><li><p><code>步骤八：</code>运行程序</p><blockquote><p>Account{id=1, name=’Tom’, money=2800.0}</p></blockquote></li></ul><p>至此，Spring与Mybatis的整合就已经完成了，其中主要用到的两个类分别是:</p><ul><li>SqlSessionFactoryBean</li><li>MapperScannerConfigurer</li></ul><h3 id="Spring整合JUnit"><a href="#Spring整合JUnit" class="headerlink" title="Spring整合JUnit"></a>Spring整合JUnit</h3><ul><li><code>步骤一：</code>引入依赖</li></ul><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="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.12<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="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<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-test<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.2.10.RELEASE<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><ul><li><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><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><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="meta">@RunWith(SpringJUnit4ClassRunner.class)</span></span><br><span class="line"><span class="comment">//设置Spring环境对应的配置类</span></span><br><span class="line"><span class="meta">@ContextConfiguration(classes = &#123;SpringConfig.class&#125;)</span><span class="comment">//加载配置类</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">AccountServiceTest</span> &#123;</span><br><span class="line">    <span class="comment">//支持自动装配注入bean</span></span><br><span class="line">    <span class="meta">@Autowired</span></span><br><span class="line">    <span class="keyword">private</span> AccountService accountService;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Test</span></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">test</span><span class="params">()</span>&#123;</span><br><span class="line">        <span class="type">Account</span> <span class="variable">account</span> <span class="operator">=</span> accountService.findById(<span class="number">1</span>);</span><br><span class="line">        System.out.println(account);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Test</span></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">selectAll</span><span class="params">()</span>&#123;</span><br><span class="line">        List&lt;Account&gt; accounts = accountService.findAll();</span><br><span class="line">        System.out.println(accounts);</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>@ContextConfiguration(classes = 配置类.class)</code></li><li>单元测试，如果测试的是配置文件，则使用<code>@ContextConfiguration(locations=&#123;配置文件名,...&#125;)</code></li><li>Junit运行后是基于Spring环境运行的，所以Spring提供了一个专用的类运行器，这个务必要设置，这个类运行器就在Spring的测试专用包中提供的，导入的坐标就是这个东西<code>SpringJUnit4ClassRunner</code></li><li>上面两个配置都是固定格式，当需要测试哪个bean时，使用自动装配加载对应的对象，下面的工作就和以前做Junit单元测试完全一样了</li></ul><p>知识点1：<code>@RunWith</code></p><h1 id="AOP简介"><a href="#AOP简介" class="headerlink" title="AOP简介"></a>AOP简介</h1><p>Spring有两个核心的概念，一个是<code>IOC/DI</code>，一个是<code>AOP</code>。</p><ul><li>前面学习的<code>IOC\DI</code>主要的内容是将对象的创建和管理交给了Spring容器，而<code>AOP</code>则是在不改变原有代码的基础上添加新的功能。<h2 id="什么是AOP？"><a href="#什么是AOP？" class="headerlink" title="什么是AOP？"></a>什么是AOP？</h2></li><li><code>AOP(Aspect Oriented Programming)</code>面向切面编程，是一种编程范式，指导开发者如何组织程序结构<h2 id="AOP核心概念"><a href="#AOP核心概念" class="headerlink" title="AOP核心概念"></a>AOP核心概念</h2></li><li>前面编写的BookDaoImpl类，设置了最简单的几个执行业务逻辑的代码，没有其他功能，现在体验一下使用AOP对代码进行升级<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><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="meta">@Repository</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">BookDaoImpl</span> <span class="keyword">implements</span> <span class="title class_">BookDao</span> &#123;</span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">save</span><span class="params">()</span> &#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">        <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">10000</span>;i++) &#123;</span><br><span class="line">            System.out.println(<span class="string">&quot;book dao save ...&quot;</span>);</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="comment">//记录程序当前执行时间（结束时间）</span></span><br><span class="line">        <span class="type">Long</span> <span class="variable">endTime</span> <span class="operator">=</span> System.currentTimeMillis();</span><br><span class="line">        <span class="comment">//计算时间差</span></span><br><span class="line">        <span class="type">Long</span> <span class="variable">totalTime</span> <span class="operator">=</span> endTime-startTime;</span><br><span class="line">        <span class="comment">//输出信息</span></span><br><span class="line">        System.out.println(<span class="string">&quot;执行万次消耗时间：&quot;</span> + totalTime + <span class="string">&quot;ms&quot;</span>);</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">update</span><span class="params">()</span>&#123;</span><br><span class="line">        System.out.println(<span class="string">&quot;book dao update ...&quot;</span>);</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">delete</span><span class="params">()</span>&#123;</span><br><span class="line">        System.out.println(<span class="string">&quot;book dao delete ...&quot;</span>);</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">select</span><span class="params">()</span>&#123;</span><br><span class="line">        System.out.println(<span class="string">&quot;book dao select ...&quot;</span>);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>代码的内容很简单，就是测试一下万次执行的耗时<br>当在App类中从容器中获取bookDao对象后，分别执行其save,delete,update和select方法后会有如下的打印结果<div class="tabs" id="aop核心概念"><ul class="nav-tabs"><li class="tab active"><button type="button" data-href="#aop核心概念-1">save</button></li><li class="tab"><button type="button" data-href="#aop核心概念-2">delete</button></li><li class="tab"><button type="button" data-href="#aop核心概念-3">update</button></li><li class="tab"><button type="button" data-href="#aop核心概念-4">select</button></li></ul><div class="tab-contents"><div class="tab-item-content active" id="aop核心概念-1"><blockquote><p>book dao save …<br>book dao save …<br>book dao save …<br>book dao save …<br>book dao save …<br>book dao save …<br>执行万次消耗时间:79ms</p></blockquote><button type="button" class="tab-to-top" aria-label="scroll to top"><i class="fas fa-arrow-up"></i></button></div><div class="tab-item-content" id="aop核心概念-2"><p>book dao delete …<br>book dao delete …<br>book dao delete …<br>book dao delete …<br>book dao delete …<br>book dao delete …<br>执行万次消耗时间:81ms</p><button type="button" class="tab-to-top" aria-label="scroll to top"><i class="fas fa-arrow-up"></i></button></div><div class="tab-item-content" id="aop核心概念-3"><blockquote><p>book dao update …<br>book dao update …<br>book dao update …<br>book dao update …<br>book dao update …<br>book dao update …<br>执行万次消耗时间:63ms</p></blockquote><button type="button" class="tab-to-top" aria-label="scroll to top"><i class="fas fa-arrow-up"></i></button></div><div class="tab-item-content" id="aop核心概念-4"><blockquote><p>book dao select …</p></blockquote><button type="button" class="tab-to-top" aria-label="scroll to top"><i class="fas fa-arrow-up"></i></button></div></div></div><div class="note info no-icon flat"><p>疑问</p><ul><li>对于计算万次执行消耗的时间只有save方法有，为什么delete和update方法也会有呢?</li><li>delete和update方法有，那为什么select方法为什么又没有呢?</li></ul></div>这个案例中使用的AOP使得在不改动原代码的前提下，增强了原代码的功能，这就是AOP<br>Spring是如何实现AOP的呢？<div class="img-wrap"><div class="img-bg"><img class="img" src="https://pic.imgdb.cn/item/63155fb516f2c2beb109d12c.jpg"/></div></div></li></ul><ul><li><code>连接点(JoinPoint)</code>：程序执行过程中的任意位置，粒度为执行方法、抛出异常、设置变量等<ul><li>在SpringAOP中，理解为能够被增强的方法</li></ul></li><li><code>切入点(Pointcut):</code>真正要被增强的连接点<ul><li>在SpringAOP中，一个切入点可以描述一个具体方法，也可也匹配多个方法<ul><li>一个具体的方法:如com.blog.dao包下的BookDao接口中的无形参无返回值的save方法</li><li>匹配多个方法:所有的save方法/所有的get开头的方法/所有以Dao结尾的接口中的任意方法/所有带有一个参数的方法</li></ul></li><li>连接点范围比切入点范围大，是切入点的方法也一定是连接点，但是是连接点的方法不一定要被增强，所以可能不是切入点。</li></ul></li><li><code>通知(Advice):</code>在切入点处执行的操作，也就是要增加的共性功能<ul><li>在SpringAOP中，功能最终以方法的形式呈现</li></ul></li><li><code>通知类</code>：定义通知的类</li><li><code>切面(Aspect):</code>描述通知与切入点的对应关系。可以理解为切入点+通知</li></ul><h1 id="AOP入门案例"><a href="#AOP入门案例" class="headerlink" title="AOP入门案例"></a>AOP入门案例</h1><div class="note info no-icon flat"><p>需求：在每个方法执行前添加一个功能：输出当前系统时间</p></div><h2 id="环境准备-8"><a href="#环境准备-8" class="headerlink" title="环境准备"></a>环境准备</h2><ul><li>创建Maven项目，并导入Spring依赖<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>org.springframework<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-context<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.2.10.RELEASE<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></li><li><p>添加BookDao和BookDaoImpl类</p><div class="tabs" id=""><ul class="nav-tabs"><li class="tab active"><button type="button" data-href="#-1">BookDao</button></li><li class="tab"><button type="button" data-href="#-2">BookDaoImpl</button></li></ul><div class="tab-contents"><div class="tab-item-content active" id="-1"><figure class="highlight java"><table><tr><td class="gutter"><pre><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="keyword">public</span> <span class="keyword">interface</span> <span class="title class_">BookDao</span> &#123;</span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">save</span><span class="params">()</span>;</span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">update</span><span class="params">()</span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><button type="button" class="tab-to-top" aria-label="scroll to top"><i class="fas fa-arrow-up"></i></button></div><div class="tab-item-content" id="-2"><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><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="meta">@Repository</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">BookDaoImpl</span> <span class="keyword">implements</span> <span class="title class_">BookDao</span> &#123;</span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">save</span><span class="params">()</span> &#123;</span><br><span class="line">        System.out.println(System.currentTimeMillis());</span><br><span class="line">        System.out.println(<span class="string">&quot;book dao save ...&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_">update</span><span class="params">()</span> &#123;</span><br><span class="line">        System.out.println(<span class="string">&quot;book dao update ...&quot;</span>);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><button type="button" class="tab-to-top" aria-label="scroll to top"><i class="fas fa-arrow-up"></i></button></div></div></div></li><li><p>创建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></pre></td><td class="code"><pre><span class="line"><span class="meta">@Configuration</span></span><br><span class="line"><span class="meta">@ComponentScan(&quot;com.blog&quot;)</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">SpringConfig</span> &#123;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></li><li>编写App运行类<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><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="keyword">public</span> <span class="keyword">class</span> <span class="title class_">App</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">AnnotationConfigApplicationContext</span> <span class="variable">context</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">AnnotationConfigApplicationContext</span>(SpringConfig.class);</span><br><span class="line">        <span class="type">BookDao</span> <span class="variable">bookDao</span> <span class="operator">=</span> context.getBean(BookDao.class);</span><br><span class="line">        bookDao.update();</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h2 id="AOP实现步骤"><a href="#AOP实现步骤" class="headerlink" title="AOP实现步骤"></a>AOP实现步骤</h2><div class="note info no-icon flat"><p>需求分析：</p><ul><li>目前调用save方法，方法中能够输出当前系统时间，现在的目的是更新update方法，实现不改变update方法，添加打印时间的功能<br>思路分析：</li><li>导入坐标</li><li>制作连接点（原始操作，Dao接口和实现类）</li><li>制作共性功能（通知类和通知方法）</li><li>定义切入点</li><li>绑定切入点和通知的关系（切面）</li></ul></div></li></ul><ul><li><code>步骤一：</code>添加依赖<br><code>spring-context</code>中已经导入了<code>spring-aop</code>,所以不需要再单独导入<code>spring-aop</code><br>导入<code>AspectJ</code>的jar包,AspectJ是AOP思想的一个具体实现，Spring有自己的AOP实现，但是相比于AspectJ来说比较麻烦，所以直接采用Spring整合ApsectJ的方式进行AOP开发。</li></ul><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>org.aspectj<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>aspectjweaver<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.9.4<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><ul><li><p><code>步骤二：</code>定义接口和实现类<br>准备环境的时候已经完成了</p></li><li><p><code>步骤三：</code>定义通知类和通知<br>通知就是将共性功能抽取出来后形成的方法，共性功能指的就是当前系统时间的打印。<br>类名和方法名没有要求，可以任意。</p></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></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">MyAdvice</span> &#123;</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">        System.out.println(System.currentTimeMillis());</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><ul><li><code>步骤四：</code>定义切入点<br>BookDaoImpl中有两个方法，分别是<code>update()</code>和<code>save()</code>，我们要增强的是update方法，那么该如何定义呢？</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="keyword">public</span> <span class="keyword">class</span> <span class="title class_">MyAdvice</span> &#123;</span><br><span class="line">    <span class="meta">@Pointcut(&quot;execution(void com.blog.dao.impl.BookDaoImpl.update())&quot;)</span></span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">void</span> <span class="title function_">pt</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="keyword">void</span> <span class="title function_">method</span><span class="params">()</span> &#123;</span><br><span class="line">        System.out.println(System.currentTimeMillis());</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><div class="note info no-icon flat"><p><strong>说明:</strong></p><ul><li>切入点定义依托一个不具有实际意义的方法进行，即无参数、无返回值、方法体无实际逻辑。</li><li>execution及后面编写的内容，之后我们会专门去学习。</li></ul></div><ul><li><code>步骤五：</code>制作切面<br>切面是用来描述通知和切入点之间的关系，如何进行关系的绑定?</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="keyword">public</span> <span class="keyword">class</span> <span class="title class_">MyAdvice</span> &#123;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Pointcut(&quot;execution(void com.blog.dao.BookDao.update())&quot;)</span></span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">void</span> <span class="title function_">pt</span><span class="params">()</span>&#123;&#125;</span><br><span class="line">    </span><br><span class="line">    <span class="meta">@Before(&quot;pt()&quot;)</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">()</span>&#123;</span><br><span class="line">        System.out.println(System.currentTimeMillis());</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>绑定切入点与通知关系，并指定通知添加到原始连接点的具体执行<code>位置</code></p><p><strong>说明:</strong><code>@Before</code>翻译过来是之前，也就是说通知会在切入点方法执行之前执行，除此之前还有其他四种类型，后面会讲。<br>那这里就会在执行update()之前，来执行我们的method()，输出当前毫秒值</p><ul><li><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><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><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="meta">@Component</span></span><br><span class="line"><span class="meta">@Aspect</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">MyAdvice</span> &#123;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Pointcut(&quot;execution(void com.blog.dao.impl.BookDaoImpl.update())&quot;)</span></span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">void</span> <span class="title function_">pt</span><span class="params">()</span> &#123;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Before(&quot;pt()&quot;)</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">()</span> &#123;</span><br><span class="line">        System.out.println(System.currentTimeMillis());</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><ul><li><code>步骤七：</code>开启注解格式AOP功能<br>使用<code>@EnableAspectJAutoProxy</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></pre></td><td class="code"><pre><span class="line"><span class="meta">@Configuration</span></span><br><span class="line"><span class="meta">@ComponentScan(&quot;com.blog&quot;)</span></span><br><span class="line"><span class="meta">@EnableAspectJAutoProxy</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">SpringConfig</span> &#123;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><ul><li><code>步骤八：</code>运行程序<br>这次我们再来调用update()</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></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">App</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">AnnotationConfigApplicationContext</span> <span class="variable">context</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">AnnotationConfigApplicationContext</span>(SpringConfig.class);</span><br><span class="line">        <span class="type">BookDao</span> <span class="variable">bookDao</span> <span class="operator">=</span> context.getBean(BookDao.class);</span><br><span class="line">        bookDao.update();</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>控制台成功输出了当前毫秒值</p><blockquote><p>1662367945787<br>book dao update …</p></blockquote><div class="tabs" id="aop注解1"><ul class="nav-tabs"><li class="tab active"><button type="button" data-href="#aop注解1-1"><i class="EnableAspectJAutoProxy" style="text-align: center;"></i></button></li><li class="tab"><button type="button" data-href="#aop注解1-2"><i class="Aspect" style="text-align: center;"></i></button></li><li class="tab"><button type="button" data-href="#aop注解1-3"><i class="Before" style="text-align: center;"></i></button></li><li class="tab"><button type="button" data-href="#aop注解1-4"><i class="Pointcut" style="text-align: center;"></i></button></li></ul><div class="tab-contents"><div class="tab-item-content active" id="aop注解1-1"><div class="table-container"><table><thead><tr><th style="text-align:center">名称</th><th style="text-align:center">@EnableAspectJAutoProxy</th></tr></thead><tbody><tr><td style="text-align:center">类型</td><td style="text-align:center">配置类注解</td></tr><tr><td style="text-align:center">位置</td><td style="text-align:center">配置类定义上方</td></tr><tr><td style="text-align:center">作用</td><td style="text-align:center">开启注解格式AOP功能</td></tr></tbody></table></div><button type="button" class="tab-to-top" aria-label="scroll to top"><i class="fas fa-arrow-up"></i></button></div><div class="tab-item-content" id="aop注解1-2"><div class="table-container"><table><thead><tr><th style="text-align:center">名称</th><th style="text-align:center">@Aspect</th></tr></thead><tbody><tr><td style="text-align:center">类型</td><td style="text-align:center">类注解</td></tr><tr><td style="text-align:center">位置</td><td style="text-align:center">切面类定义上方</td></tr><tr><td style="text-align:center">作用</td><td style="text-align:center">设置当前类为AOP切面类</td></tr></tbody></table></div><button type="button" class="tab-to-top" aria-label="scroll to top"><i class="fas fa-arrow-up"></i></button></div><div class="tab-item-content" id="aop注解1-3"><div class="table-container"><table><thead><tr><th style="text-align:center">名称</th><th style="text-align:center">@Before</th></tr></thead><tbody><tr><td style="text-align:center">类型</td><td style="text-align:center">方法注解</td></tr><tr><td style="text-align:center">位置</td><td style="text-align:center">通知方法定义上方</td></tr><tr><td style="text-align:center">作用</td><td style="text-align:center">设置当前通知方法与切入点之间的绑定关系，当前通知方法在原始切入点方法前运行</td></tr></tbody></table></div><button type="button" class="tab-to-top" aria-label="scroll to top"><i class="fas fa-arrow-up"></i></button></div><div class="tab-item-content" id="aop注解1-4"><div class="table-container"><table><thead><tr><th style="text-align:center">名称</th><th style="text-align:center">@Pointcut</th></tr></thead><tbody><tr><td style="text-align:center">类型</td><td style="text-align:center">方法注解</td></tr><tr><td style="text-align:center">位置</td><td style="text-align:center">切入点方法定义上方</td></tr><tr><td style="text-align:center">作用</td><td style="text-align:center">设置切入点方法</td></tr><tr><td style="text-align:center">属性</td><td style="text-align:center">value（默认）：切入点表达式</td></tr></tbody></table></div><button type="button" class="tab-to-top" aria-label="scroll to top"><i class="fas fa-arrow-up"></i></button></div></div></div><h1 id="AOP工作流程"><a href="#AOP工作流程" class="headerlink" title="AOP工作流程"></a>AOP工作流程</h1><p>这一节我们主要讲解两个知识点:<code>AOP工作流程</code>和<code>AOP核心概念</code>。其中核心概念是对前面核心概念的补充。</p><h2 id="工作流程"><a href="#工作流程" class="headerlink" title="工作流程"></a>工作流程</h2><p>AOP是基于Spring容器管理的bean做的增强，所以整个工作过程需要从Spring加载bean说起</p><ul><li><p>流程一：Spring容器启动</p><ul><li>容器启动就需要去加载bean,哪些类需要被加载呢?</li><li>需要被增强的类，如:BookServiceImpl</li><li>通知类，如:MyAdvice</li><li>注意此时bean对象还没有创建成功</li></ul></li><li><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="meta">@Component</span></span><br><span class="line"><span class="meta">@Aspect</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">MyAdvice</span> &#123;</span><br><span class="line">    <span class="meta">@Pointcut(&quot;execution(void com.blog.dao.impl.BookDaoImpl.save())&quot;)</span></span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">void</span> <span class="title function_">ptx</span><span class="params">()</span> &#123;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Pointcut(&quot;execution(void com.blog.dao.impl.BookDaoImpl.update())&quot;)</span></span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">void</span> <span class="title function_">pt</span><span class="params">()</span> &#123;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line"></span><br><span class="line">    <span class="meta">@Before(&quot;pt()&quot;)</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">()</span> &#123;</span><br><span class="line">        System.out.println(System.currentTimeMillis());</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>上面这个例子中有两个切入点的配置，但是第一个<code>ptx()</code>并没有被使用(该切入点没有被任何通知使用)，所以不会被读取。</p></li><li><p>流程三：初始化bean，判定bean对应的类中的方法是否匹配到任意切入点</p><ul><li>注意第一步在容器启动的时候，bean对象还没有被创建成功。</li><li>要被实例化bean对象的类中的方法和切入点进行匹配</li></ul></li></ul><div class="img-wrap"><div class="img-bg"><img class="img" src="https://pic.imgdb.cn/item/6315bf8416f2c2beb16ede79.jpg"/></div></div><ul><li><p>匹配失败，创建原始对象，如<code>UserDao</code></p><ul><li>匹配失败说明不需要增强，直接调用原始对象的方法即可。</li></ul></li><li><p>匹配成功，创建原始对象（<code>目标对象</code>）的<code>代理</code>对象，如:<code>BookDao</code></p><ul><li>匹配成功说明需要对其进行增强</li><li>对哪个类做增强，这个类对应的对象就叫做<code>目标对象</code></li><li>因为要对目标对象进行功能增强，而采用的技术是<code>动态代理</code>，所以会为其创建一个代理对象</li><li>最终运行的是代理对象的方法，在该方法中会对原始方法进行功能增强</li></ul></li><li><p>流程四：获取bean执行方法</p><ul><li>获取的bean是原始对象时，调用方法并执行，完成操作</li><li>获取的bean是代理对象时，根据代理对象的运行模式运行原始方法与增强的内容，完成操作</li></ul></li><li><p>下面我们来验证一下容器中是否为代理对象</p><ul><li>如果目标对象中的方法<code>会被增强</code>，那么容器中将存入的是目标对象的<code>代理对象</code></li><li>如果目标对象中的方法<code>不被增强</code>，那么容器中将存入的是目标<code>对象本身</code></li></ul></li><li><p><code>步骤一：</code>修改App运行类，获取类的类型并输出</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="keyword">public</span> <span class="keyword">class</span> <span class="title class_">App</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">AnnotationConfigApplicationContext</span> <span class="variable">context</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">AnnotationConfigApplicationContext</span>(SpringConfig.class);</span><br><span class="line">        <span class="type">BookDao</span> <span class="variable">bookDao</span> <span class="operator">=</span> context.getBean(BookDao.class);</span><br><span class="line">        System.out.println(bookDao);</span><br><span class="line">        System.out.println(bookDao.getClass());</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></li><li><p><code>步骤二：</code>修改MyAdvice类，改为不增强<br>将定义的切入点改为<code>updatexxx</code>，而BookDaoImpl类中不存在该方法，所以BookDao中的update方法在执行的时候，就不会被增强<br>所以此时容器中的对象应该是目标对象本身。</p></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="meta">@Component</span></span><br><span class="line"><span class="meta">@Aspect</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">MyAdvice</span> &#123;</span><br><span class="line">    <span class="meta">@Pointcut(&quot;execution(void com.blog.dao.impl.BookDaoImpl.updatexxx())&quot;)</span></span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">void</span> <span class="title function_">pt</span><span class="params">()</span> &#123;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Before(&quot;pt()&quot;)</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">()</span> &#123;</span><br><span class="line">        System.out.println(System.currentTimeMillis());</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><ul><li><p><code>步骤三：</code>运行程序<br>输出结果如下，确实是目标对象本身，符合我们的预期</p><blockquote><p>com.blog.dao.impl.BookDaoImpl@bcec361<br>class com.blog.dao.impl.BookDaoImpl</p></blockquote></li><li><p><code>步骤四：</code>修改MyAdvice类，改为增强<br>将定义的切入点改为<code>update</code>，那么BookDao中的update方法在执行的时候，就会被增强<br>所以容器中的对象应该是目标对象的代理对象</p></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="meta">@Component</span></span><br><span class="line"><span class="meta">@Aspect</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">MyAdvice</span> &#123;</span><br><span class="line">    <span class="meta">@Pointcut(&quot;execution(void com.blog.dao.impl.BookDaoImpl.update())&quot;)</span></span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">void</span> <span class="title function_">pt</span><span class="params">()</span> &#123;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Before(&quot;pt()&quot;)</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">()</span> &#123;</span><br><span class="line">        System.out.println(System.currentTimeMillis());</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><ul><li><code>步骤五：</code>运行程序<br>结果如下<blockquote><p>com.blog.dao.impl.BookDaoImpl@3d34d211<br>class com.sun.proxy.$Proxy19</p></blockquote></li></ul><div class="note warning no-icon flat"><p><strong>注意</strong><br>不能直接打印对象，从上面两次结果中可以看出，直接打印对象走的是对象的toString方法，不管是不是代理对象，打印的结果都是一样的，原因是内部对toString方法进行了重写。</p></div><h2 id="AOP核心概念-1"><a href="#AOP核心概念-1" class="headerlink" title="AOP核心概念"></a>AOP核心概念</h2><p>在上面介绍AOP的工作流程中，我们提到了两个核心概念，分别是:</p><ul><li><code>目标对象(Target)</code>：原始功能去掉共性功能对应的类产生的对象，这种对象是无法直接完成最终工作的</li><li><code>代理(Proxy)</code>：目标对象无法直接完成工作，需要对其进行功能回填，通过原始对象的代理对象实现</li></ul><p>简单来说,目标对象就是要增强的类<code>如:BookServiceImpl类</code>对应的对象，也叫原始对象，不能说它不能运行，只能说它在运行的过程中对于要增强的内容是缺失的。</p><p>SpringAOP是在不改变原有设计(代码)的前提下对其进行增强的，它的底层采用的是<code>代理模式</code>实现的，所以要对原始对象进行增强，就需要对原始对象创建代理对象，在代理对象中的方法把通知<code>如:MyAdvice中的method方法</code>内容加进去，就实现了增强，这就是我们所说的代理(Proxy)。</p><h1 id="AOP配置管理"><a href="#AOP配置管理" class="headerlink" title="AOP配置管理"></a>AOP配置管理</h1><h2 id="AOP切入点表达式"><a href="#AOP切入点表达式" class="headerlink" title="AOP切入点表达式"></a>AOP切入点表达式</h2><ul><li>在入门案例就已经用过切入点表达式了，此处具体学习<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">@Pointcut(&quot;execution(void com.blog.dao.impl.BookDaoImpl.update())&quot;)</span></span><br></pre></td></tr></table></figure>对于AOP中切入点表达式，我们总共会学习三个内容，分别是<code>语法格式</code>、<code>通配符</code>和<code>书写技巧</code>。<h3 id="语法格式"><a href="#语法格式" class="headerlink" title="语法格式"></a>语法格式</h3><div class="note info no-icon flat"><p>明确：</p><ul><li>切入点:要进行增强的方法</li><li>切入点表达式:要进行增强的方法的描述方式</li></ul></div>对切入点描述有两种方式：</li><li><span class='p red'>描述方式一：</span>执行BookDao接口的无参数update方法<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">execution(<span class="keyword">void</span> com.blog.dao.BookDao.update())</span><br></pre></td></tr></table></figure></li><li><span class='p red'>描述方式二：</span>执行BookDaoImpl类的无参数update方法<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">execution(<span class="keyword">void</span> com.blog.dao.impl.BookDaoImpl.update())</span><br></pre></td></tr></table></figure></li></ul><p>对于切入点表达式的语法为:</p><ul><li><p>切入点表达式标准格式：动作关键字(访问修饰符 返回值 包名.类/接口名.方法名(参数) 异常名)<br>对于这个格式，不需要硬记，通过一个例子去理解它:</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">execution(<span class="keyword">public</span> User com.blog.service.UserService.findById(<span class="type">int</span>))</span><br></pre></td></tr></table></figure></li><li><p>execution：动作关键字，描述切入点的行为动作，例如execution表示执行到指定切入点</p></li><li>public:访问修饰符,还可以是public，private等，可以省略</li><li>User：返回值，写返回值类型</li><li>com.blog.service：包名，多级包使用点连接</li><li>UserService:类/接口名称</li><li>findById：方法名</li><li>int:参数，直接写参数的类型，多个类型用逗号隔开</li><li>异常名：方法定义中抛出指定异常，可以省略</li></ul><div class="note info no-icon flat"><p>切入点表达式就是要找到需要增强的方法，所以它就是对一个具体方法的描述，但是方法的定义会有很多，所以如果每一个方法对应一个切入点表达式，想想这块就会觉得将来编写起来会比较麻烦，有没有更简单的方式呢?</p><ul><li>使用通配符</li></ul></div><h3 id="通配符"><a href="#通配符" class="headerlink" title="通配符"></a>通配符</h3><p>使用通配符的主要目的就是简化配置，例如：</p><ul><li><p><code>*</code>:单个独立的任意符号，可以独立出现，也可以作为前缀或者后缀的匹配符出现<br>匹配com.blog包下的任意包中的UserService类或接口中所有find开头的带有一个参数的方法</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">execution（<span class="keyword">public</span> * com.blog.*.UserService.find*(*))</span><br></pre></td></tr></table></figure></li><li><p><code>..</code>：多个连续的任意符号，可以独立出现，常用于简化包名与参数的书写<br>匹配com包下的任意包中的UserService类或接口中所有名称为findById的方法</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">execution（<span class="keyword">public</span> User com..UserService.findById(..))</span><br></pre></td></tr></table></figure></li><li><p><code>+</code>：专用于匹配子类类型<br>这个使用率较低，描述子类的，<code>*Service+</code>，表示所有以Service结尾的接口的子类</p></li></ul><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">execution(* *..*Service+.*(..))</span><br></pre></td></tr></table></figure><p>下面来具体分析一下各种用法</p><ul><li>匹配接口，能匹配到</li></ul><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">execution(<span class="keyword">void</span> com.blog.dao.BookDao.update())</span><br></pre></td></tr></table></figure><ul><li><p>匹配实现类，能匹配到</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">execution(<span class="keyword">void</span> com.blog.dao.impl.BookDaoImpl.update())</span><br></pre></td></tr></table></figure></li><li><p>返回值任意，能匹配到</p></li></ul><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">execution(* com.blog.dao.impl.BookDaoImpl.update())</span><br></pre></td></tr></table></figure><ul><li>返回值任意，但是update方法必须要有一个参数，无法匹配，要想匹配需要在update接口和实现类添加参数</li></ul><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">execution(* com.blog.dao.impl.BookDaoImpl.update(*))</span><br></pre></td></tr></table></figure><ul><li>返回值为void,com包下的任意包三层包下的任意类的update方法，匹配到的是实现类，能匹配</li></ul><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">execution(<span class="keyword">void</span> com.*.*.*.*.update())</span><br></pre></td></tr></table></figure><ul><li>返回值为void,com包下的任意两层包下的任意类的update方法，匹配到的是接口，能匹配</li></ul><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">execution(<span class="keyword">void</span> com.*.*.*.update())</span><br></pre></td></tr></table></figure><ul><li>返回值为void，方法名是update的任意包下的任意类，能匹配</li></ul><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">execution(<span class="keyword">void</span> *..update())</span><br></pre></td></tr></table></figure><ul><li>匹配项目中任意类的任意方法，能匹配，但是不建议使用这种方式，影响范围广</li></ul><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">execution(* *..*(..))</span><br></pre></td></tr></table></figure><ul><li>匹配项目中任意包任意类下只要以u开头的方法，update方法能满足，能匹配</li></ul><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">execution(* *..u*(..))</span><br></pre></td></tr></table></figure><ul><li>匹配项目中任意包任意类下只要以e结尾的方法，update和save方法能满足，能匹配</li></ul><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">execution(* *..*e(..))</span><br></pre></td></tr></table></figure><ul><li>返回值为void，com包下的任意包任意类任意方法，能匹配，*代表的是方法</li></ul><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">execution(<span class="keyword">void</span> com..*())</span><br></pre></td></tr></table></figure><ul><li>将项目中所有业务层方法的以find开头的方法匹配</li></ul><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">execution(* com.blog.*.*Service.find*(..))</span><br></pre></td></tr></table></figure><ul><li>将项目中所有业务层方法的以save开头的方法匹配</li></ul><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">execution(* com.blog.*.*Service.save*(..))</span><br></pre></td></tr></table></figure><h4 id="书写技巧"><a href="#书写技巧" class="headerlink" title="书写技巧"></a>书写技巧</h4><p>对于切入点表达式的编写其实是很灵活的，那么在编写的时候，有没有什么好的技巧让我们用用:</p><ul><li>所有代码按照标准规范开发，否则以下技巧全部失效</li><li>描述切入点通常<code>描述接口</code>，而不描述实现类,如果描述到实现类，就出现紧耦合了</li><li>访问控制修饰符针对接口开发均采用public描述（<code>可省略访问控制修饰符描述</code>）</li><li>返回值类型对于增删改类使用精准类型加速匹配，对于查询类使用<code>*</code>通配快速描述</li><li><code>包名</code>书写尽量不使用<code>..</code>匹配，效率过低，常用<code>*</code>做单个包描述匹配，或精准匹配</li><li><code>接口名/类名</code>书写名称与模块相关的采用<code>*</code>匹配，例如UserService书写成<code>*Service</code>，绑定业务层接口名</li><li>方法名书写以<code>动词</code>进行<code>精准匹配</code>，名词采用<code>*</code>匹配，例如<code>getById</code>书写成<code>getBy*</code>，<code>selectAll</code>书写成<code>selectAll</code></li><li>参数规则较为复杂，根据业务方法灵活调整</li><li>通常<code>不使用异常</code>作为<code>匹配</code>规则</li></ul>]]></content>
    
    
      
      
    <summary type="html">&lt;h1 id=&quot;课程介绍&quot;&gt;&lt;a href=&quot;#课程介绍&quot; class=&quot;headerlink&quot; title=&quot;课程介绍&quot;&gt;&lt;/a&gt;课程介绍&lt;/h1&gt;&lt;div class=&quot;note info no-icon flat&quot;&gt;&lt;p&gt;Spring是一款功能强大的框架，主要优势是&lt;cod</summary>
      
    
    
    
    
    <category term="Spring" scheme="https://silvan.chat/tags/Spring/"/>
    
  </entry>
  
  <entry>
    <title></title>
    <link href="https://silvan.chat/2025/10/28/SSM-%E9%BB%91%E9%A9%AC-SSM%E6%95%B4%E5%90%88/"/>
    <id>https://silvan.chat/2025/10/28/SSM-%E9%BB%91%E9%A9%AC-SSM%E6%95%B4%E5%90%88/</id>
    <published>2025-10-28T11:42:57.653Z</published>
    <updated>2025-10-28T11:42:57.653Z</updated>
    
    <content type="html"><![CDATA[<h1 id="SSM整合"><a href="#SSM整合" class="headerlink" title="SSM整合"></a>SSM整合</h1><h2 id="流程分析"><a href="#流程分析" class="headerlink" title="流程分析"></a>流程分析</h2><ol><li><p>创建工程</p><ul><li>创建一个Maven的web工程</li><li>pom.xml添加SSM需要的依赖jar包</li><li><p>编写Web项目的入口配置类，实现<code>AbstractAnnotationConfigDispatcherServletInitializer</code>接口并重写以下方法</p><ul><li><code>getRootConfigClasses()</code> ：返回Spring的配置类 —&gt; 需要<code>SpringConfig</code>配置类</li><li><code>getServletConfigClasses()</code> ：返回SpringMVC的配置类 —&gt; 需要<code>SpringMvcConfig</code>配置类</li><li><code>getServletMappings()</code> : 设置SpringMVC请求拦截路径规则</li><li><code>getServletFilters()</code> ：设置过滤器，解决POST请求中文乱码问题</li></ul></li></ul></li><li><p>整合SSM中的各种配置</p><ul><li><strong>Spring：</strong><ul><li>标识该类为配置类，使用<code>@Configuration</code></li><li>扫描<code>Service</code>所在的包，使用<code>@ComponentScan</code></li><li>在<code>Service</code>层要管理事务，使用<code>@EnableTransactionManagement</code></li><li>读取外部的<code>properties</code>配置文件，使用<code>@PropertySource</code></li></ul></li><li><p><strong>Mybatis:</strong></p><ul><li>第三方数据源配置类 <code>JdbcConfig</code></li><li>构建DataSource数据源，使用<code>@Bean</code>来将返回的第三方类注册为Bean，使用<code>@Value</code>来注入数据源属性</li><li>构建平台事务管理器，DataSourceTransactionManager，使用<code>@Bean</code></li><li>Mybatis配置类 <code>MybatisConfig</code></li><li>构建<code>SqlSessionFactoryBean</code>并设置别名扫描与数据源，使用<code>@Bean</code></li><li>构建<code>MapperScannerConfigurer</code>并设置DAO层的包扫描</li></ul></li><li><p><code>SpringMvcConfig</code></p><ul><li>标识该类为配置类，使用<code>@Configuration</code></li><li>扫描<code>Controller</code>所在的包，使用<code>@ComponentScan</code></li><li>开启SpringMVC注解支持，使用<code>@EnableWebMvc</code></li></ul></li></ul></li><li><p>功能模块(与具体的业务模块有关)</p><ul><li><p>创建数据库表</p></li><li><p>根据数据库表创建对应的模型类</p></li><li><p>通过Dao层完成数据库表的增删改查(接口+自动代理)</p></li><li><p>编写</p><figure class="highlight ebnf"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="attribute">Service</span></span><br></pre></td></tr></table></figure><p>层(Service接口+实现类)</p><ul><li><code>@Service</code></li><li><code>@Transactional</code></li><li>整合Junit对业务层进行单元测试<ul><li><code>@RunWith</code></li><li><code>@ContextConfiguration</code></li><li><code>@Test</code></li></ul></li></ul></li><li><p>编写</p><figure class="highlight ebnf"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="attribute">Controller</span></span><br></pre></td></tr></table></figure><p>层</p><ul><li>接收请求 <code>@RequestMapping</code>、<code>@GetMapping</code>、<code>@PostMapping</code>、<code>@PutMapping</code>、<code>@DeleteMapping</code></li><li>接收数据 简单、POJO、嵌套POJO、集合、数组、JSON数据类型<ul><li><code>@RequestParam</code></li><li><code>@PathVariable</code></li><li><code>@RequestBody</code></li></ul></li><li>转发业务层<ul><li><code>@Autowired</code></li></ul></li><li>响应结果<ul><li><code>@ResponseBody</code></li></ul></li></ul></li></ul></li></ol><h2 id="整合配置"><a href="#整合配置" class="headerlink" title="整合配置"></a>整合配置</h2><ul><li><p><code>步骤一：</code>创建Maven的web项目</p></li><li><p><code>步骤二：</code>导入坐标<br>导入的坐标有：Springmvc，jdbc，mybatis，mysql-connector-java，druid，junit，javax.servlet-api，jackson-databind</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><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="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<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-webmvc<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.2.10.RELEASE<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="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<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-jdbc<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.2.10.RELEASE<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="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<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-test<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.2.10.RELEASE<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="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>3.5.6<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="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-spring<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.3.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="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>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-java<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.1.46<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="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.1.16<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="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.12<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="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>javax.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>javax.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>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="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.fasterxml.jackson.core<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>jackson-databind<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.9.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></pre></td></tr></table></figure></li><li><p><code>步骤三：</code>创建项目包结构</p><ul><li><code>com.blog.config</code>目录存放的是相关的配置类</li><li><code>com.blog.controller</code>编写的是Controller类</li><li><code>com.blog.dao</code>存放的是Dao接口，因为使用的是Mapper接口代理方式，所以没有实现类包</li><li><code>com.blog.service</code>存的是Service接口，<code>com.blog.service.impl</code>存放的是Service实现类</li><li><code>resources</code>:存入的是配置文件，如Jdbc.properties</li><li><code>webapp</code>:目录可以存放静态资源</li><li><code>test/java</code>:存放的是测试类</li></ul></li><li><p><code>步骤四：</code>创建jdbc.properties</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></pre></td><td class="code"><pre><span class="line"><span class="attr">jdbc.driver</span>=<span class="string">com.mysql.jdbc.Driver</span></span><br><span class="line"><span class="attr">jdbc.url</span>=<span class="string">jdbc:mysql://localhost:13306/ssm_db?useSSL=false</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">PASSWORD.</span></span><br></pre></td></tr></table></figure></li><li><p><code>步骤五：</code>创建JdbcConfig配置类</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_">JdbcConfig</span> &#123;</span><br><span class="line">    <span class="meta">@Value(&quot;$&#123;jdbc.driver&#125;&quot;)</span></span><br><span class="line">    <span class="keyword">private</span> String driver;</span><br><span class="line">    <span class="meta">@Value(&quot;$&#123;jdbc.url&#125;&quot;)</span></span><br><span class="line">    <span class="keyword">private</span> String url;</span><br><span class="line">    <span class="meta">@Value(&quot;$&#123;jdbc.username&#125;&quot;)</span></span><br><span class="line">    <span class="keyword">private</span> String username;</span><br><span class="line">    <span class="meta">@Value(&quot;$&#123;jdbc.password&#125;&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="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">DruidDataSource</span> <span class="variable">dataSource</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">DruidDataSource</span>();</span><br><span class="line">        dataSource.setDriverClassName(driver);</span><br><span class="line">        dataSource.setUrl(url);</span><br><span class="line">        dataSource.setUsername(username);</span><br><span class="line">        dataSource.setPassword(password);</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="meta">@Bean</span></span><br><span class="line">    <span class="keyword">public</span> PlatformTransactionManager <span class="title function_">platformTransactionManager</span><span class="params">(DataSource dataSource)</span>&#123;</span><br><span class="line">        <span class="type">DataSourceTransactionManager</span> <span class="variable">ds</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">DataSourceTransactionManager</span>();</span><br><span class="line">        ds.setDataSource(dataSource);</span><br><span class="line">        <span class="keyword">return</span> ds;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></li><li><p><code>步骤六：</code>创建MyBatisConfig配置类</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_">MyBatisConfig</span> &#123;</span><br><span class="line">    <span class="meta">@Bean</span></span><br><span class="line">    <span class="keyword">public</span> SqlSessionFactoryBean <span class="title function_">sqlSessionFactory</span><span class="params">(DataSource dataSource)</span>&#123;</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">        factoryBean.setTypeAliasesPackage(<span class="string">&quot;com.blog.domain&quot;</span>);</span><br><span class="line">        <span class="keyword">return</span> factoryBean;</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> MapperScannerConfigurer <span class="title function_">mapperScannerConfigurer</span><span class="params">()</span>&#123;</span><br><span class="line">        <span class="type">MapperScannerConfigurer</span> <span class="variable">msc</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">MapperScannerConfigurer</span>();</span><br><span class="line">        msc.setBasePackage(<span class="string">&quot;com.blog.dao&quot;</span>);</span><br><span class="line">        <span class="keyword">return</span> msc;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></li><li><p><code>步骤七：</code>创建SpringConfig配置类</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="meta">@Configuration</span></span><br><span class="line"><span class="meta">@ComponentScan(&quot;com.blog.service&quot;)</span></span><br><span class="line"><span class="meta">@PropertySource(&quot;jdbc.properties&quot;)</span></span><br><span class="line"><span class="meta">@EnableTransactionManagement</span></span><br><span class="line"><span class="meta">@Import(&#123;JdbcConfig.class, MyBatisConfig.class&#125;)</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">SpringConfig</span> &#123;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></li><li><p><code>步骤八：</code>创建SpringMvcConfig配置类</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></pre></td><td class="code"><pre><span class="line"><span class="meta">@Configuration</span></span><br><span class="line"><span class="meta">@ComponentScan(&quot;com.blog.controller&quot;)</span></span><br><span class="line"><span class="meta">@EnableWebMvc</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">SpringMvcConfig</span> &#123;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></li><li><p><code>步骤九：</code>创建ServletContainersInitConfig配置类</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="keyword">public</span> <span class="keyword">class</span> <span class="title class_">ServletContainersInitConfig</span> <span class="keyword">extends</span> <span class="title class_">AbstractAnnotationConfigDispatcherServletInitializer</span> &#123;</span><br><span class="line">    <span class="keyword">protected</span> Class&lt;?&gt;[] getRootConfigClasses() &#123;</span><br><span class="line">        <span class="keyword">return</span> <span class="keyword">new</span> <span class="title class_">Class</span>[]&#123;SpringConfig.class&#125;;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">protected</span> Class&lt;?&gt;[] getServletConfigClasses() &#123;</span><br><span class="line">        <span class="keyword">return</span> <span class="keyword">new</span> <span class="title class_">Class</span>[]&#123;SpringMvcConfig.class&#125;;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">protected</span> String[] getServletMappings() &#123;</span><br><span class="line">        <span class="keyword">return</span> <span class="keyword">new</span> <span class="title class_">String</span>[]&#123;<span class="string">&quot;/&quot;</span>&#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> Filter[] getServletFilters() &#123;</span><br><span class="line">        <span class="type">CharacterEncodingFilter</span> <span class="variable">filter</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">CharacterEncodingFilter</span>();</span><br><span class="line">        filter.setEncoding(<span class="string">&quot;utf-8&quot;</span>);</span><br><span class="line">        <span class="keyword">return</span> <span class="keyword">new</span> <span class="title class_">Filter</span>[]&#123;filter&#125;;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></li></ul><h2 id="功能模块开发"><a href="#功能模块开发" class="headerlink" title="功能模块开发"></a>功能模块开发</h2><div class="note info no-icon flat"><p>需求：对表tbl_book进行新增、修改、删除、根据ID查询和查询所有</p></div><ul><li><p><code>步骤一：</code></p><p>创建数据库及表</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><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><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">create</span> database ssm_db;</span><br><span class="line">use ssm_db;</span><br><span class="line"><span class="keyword">create table</span> tbl_book</span><br><span class="line">(</span><br><span class="line">    id          <span class="type">int</span> <span class="keyword">primary key</span> auto_increment,</span><br><span class="line">    type        <span class="type">varchar</span>(<span class="number">20</span>),</span><br><span class="line">    `name`      <span class="type">varchar</span>(<span class="number">50</span>),</span><br><span class="line">    description <span class="type">varchar</span>(<span class="number">255</span>)</span><br><span class="line">);</span><br><span class="line"></span><br><span class="line"><span class="keyword">insert into</span> `tbl_book`(`id`, `type`, `name`, `description`)</span><br><span class="line"><span class="keyword">values</span> (<span class="number">1</span>, <span class="string">&#x27;计算机理论&#x27;</span>, <span class="string">&#x27;Spring实战 第五版&#x27;</span>, <span class="string">&#x27;Spring入门经典教程，深入理解Spring原理技术内幕&#x27;</span>),</span><br><span class="line">       (<span class="number">2</span>, <span class="string">&#x27;计算机理论&#x27;</span>, <span class="string">&#x27;Spring 5核心原理与30个类手写实践&#x27;</span>, <span class="string">&#x27;十年沉淀之作，手写Spring精华思想&#x27;</span>),</span><br><span class="line">       (<span class="number">3</span>, <span class="string">&#x27;计算机理论&#x27;</span>, <span class="string">&#x27;Spring 5设计模式&#x27;</span>, <span class="string">&#x27;深入Spring源码刨析Spring源码中蕴含的10大设计模式&#x27;</span>),</span><br><span class="line">       (<span class="number">4</span>, <span class="string">&#x27;计算机理论&#x27;</span>, <span class="string">&#x27;Spring MVC+Mybatis开发从入门到项目实战&#x27;</span>,</span><br><span class="line">        <span class="string">&#x27;全方位解析面向Web应用的轻量级框架，带你成为Spring MVC开发高手&#x27;</span>),</span><br><span class="line">       (<span class="number">5</span>, <span class="string">&#x27;计算机理论&#x27;</span>, <span class="string">&#x27;轻量级Java Web企业应用实战&#x27;</span>, <span class="string">&#x27;源码级刨析Spring框架，适合已掌握Java基础的读者&#x27;</span>),</span><br><span class="line">       (<span class="number">6</span>, <span class="string">&#x27;计算机理论&#x27;</span>, <span class="string">&#x27;Java核心技术 卷Ⅰ 基础知识(原书第11版)&#x27;</span>,</span><br><span class="line">        <span class="string">&#x27;Core Java第11版，Jolt大奖获奖作品，针对Java SE9、10、11全面更新&#x27;</span>),</span><br><span class="line">       (<span class="number">7</span>, <span class="string">&#x27;计算机理论&#x27;</span>, <span class="string">&#x27;深入理解Java虚拟机&#x27;</span>, <span class="string">&#x27;5个纬度全面刨析JVM,大厂面试知识点全覆盖&#x27;</span>),</span><br><span class="line">       (<span class="number">8</span>, <span class="string">&#x27;计算机理论&#x27;</span>, <span class="string">&#x27;Java编程思想(第4版)&#x27;</span>, <span class="string">&#x27;Java学习必读经典，殿堂级著作！赢得了全球程序员的广泛赞誉&#x27;</span>),</span><br><span class="line">       (<span class="number">9</span>, <span class="string">&#x27;计算机理论&#x27;</span>, <span class="string">&#x27;零基础学Java(全彩版)&#x27;</span>, <span class="string">&#x27;零基础自学编程的入门图书，由浅入深，详解Java语言的编程思想和核心技术&#x27;</span>),</span><br><span class="line">       (<span class="number">10</span>, <span class="string">&#x27;市场营销&#x27;</span>, <span class="string">&#x27;直播就这么做:主播高效沟通实战指南&#x27;</span>, <span class="string">&#x27;李子柒、李佳奇、薇娅成长为网红的秘密都在书中&#x27;</span>),</span><br><span class="line">       (<span class="number">11</span>, <span class="string">&#x27;市场营销&#x27;</span>, <span class="string">&#x27;直播销讲实战一本通&#x27;</span>, <span class="string">&#x27;和秋叶一起学系列网络营销书籍&#x27;</span>),</span><br><span class="line">       (<span class="number">12</span>, <span class="string">&#x27;市场营销&#x27;</span>, <span class="string">&#x27;直播带货:淘宝、天猫直播从新手到高手&#x27;</span>, <span class="string">&#x27;一本教你如何玩转直播的书，10堂课轻松实现带货月入3W+&#x27;</span>);</span><br></pre></td></tr></table></figure></li><li><p><code>步骤二：</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></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">Book</span> &#123;</span><br><span class="line">    <span class="keyword">private</span> Integer id;</span><br><span class="line">    <span class="keyword">private</span> String type;</span><br><span class="line">    <span class="keyword">private</span> String name;</span><br><span class="line">    <span class="keyword">private</span> String description;</span><br><span class="line">    </span><br><span class="line">    <span class="keyword">public</span> Integer <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">(Integer 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_">getType</span><span class="params">()</span> &#123;</span><br><span class="line">        <span class="keyword">return</span> type;</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_">setType</span><span class="params">(String type)</span> &#123;</span><br><span class="line">        <span class="built_in">this</span>.type = type;</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_">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> String <span class="title function_">getDescription</span><span class="params">()</span> &#123;</span><br><span class="line">        <span class="keyword">return</span> description;</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_">setDescription</span><span class="params">(String description)</span> &#123;</span><br><span class="line">        <span class="built_in">this</span>.description = description;</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;Book&#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;, type=&#x27;&quot;</span> + type + <span class="string">&#x27;\&#x27;&#x27;</span> +</span><br><span class="line">                <span class="string">&quot;, name=&#x27;&quot;</span> + name + <span class="string">&#x27;\&#x27;&#x27;</span> +</span><br><span class="line">                <span class="string">&quot;, description=&#x27;&quot;</span> + description + <span class="string">&#x27;\&#x27;&#x27;</span> +</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></li><li><p><code>步骤三：</code>编写Dao接口</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">interface</span> <span class="title class_">BookDao</span> &#123;</span><br><span class="line">    <span class="meta">@Insert(&quot;insert into tbl_book values (null, #&#123;type&#125;, #&#123;name&#125;, #&#123;description&#125;)&quot;)</span></span><br><span class="line">    <span class="keyword">void</span> <span class="title function_">save</span><span class="params">(Book book)</span>;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Update(&quot;update tbl_book set type=#&#123;type&#125;, `name`=#&#123;name&#125;, `description`=#&#123;description&#125; where id=#&#123;id&#125;&quot;)</span></span><br><span class="line">    <span class="keyword">void</span> <span class="title function_">update</span><span class="params">(Book book)</span>;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Delete(&quot;delete from tbl_book where id=#&#123;id&#125;&quot;)</span></span><br><span class="line">    <span class="keyword">void</span> <span class="title function_">delete</span><span class="params">(Integer id)</span>;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Select(&quot;select * from tbl_book where id=#&#123;id&#125;&quot;)</span></span><br><span class="line">    <span class="keyword">void</span> <span class="title function_">getById</span><span class="params">(Integer id)</span>;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Select(&quot;select * from tbl_book&quot;)</span></span><br><span class="line">    <span class="keyword">void</span> <span class="title function_">getAll</span><span class="params">()</span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></li><li><p><code>步骤四：</code>编写Service接口及其实现类</p><ul><li>BookService</li><li>BookServiceImpl</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></pre></td><td class="code"><pre><span class="line"><span class="meta">@Transactional</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">interface</span> <span class="title class_">BookService</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"><span class="comment">     * <span class="doctag">@param</span> book</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@return</span></span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    <span class="type">boolean</span> <span class="title function_">save</span><span class="params">(Book book)</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 class="doctag">@param</span> book</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@return</span></span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    <span class="type">boolean</span> <span class="title function_">update</span><span class="params">(Book book)</span>;</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 class="doctag">@param</span> id</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@return</span></span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    <span class="type">boolean</span> <span class="title function_">delete</span><span class="params">(Integer id)</span>;</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 class="doctag">@param</span> id</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@return</span></span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    Book <span class="title function_">getById</span><span class="params">(Integer 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 class="doctag">@return</span></span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    List&lt;Book&gt; <span class="title function_">getAll</span><span class="params">()</span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></li></ul><ul><li><p>```<br>步骤五：</p><figure class="highlight"><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><br><span class="line">编写Controller类</span><br><span class="line"></span><br></pre></td></tr></table></figure><p>@RestController<br>@RequestMapping(“/books”)<br>public class BookController {</p><pre><code>@Autowiredprivate BookService bookService;@PostMappingpublic boolean save(@RequestBody Book book) &#123;    return bookService.save(book);&#125;@PutMappingpublic boolean update(@RequestBody Book book) &#123;    return bookService.update(book);&#125;@DeleteMapping(&quot;/&#123;id&#125;&quot;)public boolean delete(@PathVariable Integer id) &#123;    return bookService.delete(id);&#125;@GetMapping(&quot;/&#123;id&#125;&quot;)public Book getById(@PathVariable Integer id) &#123;    return bookService.getById(id);&#125;@GetMappingpublic List&lt;Book&gt; getAll() &#123;    return bookService.getAll();&#125;</code></pre><p>}</p><figure class="highlight clean"><table><tr><td class="gutter"><pre><span class="line">1</span><br><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><br><span class="line">### 单元测试</span><br><span class="line"></span><br><span class="line">- ```</span><br><span class="line">  步骤一：</span><br></pre></td></tr></table></figure><p>新建测试类</p><figure class="highlight less"><table><tr><td class="gutter"><pre><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="variable">@RunWith</span>(SpringJUnit4ClassRunner.class)</span><br><span class="line"><span class="variable">@ContextConfiguration</span>(classes = SpringConfig.class)</span><br><span class="line">public class BookServiceTest &#123;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></li><li><p>```<br>步骤二：</p><figure class="highlight"><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><br><span class="line">注入Service</span><br><span class="line"></span><br></pre></td></tr></table></figure><p>@RunWith(SpringJUnit4ClassRunner.class)<br>@ContextConfiguration(classes = SpringConfig.class)<br>public class BookServiceTest {</p><pre><code>@Autowiredprivate BookService bookService;</code></pre><p>}</p><figure class="highlight autohotkey"><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><br><span class="line">- ```</span><br><span class="line">  步骤三：</span><br></pre></td></tr></table></figure><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></pre></td><td class="code"><pre><span class="line"><span class="meta">@RunWith(SpringJUnit4ClassRunner.class)</span></span><br><span class="line"><span class="meta">@ContextConfiguration(classes = SpringConfig.class)</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">BookServiceTest</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> BookService bookService;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Test</span></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">testGetById</span><span class="params">()</span> &#123;</span><br><span class="line">        <span class="type">Book</span> <span class="variable">book</span> <span class="operator">=</span> bookService.getById(<span class="number">1</span>);</span><br><span class="line">        System.out.println(book);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Test</span></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">testGetAll</span><span class="params">()</span> &#123;</span><br><span class="line">        <span class="keyword">for</span> (Book book : bookService.getAll()) &#123;</span><br><span class="line">            System.out.println(book);</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></li></ul><h3 id="PostMan测试"><a href="#PostMan测试" class="headerlink" title="PostMan测试"></a>PostMan测试</h3><ul><li><p><code>新增</code><br>发送<code>POST</code>请求与数据，访问<code>localhost:8080/books</code></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></pre></td><td class="code"><pre><span class="line"><span class="punctuation">&#123;</span></span><br><span class="line"><span class="attr">&quot;type&quot;</span><span class="punctuation">:</span><span class="string">&quot;类别测试数据&quot;</span><span class="punctuation">,</span></span><br><span class="line">    <span class="attr">&quot;name&quot;</span><span class="punctuation">:</span><span class="string">&quot;书名测试数据&quot;</span><span class="punctuation">,</span></span><br><span class="line">    <span class="attr">&quot;description&quot;</span><span class="punctuation">:</span><span class="string">&quot;描述测试数据&quot;</span></span><br><span class="line"><span class="punctuation">&#125;</span></span><br></pre></td></tr></table></figure><p>数据库中能看到新增的数据</p></li><li><p><code>修改</code><br>发送<code>PUT</code>请求与数据，访问<code>localhost:8080/books</code></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></pre></td><td class="code"><pre><span class="line"><span class="punctuation">&#123;</span></span><br><span class="line">    <span class="attr">&quot;id&quot;</span><span class="punctuation">:</span><span class="string">&quot;13&quot;</span><span class="punctuation">,</span></span><br><span class="line"><span class="attr">&quot;type&quot;</span><span class="punctuation">:</span><span class="string">&quot;类别测试数据&quot;</span><span class="punctuation">,</span></span><br><span class="line">    <span class="attr">&quot;name&quot;</span><span class="punctuation">:</span><span class="string">&quot;书名测试数据9527&quot;</span><span class="punctuation">,</span></span><br><span class="line">    <span class="attr">&quot;description&quot;</span><span class="punctuation">:</span><span class="string">&quot;描述测试数据&quot;</span></span><br><span class="line"><span class="punctuation">&#125;</span></span><br></pre></td></tr></table></figure><p>数据库中能看到修改后的数据</p></li><li><p><code>删除</code><br>发送DELETE请求，访问<code>localhost:8080/books/13</code><br>数据库中能看到id为13的数据不见了</p></li><li><p><code>查询单个</code><br>发送<code>GET</code>请求，访问<code>localhost:8080/books/1</code><br>可以查询到ID为1的数据，在PostMan中表现为JSON数据</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></pre></td><td class="code"><pre><span class="line"><span class="punctuation">&#123;</span></span><br><span class="line">    <span class="attr">&quot;id&quot;</span><span class="punctuation">:</span> <span class="number">1</span><span class="punctuation">,</span></span><br><span class="line">    <span class="attr">&quot;type&quot;</span><span class="punctuation">:</span> <span class="string">&quot;计算机理论&quot;</span><span class="punctuation">,</span></span><br><span class="line">    <span class="attr">&quot;name&quot;</span><span class="punctuation">:</span> <span class="string">&quot;Spring实战 第五版&quot;</span><span class="punctuation">,</span></span><br><span class="line">    <span class="attr">&quot;description&quot;</span><span class="punctuation">:</span> <span class="string">&quot;Spring入门经典教程，深入理解Spring原理技术内幕&quot;</span></span><br><span class="line"><span class="punctuation">&#125;</span></span><br></pre></td></tr></table></figure></li><li><p><code>查询所有</code><br>发送<code>GET</code>请求，访问<code>localhost:8080/books</code><br>PostMan中以JSON对象数组的形式显示了数据库中的所有数据</p></li></ul><h2 id="统一结果封装"><a href="#统一结果封装" class="headerlink" title="统一结果封装"></a>统一结果封装</h2><h3 id="表现层与前端数据传输协议定义"><a href="#表现层与前端数据传输协议定义" class="headerlink" title="表现层与前端数据传输协议定义"></a>表现层与前端数据传输协议定义</h3><p>SSM整合以及功能模块开发完成后，接下来我们在上述案例的基础上，分析一下有哪些问题需要我们解决。<br>首先第一个问题是：</p><ul><li><p>在Controller层增删改操作完成后，返回给前端的是boolean类型的数据</p><blockquote><p>true</p></blockquote></li><li><p>在Controller层查询单个，返回给前端的是对象</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></pre></td><td class="code"><pre><span class="line"><span class="punctuation">&#123;</span></span><br><span class="line">    <span class="attr">&quot;id&quot;</span><span class="punctuation">:</span> <span class="number">1</span><span class="punctuation">,</span></span><br><span class="line">    <span class="attr">&quot;type&quot;</span><span class="punctuation">:</span> <span class="string">&quot;计算机理论&quot;</span><span class="punctuation">,</span></span><br><span class="line">    <span class="attr">&quot;name&quot;</span><span class="punctuation">:</span> <span class="string">&quot;Spring实战 第五版&quot;</span><span class="punctuation">,</span></span><br><span class="line">    <span class="attr">&quot;description&quot;</span><span class="punctuation">:</span> <span class="string">&quot;Spring入门经典教程，深入理解Spring原理技术内幕&quot;</span></span><br><span class="line"><span class="punctuation">&#125;</span></span><br></pre></td></tr></table></figure></li><li><p>在Controller层查询所有，返回给前端的是集合对象</p><figure class="highlight erlang"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><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><br><span class="line">    &#123;</span><br><span class="line">        <span class="string">&quot;id&quot;</span>: <span class="number">1</span>,</span><br><span class="line">        <span class="string">&quot;type&quot;</span>: <span class="string">&quot;计算机理论&quot;</span>,</span><br><span class="line">        <span class="string">&quot;name&quot;</span>: <span class="string">&quot;Spring实战 第五版&quot;</span>,</span><br><span class="line">        <span class="string">&quot;description&quot;</span>: <span class="string">&quot;Spring入门经典教程，深入理解Spring原理技术内幕&quot;</span></span><br><span class="line">    &#125;,</span><br><span class="line">    &#123;</span><br><span class="line">        <span class="string">&quot;id&quot;</span>: <span class="number">2</span>,</span><br><span class="line">        <span class="string">&quot;type&quot;</span>: <span class="string">&quot;计算机理论&quot;</span>,</span><br><span class="line">        <span class="string">&quot;name&quot;</span>: <span class="string">&quot;Spring 5核心原理与30个类手写实践&quot;</span>,</span><br><span class="line">        <span class="string">&quot;description&quot;</span>: <span class="string">&quot;十年沉淀之作，手写Spring精华思想&quot;</span></span><br><span class="line">    &#125;,</span><br><span class="line">    ...</span><br><span class="line">]</span><br></pre></td></tr></table></figure></li><li><p>目前我们就已经有<code>三种数据类型</code>返回给前端了，随着业务的增长，我们需要返回的数据类型就会<code>越来越多</code>。那么前端开发人员在解析数据的时候就比较<code>凌乱</code>了，所以对于前端来说，如果后端能返回一个<code>统一的数据结果</code>，前端在解析的时候就可以按照一种方式进行解析，开发就会变得更加简单</p></li><li><p>所以现在我们需要解决的问题就是<code>如何将返回的结果数据进行统一</code>，具体如何来做，大体思路如下</p><ul><li><p>为了</p><figure class="highlight"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">封装返回的结果数据</span><br></pre></td></tr></table></figure><p>：创建结果模型类，封装数据到</p><figure class="highlight haskell"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="class"><span class="keyword">data</span></span></span><br></pre></td></tr></table></figure><p>属性中</p><ul><li>我们可以设置data的数据类型为<code>Object</code>，这样data中就可以放任意的结果类型了，包括但不限于上面的<code>boolean</code>、<code>对象</code>、<code>集合对象</code></li></ul></li><li><p>为了封装返回的数据是</p><figure class="highlight"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">何种操作</span><br></pre></td></tr></table></figure><p>，以及是否</p><figure class="highlight"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">操作成功</span><br></pre></td></tr></table></figure><p>：封装操作结果到</p><figure class="highlight clean"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">code</span></span><br></pre></td></tr></table></figure><p>属性中</p><ul><li>例如增删改操作返回的都是<code>true</code>，那我们怎么分辨这个<code>true</code>到底是<code>增</code>还是<code>删</code>还是<code>改</code>呢？我们就通过这个<code>code</code>来区分</li></ul></li><li><p>操作失败后，需要封装</p><figure class="highlight"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">返回错误信息</span><br></pre></td></tr></table></figure><p>提示给用户：封装特殊消息到message(msg)属性中</p><ul><li>例如查询或删除的目标不存在，会返回null，那么此时我们需要提示<code>查询/删除的目标不存在，请重试！</code></li></ul></li></ul></li><li><p>那么之前的三种返回方式就可以变为如下形式</p><ul><li>boolean</li><li>对象</li><li>对象集合</li></ul><p>规则可以自己定<br>这里前三位是固定的<br>第四位表示不同的操作<br>末位表示成功/失败，1成功，0失败</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></pre></td><td class="code"><pre><span class="line"><span class="punctuation">&#123;</span></span><br><span class="line">    <span class="attr">&quot;code&quot;</span><span class="punctuation">:</span><span class="number">20011</span><span class="punctuation">,</span></span><br><span class="line">    <span class="attr">&quot;data&quot;</span><span class="punctuation">:</span><span class="literal"><span class="keyword">true</span></span></span><br><span class="line"><span class="punctuation">&#125;</span></span><br></pre></td></tr></table></figure></li></ul><ul><li><p>根据分析，我们可以设置统一数据返回结果类</p><figure class="highlight fortran"><table><tr><td class="gutter"><pre><span class="line">1</span><br><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> Result&#123;</span><br><span class="line"><span class="keyword">private</span> Object <span class="keyword">data</span>;</span><br><span class="line"><span class="keyword">private</span> <span class="keyword">Integer</span> code;</span><br><span class="line"><span class="keyword">private</span> String msg;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>注意：Result类名及类中的字段并不是固定的，可以根据需要自行增减提供若干个构造方法，方便操作。</p></li></ul><h3 id="表现层与前端数据传输协议实现"><a href="#表现层与前端数据传输协议实现" class="headerlink" title="表现层与前端数据传输协议实现"></a>表现层与前端数据传输协议实现</h3><p>前面我们已经分析了如何封装返回结果数据，现在我们来具体实现一下</p><p>对于结果封装，我们应该是在表现层进行处理，所以我们把结果类放在controller包下，当然你也可以放在domain包，这个都是可以的，具体如何实现结果封装，具体的步骤如下</p><ul><li><p>```<br>步骤一：</p><figure class="highlight isbl"><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><br><span class="line">创建<span class="variable"><span class="class">Result</span></span>类</span><br><span class="line"></span><br></pre></td></tr></table></figure><p>public class Result {</p><pre><code>//描述统一格式中的编码，用于区分操作，可以简化配置0或1表示成功失败private Integer code;//描述统一格式中的数据private Object data;//描述统一格式中的消息，可选属性private String msg;public Result() &#123;&#125;//构造器可以根据自己的需要来编写public Result(Integer code, Object data) &#123;    this.code = code;    this.data = data;&#125;public Result(Integer code, Object data, String msg) &#123;    this.code = code;    this.data = data;    this.msg = msg;&#125;public Integer getCode() &#123;    return code;&#125;public void setCode(Integer code) &#123;    this.code = code;&#125;public Object getData() &#123;    return data;&#125;public void setData(Object data) &#123;    this.data = data;&#125;public String getMsg() &#123;    return msg;&#125;public void setMsg(String msg) &#123;    this.msg = msg;&#125;@Overridepublic String toString() &#123;    return &quot;Result&#123;&quot; +            &quot;code=&quot; + code +            &quot;, data=&quot; + data +            &quot;, msg=&#39;&quot; + msg + &#39;\&#39;&#39; +            &#39;&#125;&#39;;&#125;</code></pre><p>}</p><figure class="highlight autohotkey"><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><br><span class="line">- `步骤二：`定义返回码Code类</span><br><span class="line"></span><br></pre></td></tr></table></figure><p>public class Code {</p><pre><code>public static final Integer SAVE_OK = 20011;public static final Integer UPDATE_OK = 20021;public static final Integer DELETE_OK = 20031;public static final Integer GET_OK = 20041;public static final Integer SAVE_ERR = 20010;public static final Integer UPDATE_ERR = 20020;public static final Integer DELETE_ERR = 20030;public static final Integer GET_ERR = 20040;</code></pre><p>}</p><figure class="highlight autohotkey"><table><tr><td class="gutter"><pre><span class="line">1</span><br><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><br><span class="line">  注意：code类中的常量设计也不是固定的，可以根据需要自行增减，例如将查询再进行细分为`GET_OK`，`GET_ALL_OK`，`GET_PAGE_OK`等。</span><br><span class="line"></span><br><span class="line">- `步骤三：`修改Controller类的返回值</span><br><span class="line"></span><br></pre></td></tr></table></figure><p>@RestController<br>@RequestMapping(“/books”)<br>public class BookController {</p><pre><code>@Autowiredprivate BookService bookService;@PostMappingpublic Result save(@RequestBody Book book) &#123;    boolean flag = bookService.save(book);    return new Result(flag ? Code.SAVE_OK : Code.SAVE_ERR, flag);&#125;@PutMappingpublic Result update(@RequestBody Book book) &#123;    boolean flag = bookService.update(book);    return new Result(flag ? Code.UPDATE_OK : Code.UPDATE_ERR, flag);&#125;@DeleteMapping(&quot;/&#123;id&#125;&quot;)public Result delete(@PathVariable Integer id) &#123;    boolean flag = bookService.delete(id);    return new Result(flag ? Code.DELETE_OK : Code.DELETE_ERR, flag);&#125;@GetMapping(&quot;/&#123;id&#125;&quot;)public Result getById(@PathVariable Integer id) &#123;    Book book = bookService.getById(id);    Integer code = book == null ? Code.GET_ERR : Code.GET_OK;    String msg = book == null ? &quot;数据查询失败，请重试！&quot; : &quot;&quot;;    return new Result(code, book, msg);&#125;@GetMappingpublic Result getAll() &#123;    List&lt;Book&gt; bookList = bookService.getAll();    Integer code = bookList == null ? Code.GET_ERR : Code.GET_OK;    String msg = bookList == null ? &quot;数据查询失败，请重试！&quot; : &quot;&quot;;    return new Result(code, bookList, msg);&#125;</code></pre><p>}</p><figure class="highlight autohotkey"><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><br><span class="line">- ```</span><br><span class="line">  步骤四：</span><br></pre></td></tr></table></figure><p>启动服务测试</p><p>五个方法的测试结果如下</p><ul><li>save</li><li>update</li><li>delete</li><li>getById</li><li>getAll</li></ul><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></pre></td><td class="code"><pre><span class="line"><span class="punctuation">&#123;</span></span><br><span class="line">    <span class="attr">&quot;code&quot;</span><span class="punctuation">:</span> <span class="number">20011</span><span class="punctuation">,</span></span><br><span class="line">    <span class="attr">&quot;data&quot;</span><span class="punctuation">:</span> <span class="literal"><span class="keyword">true</span></span><span class="punctuation">,</span></span><br><span class="line">    <span class="attr">&quot;msg&quot;</span><span class="punctuation">:</span> <span class="literal"><span class="keyword">null</span></span></span><br><span class="line"><span class="punctuation">&#125;</span></span><br></pre></td></tr></table></figure></li></ul><h2 id="统一异常处理"><a href="#统一异常处理" class="headerlink" title="统一异常处理"></a>统一异常处理</h2><h3 id="问题描述"><a href="#问题描述" class="headerlink" title="问题描述"></a>问题描述</h3><p>在学习这部分知识之前，我们先来演示一个效果，修改<code>BookController</code>的<code>getById()</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></pre></td><td class="code"><pre><span class="line"><span class="meta">@GetMapping(&quot;/&#123;id&#125;&quot;)</span></span><br><span class="line"><span class="keyword">public</span> Result <span class="title function_">getById</span><span class="params">(<span class="meta">@PathVariable</span> Integer id)</span> &#123;</span><br><span class="line">    <span class="comment">//当id为1的时候，手动添加了一个错误信息</span></span><br><span class="line">    <span class="keyword">if</span> (id == <span class="number">1</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="number">0</span>;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="type">Book</span> <span class="variable">book</span> <span class="operator">=</span> bookService.getById(id);</span><br><span class="line">    <span class="type">Integer</span> <span class="variable">code</span> <span class="operator">=</span> book == <span class="literal">null</span> ? Code.GET_ERR : Code.GET_OK;</span><br><span class="line">    <span class="type">String</span> <span class="variable">msg</span> <span class="operator">=</span> book == <span class="literal">null</span> ? <span class="string">&quot;数据查询失败，请重试！&quot;</span> : <span class="string">&quot;&quot;</span>;</span><br><span class="line">    <span class="keyword">return</span> <span class="keyword">new</span> <span class="title class_">Result</span>(code, book, msg);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>重新启动服务器，使用<code>PostMan</code>发送请求，当传入的<code>id为1</code>时，会出现如下效果<br><a href="https://pic.imgdb.cn/item/631c97e516f2c2beb1326946.jpg"><img src="data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7" alt="img"></a><br>前端接收到这个信息后，和我们之前约定的格式不一致，怎么解决呢？<br>在解决问题之前，我们先来看一下异常的种类，以及出现异常的原因：</p><ul><li>框架内部抛出的异常：因<code>使用不合规</code>导致</li><li>数据层抛出的异常：因使用<code>外部服务器故障</code>导致（例如：服务器访问超时）</li><li>业务层抛出的异常：因<code>业务逻辑书写错误</code>导致（例如：遍历业务书写操作，导致索引越界异常等）</li><li>表现层抛出的异常：因<code>数据收集</code>、<code>校验</code>等规则导致（例如：不匹配的数据类型间转换导致异常）</li><li>工具类抛出的异常：因工具类<code>书写不严谨</code>，<code>健壮性不足</code>导致（例如：必要时放的连接，长时间未释放等）</li></ul><p>了解完上面这些出现<code>异常的位置</code>，我们发现，在我们开发的<code>任何一个位置</code>都可能会出现异常，而且这些异常是<code>不能避免的</code>，所以我们就需要对这些异常来<code>进行处理</code>。</p><figure class="highlight"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">思考</span><br></pre></td></tr></table></figure><ol><li>各个层级均出现异常，那么异常处理代码要写在哪一层？<ul><li>所有的异常均抛出到表现层进行处理</li></ul></li><li>异常的种类很多，表现层如何将所有的异常都处理到呢？<ul><li>异常分类</li></ul></li><li>表现层处理异常，每个方法中单独书写，代码书写两巨大，且意义不强，如何解决呢？<ul><li>AOP</li></ul></li></ol><p>对于上面这些问题以及解决方案，SpringMVC已经为我们提供了一套了：</p><ul><li><p>异常处理器：</p><ul><li><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></pre></td><td class="code"><pre><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_">ProjectExceptionAdvice</span> &#123;</span><br><span class="line">    <span class="meta">@ExceptionHandler(Exception.class)</span></span><br><span class="line">    <span class="keyword">public</span> Result <span class="title function_">doException</span><span class="params">(Exception ex)</span> &#123;</span><br><span class="line">        <span class="keyword">return</span> <span class="keyword">new</span> <span class="title class_">Result</span>(<span class="number">666</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></li></ul></li></ul><h3 id="异常处理器的使用"><a href="#异常处理器的使用" class="headerlink" title="异常处理器的使用"></a>异常处理器的使用</h3><ul><li><p><code>步骤一：</code>创建异常处理器类</p><figure class="highlight pgsql"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><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">@RestControllerAdvice</span><br><span class="line"><span class="built_in">public</span> <span class="keyword">class</span> ProjectExceptionAdvice &#123;</span><br><span class="line">    @ExceptionHandler(<span class="keyword">Exception</span>.<span class="keyword">class</span>)</span><br><span class="line">    <span class="built_in">public</span> <span class="type">void</span> doException(<span class="keyword">Exception</span> ex) &#123;</span><br><span class="line">        <span class="keyword">System</span>.<span class="keyword">out</span>.println(&quot;嘿嘿，逮到一个异常~&quot;);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>注意：要确保SpringMvcConfig能够扫描到异常处理器类</p></li><li><p><code>步骤二：</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></pre></td><td class="code"><pre><span class="line"><span class="meta">@GetMapping(&quot;/&#123;id&#125;&quot;)</span></span><br><span class="line"><span class="keyword">public</span> Result <span class="title function_">getById</span><span class="params">(<span class="meta">@PathVariable</span> Integer id)</span> &#123;</span><br><span class="line">    <span class="comment">//当id为1的时候，手动添加了一个错误信息</span></span><br><span class="line">    <span class="keyword">if</span> (id == <span class="number">1</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="number">0</span>;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="type">Book</span> <span class="variable">book</span> <span class="operator">=</span> bookService.getById(id);</span><br><span class="line">    <span class="type">Integer</span> <span class="variable">code</span> <span class="operator">=</span> book == <span class="literal">null</span> ? Code.GET_ERR : Code.GET_OK;</span><br><span class="line">    <span class="type">String</span> <span class="variable">msg</span> <span class="operator">=</span> book == <span class="literal">null</span> ? <span class="string">&quot;数据查询失败，请重试！&quot;</span> : <span class="string">&quot;&quot;</span>;</span><br><span class="line">    <span class="keyword">return</span> <span class="keyword">new</span> <span class="title class_">Result</span>(code, book, msg);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></li><li><p>```<br>步骤三：</p><figure class="highlight"><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><br><span class="line">使用</span><br><span class="line"></span><br></pre></td></tr></table></figure><p>PostMan</p><figure class="highlight"><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><br><span class="line">发送</span><br><span class="line"></span><br></pre></td></tr></table></figure><p>GET</p><figure class="highlight"><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><br><span class="line">请求访问</span><br><span class="line"></span><br></pre></td></tr></table></figure><p>localhost:8080/books/1</p><figure class="highlight"><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><br><span class="line">控制台输出如下，说明异常已经被拦截，且执行了</span><br><span class="line"></span><br></pre></td></tr></table></figure><p>doException()</p><figure class="highlight nestedtext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><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><br><span class="line">  方法</span><br><span class="line"></span><br><span class="line">  <span class="punctuation">&gt;</span> <span class="string">嘿嘿，逮到一个异常~</span></span><br><span class="line"></span><br><span class="line">但是现在没有返回数据给前端，为了统一返回结果，我们继续修改异常处理器类</span><br><span class="line"></span><br></pre></td></tr></table></figure><p>@RestControllerAdvice<br>public class ProjectExceptionAdvice {<br>  @ExceptionHandler(Exception.class)<br>  public Result doException(Exception ex) {</p><pre><code>  System.out.println(&quot;嘿嘿，逮到一个异常~&quot;);  return new Result(666, null, &quot;嘿嘿，逮到一个异常~&quot;);</code></pre><p>  }<br>}</p><figure class="highlight"><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><br><span class="line">重启服务器，使用PostMan发送请求，此时就能接收到结果了</span><br><span class="line"></span><br></pre></td></tr></table></figure><p>{<br>  “code”: 666,<br>  “data”: null,<br>  “msg”: “嘿嘿，逮到一个异常~”<br>}</p><figure class="highlight elixir"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><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><br><span class="line"></span><br><span class="line"></span><br><span class="line">知识点：`<span class="variable">@RestControllerAdvice</span>`</span><br><span class="line"></span><br><span class="line">说明：此注解自带`<span class="variable">@ResponseBody</span>`注解与`<span class="variable">@Component</span>`注解，具备对应的功能</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"></span><br><span class="line">| 名称 |       <span class="variable">@RestControllerAdvice</span>        |</span><br><span class="line">| <span class="symbol">:--</span>: | <span class="symbol">:--------------------------------</span>: |</span><br><span class="line">| 类型 |               类注解               |</span><br><span class="line">| 位置 | <span class="title class_">Rest</span>风格开发的控制器增强类定义上方 |</span><br><span class="line">| 作用 |   为<span class="title class_">Rest</span>风格开发的控制器类做增强   |</span><br><span class="line"></span><br><span class="line">知识点：`<span class="variable">@ExceptionHandler</span>`</span><br><span class="line"></span><br><span class="line">说明：此类方法可以根据处理的异常不同，制作多个方法分别处理对应的异常</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"></span><br><span class="line">| 名称 |                      <span class="variable">@ExceptionHandler</span>                       |</span><br><span class="line">| <span class="symbol">:--</span>: | <span class="symbol">:----------------------------------------------------------</span>: |</span><br><span class="line">| 类型 |                           方法注解                           |</span><br><span class="line">| 位置 |                专用于异常处理的控制器方法上方                |</span><br><span class="line">| 作用 | 设置指定异常的处理方案，功能等同于控制器方法， 出现异常后终止原始控制器执行,并转入当前方法执行 |</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><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="variable">@ExceptionHandler</span>`，那得写多少个方法来处理各自的异常，所以我们在处理异常之前，需要对异常进行一个分类:</span><br><span class="line"></span><br><span class="line">- ```</span><br><span class="line">  业务异常</span><br></pre></td></tr></table></figure><p>（BusinessException）</p><ul><li>规范的用户行为产生的异常<ul><li>用户在页面输入内容的时候未按照指定格式进行数据填写，如在年龄框输入的是字符串</li></ul></li><li>不规范的用户行为操作产生的异常<ul><li>如用户手改URL，故意传递错误数据<code>localhost:8080/books/略略略</code></li></ul></li></ul></li><li><p>```<br>系统异常</p><figure class="highlight markdown"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><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><br><span class="line">  （SystemException）</span><br><span class="line"></span><br><span class="line"><span class="bullet">  -</span> 项目运行过程中可预计，但无法避免的异常</span><br><span class="line"><span class="bullet">    -</span> 如服务器宕机</span><br><span class="line"></span><br><span class="line"><span class="bullet">-</span> <span class="code">```</span></span><br><span class="line"><span class="code">  其他异常</span></span><br></pre></td></tr></table></figure><p>（Exception）</p><ul><li>编程人员未预期到的异常<ul><li>如：系统找不到指定文件</li></ul></li></ul></li></ul><p>将异常分类以后，针对不同类型的异常，要提供具体的解决方案</p><h3 id="异常解决方案"><a href="#异常解决方案" class="headerlink" title="异常解决方案"></a>异常解决方案</h3><ul><li><p>```<br>业务异常</p><figure class="highlight markdown"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><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><br><span class="line">  （BusinessException）</span><br><span class="line"></span><br><span class="line"><span class="bullet">  -</span> 发送对应消息传递给用户，提醒规范操作</span><br><span class="line"><span class="bullet">    -</span> 大家常见的就是提示用户名已存在或密码格式不正确等</span><br><span class="line"></span><br><span class="line"><span class="bullet">-</span> <span class="code">```</span></span><br><span class="line"><span class="code">  系统异常</span></span><br></pre></td></tr></table></figure><p>（SystemException）</p><ul><li>发送固定消息传递给用户，安抚用户<ul><li>系统繁忙，请稍后再试</li><li>系统正在维护升级，请稍后再试</li><li>系统出问题，请联系系统管理员等</li></ul></li><li>发送特定消息给运维人员，提醒维护<ul><li>可以发送短信、邮箱或者是公司内部通信软件</li></ul></li><li>记录日志<ul><li>发消息给运维和记录日志对用户来说是不可见的，属于后台程序</li></ul></li></ul></li><li><p>```<br>其他异常</p><figure class="highlight markdown"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><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><br><span class="line">  （Exception）</span><br><span class="line"></span><br><span class="line"><span class="bullet">  -</span> 发送固定消息传递给用户，安抚用户</span><br><span class="line"><span class="bullet">  -</span> 发送特定消息给编程人员，提醒维护（纳入预期范围内）</span><br><span class="line"><span class="bullet">    -</span> 一般是程序没有考虑全，比如未做非空校验等</span><br><span class="line"><span class="bullet">  -</span> 记录日志</span><br><span class="line"></span><br><span class="line"><span class="section">### 具体实现</span></span><br><span class="line"></span><br><span class="line"><span class="bullet">-</span> 思路:</span><br><span class="line"></span><br><span class="line"><span class="bullet">  1.</span> 先通过自定义异常，完成BusinessException和SystemException的定义</span><br><span class="line"><span class="bullet">  2.</span> 将其他异常包装成自定义异常类型</span><br><span class="line"><span class="bullet">  3.</span> 在异常处理器类中对不同的异常进行处理</span><br><span class="line"></span><br><span class="line"><span class="bullet">-</span> <span class="code">`步骤一：`</span>自定义异常类</span><br><span class="line"></span><br><span class="line"><span class="bullet">  -</span> SystemException</span><br><span class="line"><span class="bullet">  -</span> BusinessException</span><br><span class="line"></span><br></pre></td></tr></table></figure><p>public class SystemException extends RuntimeException {</p><pre><code>private Integer code;public Integer getCode() &#123;    return code;&#125;public void setCode(Integer code) &#123;    this.code = code;&#125;public SystemException() &#123;&#125;public SystemException(Integer code) &#123;    this.code = code;&#125;public SystemException(Integer code, String message) &#123;    super(message);    this.code = code;&#125;public SystemException(Integer code, String message, Throwable cause) &#123;    super(message, cause);    this.code = code;&#125;</code></pre><p>}</p><figure class="highlight autohotkey"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><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><br><span class="line">  </span><br><span class="line"></span><br><span class="line">  说明：</span><br><span class="line"></span><br><span class="line">  - 让自定义异常类继承`RuntimeException`的好处是，后期在抛出这两个异常的时候，就不用在`try..catch..`或`throws`了</span><br><span class="line">  - 自定义异常类中添加`code`属性的原因是为了更好的区分异常是来自哪个业务的</span><br><span class="line"></span><br><span class="line">- `步骤二：`将其他异常包成自定义异常</span><br><span class="line">  假如在`BookServiceImpl`的`getById`方法抛异常了，该如何来包装呢?</span><br><span class="line"></span><br><span class="line">  具体的包装方式有：</span><br><span class="line"></span><br><span class="line">  - 方式一：`try&#123;&#125;catch()&#123;&#125;`在catch中重新<span class="keyword">throw</span>我们自定义异常即可。</span><br><span class="line">  - 方式二：直接`throw`自定义异常即可</span><br><span class="line"></span><br></pre></td></tr></table></figure><p>public Book getById(Integer id) {</p><pre><code>//模拟业务异常，包装成自定义异常if(id == 1)&#123;    throw new BusinessException(Code.BUSINESS_ERR,&quot;你别给我乱改URL噢&quot;);&#125;//模拟系统异常，将可能出现的异常进行包装，转换成自定义异常try&#123;    int i = 1/0;&#125;catch (Exception e)&#123;    throw new SystemException(Code.SYSTEM_TIMEOUT_ERR,&quot;服务器访问超时，请重试!&quot;,e);&#125;return bookDao.getById(id);</code></pre><p>}</p><figure class="highlight armasm"><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><br><span class="line">上面为了使`<span class="meta">code</span>`看着更专业些，我们在<span class="meta">Code</span>类中再新增需要的属性</span><br><span class="line"></span><br></pre></td></tr></table></figure><p>public class Code {</p><pre><code>public static final Integer SAVE_OK = 20011;public static final Integer UPDATE_OK = 20021;public static final Integer DELETE_OK = 20031;public static final Integer GET_OK = 20041;public static final Integer SAVE_ERR = 20010;public static final Integer UPDATE_ERR = 20020;public static final Integer DELETE_ERR = 20030;public static final Integer GET_ERR = 20040;public static final Integer SYSTEM_ERR = 50001;public static final Integer SYSTEM_TIMEOUT_ERR = 50002;public static final Integer SYSTEM_UNKNOW_ERR = 59999;public static final Integer BUSINESS_ERR = 60001;</code></pre><p>}</p><figure class="highlight autohotkey"><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><br><span class="line">- ```</span><br><span class="line">  步骤三：</span><br></pre></td></tr></table></figure><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="meta">@RestControllerAdvice</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">ProjectExceptionAdvice</span> &#123;</span><br><span class="line">    <span class="meta">@ExceptionHandler(SystemException.class)</span></span><br><span class="line">    <span class="keyword">public</span> Result <span class="title function_">doSystemException</span><span class="params">(SystemException ex)</span> &#123;</span><br><span class="line">        <span class="keyword">return</span> <span class="keyword">new</span> <span class="title class_">Result</span>(ex.getCode(), <span class="literal">null</span>, ex.getMessage());</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@ExceptionHandler(BusinessException.class)</span></span><br><span class="line">    <span class="keyword">public</span> Result <span class="title function_">doBusinessException</span><span class="params">(BusinessException ex)</span> &#123;</span><br><span class="line">        <span class="keyword">return</span> <span class="keyword">new</span> <span class="title class_">Result</span>(ex.getCode(), <span class="literal">null</span>, ex.getMessage());</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@ExceptionHandler(Exception.class)</span></span><br><span class="line">    <span class="keyword">public</span> Result <span class="title function_">doException</span><span class="params">(Exception ex)</span> &#123;</span><br><span class="line">        <span class="keyword">return</span> <span class="keyword">new</span> <span class="title class_">Result</span>(Code.SYSTEM_UNKNOW_ERR, <span class="literal">null</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></li><li><p>```<br>步骤四：</p><figure class="highlight"><table><tr><td class="gutter"><pre><span class="line">1</span><br><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><br><span class="line">运行程序</span><br><span class="line"></span><br><span class="line">根据ID查询，如果传入的参数为1，会报</span><br><span class="line"></span><br></pre></td></tr></table></figure><p>BusinessException</p><figure class="highlight"><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><br><span class="line">，错误信息应为</span><br><span class="line"></span><br></pre></td></tr></table></figure><p>你别给我乱改URL噢</p><figure class="highlight"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"></span><br></pre></td></tr></table></figure><p>{</p><pre><code>&quot;code&quot;: 60001,&quot;data&quot;: null,&quot;msg&quot;: &quot;你别给我乱改URL噢&quot;</code></pre><p>}</p><figure class="highlight"><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><br><span class="line">如果传入的是其他参数，会报</span><br><span class="line"></span><br></pre></td></tr></table></figure><p>SystemException</p><figure class="highlight"><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><br><span class="line">，错误信息应为</span><br><span class="line"></span><br></pre></td></tr></table></figure><p>服务器访问超时，请重试!</p><figure class="highlight"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"></span><br></pre></td></tr></table></figure><p>{</p><pre><code>&quot;code&quot;: 50002,&quot;data&quot;: null,&quot;msg&quot;: &quot;服务器访问超时，请重试!&quot;</code></pre><p>}</p><figure class="highlight markdown"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><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><br><span class="line">那么对于异常我们就已经处理完成了，不管后台哪一层抛出异常，都会以我们与前端约定好的方式进行返回，前端只需要把信息获取到，根据返回的正确与否来展示不同的内容即可。</span><br><span class="line"></span><br><span class="line">[<span class="string">![img</span>](<span class="link">data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7</span>)](<span class="link">https://pic.imgdb.cn/item/631d597316f2c2beb1d20ccd.jpg</span>)</span><br><span class="line"></span><br><span class="line"><span class="section">## 前后台协议联调</span></span><br><span class="line"></span><br><span class="line"><span class="section">### 环境准备</span></span><br><span class="line"></span><br><span class="line"><span class="bullet">-</span> 导入提供好的前端页面，如果想自己写页面，也可以用element-ui，有空了考虑考虑</span><br><span class="line"></span><br><span class="line"><span class="bullet">-</span> 由于添加了静态资源，SpringMVC会拦截，所以需要在对静态资源放行</span><br><span class="line"></span><br><span class="line"><span class="bullet">  -</span> 新建SpringMVCSupport类，继承</span><br><span class="line"></span><br></pre></td></tr></table></figure><p>  WebMvcConfigurationSupport</p>  <figure class="highlight"><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><br><span class="line">，并重写</span><br><span class="line"></span><br></pre></td></tr></table></figure><p>  addResourceHandlers()</p>  <figure class="highlight"><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><br><span class="line">方法</span><br><span class="line"></span><br></pre></td></tr></table></figure><p>  @Configuration<br>  public class SpringMvcSupport extends WebMvcConfigurationSupport {</p><pre><code>  @Override  protected void addResourceHandlers(ResourceHandlerRegistry registry) &#123;      registry.addResourceHandler(&quot;/css/**&quot;).addResourceLocations(&quot;/css/&quot;);      registry.addResourceHandler(&quot;/js/**&quot;).addResourceLocations(&quot;/js/&quot;);      registry.addResourceHandler(&quot;/pages/**&quot;).addResourceLocations(&quot;/pages/&quot;);      registry.addResourceHandler(&quot;/plugins/**&quot;).addResourceLocations(&quot;/plugins/&quot;);  &#125;</code></pre><p>  }</p>  <figure class="highlight asciidoc"><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><br><span class="line"><span class="bullet">- </span>同时也需要让SpringMvcConfig扫描到我们的配置类</span><br><span class="line"></span><br></pre></td></tr></table></figure><p>  @Configuration<br>  @ComponentScan({“com.blog.controller”,”com.blog.config”})<br>  @EnableWebMvc<br>  public class SpringMvcConfig {<br>  }</p>  <figure class="highlight markdown"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><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><br><span class="line">现在我们打开浏览器，访问<span class="code">`http://localhost:8080/pages/books.html`</span>，应该是可以正常看到页面的</span><br><span class="line"></span><br><span class="line"><span class="section">### 页面分析</span></span><br><span class="line"></span><br><span class="line">在完成增删改查操作之前，我们先来看看给我们提供的页面源码</span><br><span class="line"></span><br><span class="line"><span class="bullet">-</span> 源码</span><br><span class="line"><span class="bullet">-</span> 页头部分</span><br><span class="line"><span class="bullet">-</span> 表格部分</span><br><span class="line"><span class="bullet">-</span> 分页</span><br><span class="line"><span class="bullet">-</span> 新增按钮弹出框</span><br><span class="line"><span class="bullet">-</span> data</span><br><span class="line"><span class="bullet">-</span> 钩子函数</span><br><span class="line"><span class="bullet">-</span> methods</span><br><span class="line"><span class="bullet">-</span> DIY</span><br><span class="line"><span class="bullet">-</span> 最终页面</span><br><span class="line"></span><br></pre></td></tr></table></figure><p>&lt;!DOCTYPE html&gt;</p></li></ul><p><html></p><head>    <!-- 页面meta -->    <meta charset="utf-8">    <title>SpringMVC案例</title>    <!-- 引入样式 -->    <link rel="stylesheet" href="../plugins/elementui/index.css">    <link rel="stylesheet" href="../plugins/font-awesome/css/font-awesome.min.css">    <link rel="stylesheet" href="../css/style.css"></head><body class="hold-transition"><div id="app">    <div class="content-header">        <h1>图书管理</h1>    </div>    <div class="app-container">        <div class="box">            <div class="filter-container">                <el-input placeholder="图书名称" style="width: 200px;" class="filter-item"></el-input>                <el-button class="dalfBut">查询</el-button>                <el-button type="primary" class="butT" @click="openSave()">新建</el-button>            </div>            <el-table size="small" current-row-key="id" :data="dataList" stripe highlight-current-row>                <el-table-column type="index" align="center" label="序号"></el-table-column>                <el-table-column prop="type" label="图书类别" align="center"></el-table-column>                <el-table-column prop="name" label="图书名称" align="center"></el-table-column>                <el-table-column prop="description" label="描述" align="center"></el-table-column>                <el-table-column label="操作" align="center">                    <template slot-scope="scope">                        <el-button type="primary" size="mini">编辑</el-button>                        <el-button size="mini" type="danger">删除</el-button>                    </template>                </el-table-column>            </el-table>            <div class="pagination-container">                <el-pagination                        class="pagiantion"                        @current-change="handleCurrentChange"                        :current-page="pagination.currentPage"                        :page-size="pagination.pageSize"                        layout="total, prev, pager, next, jumper"                        :total="pagination.total">                </el-pagination>            </div>            <!-- 新增标签弹层 -->            <div class="add-form">                <el-dialog title="新增图书" :visible.sync="dialogFormVisible">                    <el-form ref="dataAddForm" :model="formData" :rules="rules" label-position="right"                             label-width="100px">                        <el-row>                            <el-col :span="12">                                <el-form-item label="图书类别" prop="type">                                    <el-input v-model="formData.type"/>                                </el-form-item>                            </el-col>                            <el-col :span="12">                                <el-form-item label="图书名称" prop="name">                                    <el-input v-model="formData.name"/>                                </el-form-item>                            </el-col>                        </el-row>                        <el-row>                            <el-col :span="24">                                <el-form-item label="描述">                                    <el-input v-model="formData.description" type="textarea"></el-input>                                </el-form-item>                            </el-col>                        </el-row>                    </el-form>                    <div slot="footer" class="dialog-footer">                        <el-button @click="dialogFormVisible = false">取消</el-button>                        <el-button type="primary" @click="saveBook()">确定</el-button>                    </div>                </el-dialog>            </div>        </div>    </div></div></body><!-- 引入组件库 --><script src="../js/vue.js"></script><script src="../plugins/elementui/index.js"></script><script type="text/javascript" src="../js/jquery.min.js"></script><script src="../js/axios-0.18.0.js"></script><script>    var vue = new Vue({        el: '#app',        data: {            dataList: [],//当前页要展示的分页列表数据            formData: {},//表单数据            dialogFormVisible: false,//增加表单是否可见            dialogFormVisible4Edit: false,//编辑表单是否可见            pagination: {},//分页模型数据，暂时弃用        },        //钩子函数，VUE对象初始化完成后自动执行        created() {        },        methods: {            // 重置表单            resetForm() {            },            // 弹出添加窗口            openSave() {            },            //添加            saveBook() {            },            //主页列表查询            getAll() {            },        }    })</script></html><figure class="highlight markdown"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><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><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="section">### 列表功能</span></span><br><span class="line"></span><br><span class="line">需求：页面加载完后发送异步请求到后台获取列表数据进行展示</span><br><span class="line"></span><br><span class="line"><span class="bullet">1.</span> 找到页面的钩子函数，<span class="code">`created()`</span></span><br><span class="line"><span class="bullet">2.</span> <span class="code">`created()`</span>方法中调用了<span class="code">`this.getAll()`</span>方法</span><br><span class="line"><span class="bullet">3.</span> 在getAll()方法中使用axios发送异步请求从后台获取数据</span><br><span class="line"><span class="bullet">4.</span> 访问的路径为<span class="code">`http://localhost/books`</span></span><br><span class="line"><span class="bullet">5.</span> 返回数据</span><br><span class="line"></span><br><span class="line">那么修改getAll()方法</span><br><span class="line"></span><br></pre></td></tr></table></figure>res.data`表示获取的`Result`对象，而`Result`对象的`data`属性才是真正的数据也就是将`Rusult.data`赋给了`this.dataListgetAll() {    axios.get("/books").then((res)=>{        this.dataList = res.data.data;    })}<figure class="highlight autohotkey"><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><br><span class="line">在钩子函数中直接调用`getAll()`即可</span><br><span class="line"></span><br></pre></td></tr></table></figure>created() {    this.getAll();}<figure class="highlight autohotkey"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><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><br><span class="line"><span class="title">那么现在重启服务器，打开浏览器访问`http://localhost:</span><span class="number">8080</span>/pages/books.html`，表格中可以正常显示数据了</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"></span><br><span class="line">### 添加功能</span><br><span class="line"></span><br><span class="line">需求：完成图片的新增功能模块</span><br><span class="line"></span><br><span class="line"><span class="number">1</span>. 找到页面上的`新建`按钮，按钮上绑定了`@click=<span class="string">&quot;openSave()&quot;</span>`方法</span><br><span class="line"><span class="number">2</span>. 在method中找到`openSave`方法，方法中打开新增面板</span><br><span class="line"><span class="number">3</span>. 新增面板中找到`确定`按钮,按钮上绑定了`@click=<span class="string">&quot;saveBook()&quot;</span>`方法</span><br><span class="line"><span class="number">4</span>. 在method中找到`saveBook`方法</span><br><span class="line"><span class="number">5</span>. 在方法中发送请求和数据，响应成功后将新增面板关闭并重新查询数据</span><br><span class="line"></span><br><span class="line">- ```</span><br><span class="line">  openSave</span><br></pre></td></tr></table></figure>  打开新增面板  <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></pre></td><td class="code"><pre><span class="line"><span class="title function_">openSave</span>(<span class="params"></span>) &#123;</span><br><span class="line">    <span class="variable language_">this</span>.<span class="property">dialogFormVisible</span> = <span class="literal">true</span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>- ```  saveBook  <figure class="highlight"><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><br><span class="line">方法发送异步请求并携带数据</span><br><span class="line"></span><br></pre></td></tr></table></figure>  saveBook () {      //发送ajax请求      axios.post("/books",this.formData).then((res)=>{          this.dialogFormVisible = false;          this.getAll();      });  }  <figure class="highlight markdown"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><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><br><span class="line"><span class="section">### 添加功能状态处理</span></span><br><span class="line"></span><br><span class="line">基础的新增功能已经完成，但是还有一些问题需要解决下:</span><br><span class="line"></span><br><span class="line">需求：新增成功是关闭面板，重新查询数据，那么新增失败以后该如何处理?</span><br><span class="line"></span><br><span class="line"><span class="bullet">1.</span> 在handlerAdd方法中根据后台返回的数据来进行不同的处理</span><br><span class="line"><span class="bullet">2.</span> 如果后台返回的是成功，则提示成功信息，并关闭面板</span><br><span class="line"><span class="bullet">3.</span> 如果后台返回的是失败，则提示错误信息</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="bullet">-</span> 修改前端页面</span><br><span class="line"></span><br></pre></td></tr></table></figure>  saveBook() {      axios.post("/books",this.formData).then((res)=>{          //20011是成功的状态码，成功之后就关闭对话框，并显示添加成功          if (res.data.code == 20011){              this.dialogFormVisible = false;              this.$message.success("添加成功")          //20010是失败的状态码，失败后给用户提示信息          }else if(res.data.code == 20010){              this.$message.error("添加失败");          //如果前两个都不满足，那就是SYSTEM_UNKNOW_ERR，未知异常了，显示未知异常的错误提示信息安抚用户情绪          }else {              this.$message.error(res.data.msg);          }      }).finally(()=>{          this.getAll();      })  }  <figure class="highlight angelscript"><table><tr><td class="gutter"><pre><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><br><span class="line">- 后台返回操作结果，将Dao层的增删改方法返回值从`<span class="built_in">void</span>`改成`<span class="built_in">int</span>`</span><br><span class="line">  如果添加失败，<span class="built_in">int</span>值为<span class="number">0</span>，添加成功则<span class="built_in">int</span>值为显示受影响的行数</span><br><span class="line"></span><br></pre></td></tr></table></figure>  public interface BookDao {      @Insert("insert into tbl_book values (null, #{type}, #{name}, #{description})")      int save(Book book);      @Update("update tbl_book set type=#{type}, `name`=#{name}, `description`=#{description} where id=#{id}")      int update(Book book);      @Delete("delete from tbl_book where id=#{id}")      int delete(Integer id);      @Select("select * from tbl_book where id=#{id}")      Book getById(Integer id);      @Select("select * from tbl_book")      List<Book> getAll();  }  <figure class="highlight actionscript"><table><tr><td class="gutter"><pre><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><br><span class="line">- 在BookServiceImpl中，增删改方法根据DAO的返回值来决定返回<span class="literal">true</span>/<span class="literal">false</span></span><br><span class="line">  如果受影响的行大于<span class="number">0</span>，则添加成功，否则添加失败</span><br><span class="line"></span><br></pre></td></tr></table></figure>  @Service  public class BookServiceImpl implements BookService {      @Autowired      private BookDao bookDao;      public boolean save(Book book) {          return bookDao.save(book) > 0;      }      public boolean update(Book book) {          return bookDao.update(book) > 0;      }      public boolean delete(Integer id) {         return bookDao.delete(id) > 0;      }      public Book getById(Integer id) {          return bookDao.getById(id);      }      public List<Book> getAll() {          return bookDao.getAll();      }  }  <figure class="highlight autohotkey"><table><tr><td class="gutter"><pre><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><br><span class="line">处理完新增后，会发现新增还存在一个问题，</span><br><span class="line">新增成功后，再次点击`新增`按钮会发现之前的数据还存在，这个时候就需要在新增的时候将表单内容清空。</span><br><span class="line"></span><br></pre></td></tr></table></figure>  // 重置表单  resetForm() {      this.formData = {};  }  // 弹出添加窗口  openSave() {      this.dialogFormVisible = true;      //每次弹出表单的时候，都重置一下数据      this.resetForm();  }  <figure class="highlight markdown"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><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><br><span class="line"><span class="section">### 修改功能</span></span><br><span class="line"></span><br><span class="line">需求:完成图书信息的修改功能</span><br><span class="line"></span><br><span class="line"><span class="bullet">1.</span> 找到页面中的<span class="code">`编辑`</span>按钮，该按钮绑定了<span class="code">`@click=&quot;openEdit(scope.row)&quot;`</span></span><br><span class="line"><span class="bullet">2.</span> 在method的<span class="code">`openEdit`</span>方法中发送异步请求根据ID查询图书信息</span><br><span class="line"><span class="bullet">3.</span> 根据后台返回的结果，判断是否查询成功</span><br><span class="line"><span class="bullet">   -</span> 如果查询成功打开修改面板回显数据，如果失败提示错误信息</span><br><span class="line"><span class="bullet">4.</span> 修改完成后找到修改面板的<span class="code">`确定`</span>按钮，该按钮绑定了<span class="code">`@click=&quot;handleEdit()&quot;`</span></span><br><span class="line"><span class="bullet">5.</span> 在method的<span class="code">`handleEdit`</span>方法中发送异步请求提交修改数据</span><br><span class="line"><span class="bullet">6.</span> 根据后台返回的结果，判断是否修改成功</span><br><span class="line"><span class="bullet">   -</span> 如果成功提示错误信息，关闭修改面板，重新查询数据，如果失败提示错误信息</span><br><span class="line"></span><br><span class="line"><span class="bullet">-</span> scope.row代表的是当前行的行数据，也就是说,scope.row就是选中行对应的json数据，如下:</span><br><span class="line"></span><br></pre></td></tr></table></figure>  {      "id": 1,      "type": "计算机理论",      "name": "Spring实战 第五版",      "description": "Spring入门经典教程，深入理解Spring原理技术内幕"  }  <figure class="highlight scss"><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><br><span class="line">- 修改<span class="built_in">openEdit</span>()方法</span><br><span class="line"></span><br></pre></td></tr></table></figure>  openEdit(row) {      axios.get("/books/" + row.id).then((res) => {          if (res.data.code == 20041) {              this.formData = res.data.data;              this.dialogFormVisible4Edit = true;          } else {              this.$message.error(res.data.msg);          }      });  }  <figure class="highlight autohotkey"><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><br><span class="line">修改`handleUpdate`方法</span><br><span class="line"></span><br></pre></td></tr></table></figure>handleEdit() {    axios.put("/books", this.formData).then((res) => {        if (res.data.code == 20021) {            this.dialogFormVisible4Edit = false;            this.$message.success("修改成功")        } else if (res.data.code == 20020) {            this.$message.error("修改失败")        } else {            this.$message.error(res.data.msg);        }    }).finally(() => {        this.getAll();    });}<figure class="highlight markdown"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><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><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="section">### 删除功能</span></span><br><span class="line"></span><br><span class="line">需求:完成页面的删除功能。</span><br><span class="line"></span><br><span class="line"><span class="bullet">1.</span> 找到页面的删除按钮，按钮上绑定了<span class="code">`@click=&quot;delete(scope.row)&quot;`</span></span><br><span class="line"><span class="bullet">2.</span> method的<span class="code">`delete`</span>方法弹出提示框</span><br><span class="line"><span class="bullet">3.</span> 用户点击取消，提示操作已经被取消。</span><br><span class="line"><span class="bullet">4.</span> 用户点击确定，发送异步请求并携带需要删除数据的主键ID</span><br><span class="line"><span class="bullet">5.</span> 根据后台返回结果做不同的操作</span><br><span class="line"><span class="bullet">   -</span> 如果返回成功，提示成功信息，并重新查询数据</span><br><span class="line"><span class="bullet">   -</span> 如果返回失败，提示错误信息，并重新查询数据</span><br><span class="line"></span><br><span class="line"><span class="bullet">-</span> 修改</span><br><span class="line"></span><br></pre></td></tr></table></figure>  delete  <figure class="highlight"><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><br><span class="line">方法</span><br><span class="line"></span><br></pre></td></tr></table></figure>  deleteBook(row) {      this.$confirm("此操作永久删除当前数据，是否继续？","提示",{          type:'info'      }).then(()=> {          axios.delete("/books/" + row.id).then((res) => {              if (res.data.code == 20031) {                  this.$message.success("删除成功")              } else if (res.data.code == 20030) {                  this.$message.error("删除失败")              }          }).finally(() => {              this.getAll();          });      }).catch(()=>{          this.$message.info("取消删除操作")      })  }  <figure class="highlight"><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><br><span class="line">至此增删改操作就都完成了，完整的前端代码如下</span><br><span class="line"></span><br></pre></td></tr></table></figure><!DOCTYPE html><html><head>    <!-- 页面meta -->    <meta charset="utf-8">    <title>SpringMVC案例</title>    <!-- 引入样式 -->    <link rel="stylesheet" href="../plugins/elementui/index.css">    <link rel="stylesheet" href="../plugins/font-awesome/css/font-awesome.min.css">    <link rel="stylesheet" href="../css/style.css"></head><body class="hold-transition"><div id="app">    <div class="content-header">        <h1>图书管理</h1>    </div>    <div class="app-container">        <div class="box">            <div class="filter-container">                <el-input placeholder="图书名称" style="width: 200px;" class="filter-item"></el-input>                <el-button class="dalfBut">查询</el-button>                <el-button type="primary" class="butT" @click="openSave()">新建</el-button>            </div>            <el-table size="small" current-row-key="id" :data="dataList" stripe highlight-current-row>                <el-table-column type="index" align="center" label="序号"></el-table-column>                <el-table-column prop="type" label="图书类别" align="center"></el-table-column>                <el-table-column prop="name" label="图书名称" align="center"></el-table-column>                <el-table-column prop="description" label="描述" align="center"></el-table-column>                <el-table-column label="操作" align="center">                    <template slot-scope="scope">                        <el-button type="primary" size="mini" @click="openEdit(scope.row)">编辑</el-button>                        <el-button size="mini" type="danger" @click="deleteBook(scope.row)">删除</el-button>                    </template>                </el-table-column>            </el-table>            <!-- 新增标签弹层 -->            <div class="add-form">                <el-dialog title="新增图书" :visible.sync="dialogFormVisible">                    <el-form ref="dataAddForm" :model="formData" label-position="right"                             label-width="100px">                        <el-row>                            <el-col :span="12">                                <el-form-item label="图书类别" prop="type">                                    <el-input v-model="formData.type"/>                                </el-form-item>                            </el-col>                            <el-col :span="12">                                <el-form-item label="图书名称" prop="name">                                    <el-input v-model="formData.name"/>                                </el-form-item>                            </el-col>                        </el-row>                        <el-row>                            <el-col :span="24">                                <el-form-item label="描述">                                    <el-input v-model="formData.description" type="textarea"></el-input>                                </el-form-item>                            </el-col>                        </el-row>                    </el-form>                    <div slot="footer" class="dialog-footer">                        <el-button @click="dialogFormVisible = false">取消</el-button>                        <el-button type="primary" @click="saveBook()">确定</el-button>                    </div>                </el-dialog>            </div>        </div>        <!-- 编辑标签弹层 -->        <div class="add-form">            <el-dialog title="编辑检查项" :visible.sync="dialogFormVisible4Edit">                <el-form ref="dataEditForm" :model="formData" label-position="right" label-width="100px">                    <el-row>                        <el-col :span="12">                            <el-form-item label="图书类别" prop="type">                                <el-input v-model="formData.type"/>                            </el-form-item>                        </el-col>                        <el-col :span="12">                            <el-form-item label="图书名称" prop="name">                                <el-input v-model="formData.name"/>                            </el-form-item>                        </el-col>                    </el-row>                    <el-row>                        <el-col :span="24">                            <el-form-item label="描述">                                <el-input v-model="formData.description" type="textarea"></el-input>                            </el-form-item>                        </el-col>                    </el-row>                </el-form>                <div slot="footer" class="dialog-footer">                    <el-button @click="dialogFormVisible4Edit = false">取消</el-button>                    <el-button type="primary" @click="handleEdit()">确定</el-button>                </div>            </el-dialog>        </div>    </div></div></div></body><!-- 引入组件库 --><script src="../js/vue.js"></script><script src="../plugins/elementui/index.js"></script><script type="text/javascript" src="../js/jquery.min.js"></script><script src="../js/axios-0.18.0.js"></script><p><script><br>    var vue = new Vue({</p><pre><code>    el: &#39;#app&#39;,    data: &#123;        dataList: [],//当前页要展示的分页列表数据        formData: &#123;&#125;,//表单数据        dialogFormVisible: false,//增加表单是否可见        dialogFormVisible4Edit: false,//编辑表单是否可见        pagination: &#123;&#125;,//分页模型数据，暂时弃用    &#125;,    //钩子函数，VUE对象初始化完成后自动执行    created() &#123;        this.getAll();    &#125;,    methods: &#123;        // 重置表单        resetForm() &#123;            this.formData = &#123;&#125;;        &#125;,        // 弹出添加窗口        openSave() &#123;            this.dialogFormVisible = true;            this.resetForm();        &#125;,        //添加        saveBook() &#123;            axios.post(&quot;/books&quot;, this.formData).then((res) =&gt; &#123;                if (res.data.code == 20011) &#123;                    this.dialogFormVisible = false;                    this.$message.success(&quot;添加成功&quot;)                &#125; else if (res.data.code == 20010) &#123;                    this.$message.error(&quot;添加失败&quot;);                &#125; else &#123;                    this.$message.error(res.data.msg);                &#125;            &#125;).finally(() =&gt; &#123;                this.getAll();            &#125;)        &#125;,        //主页列表查询        getAll() &#123;            axios.get(&quot;/books&quot;).then((res) =&gt; &#123;                this.dataList = res.data.data;            &#125;)        &#125;,        // 弹出编辑页面，调用查询，将查询到的数据赋给formData，这样就能回显数据了        openEdit(row) &#123;            axios.get(&quot;/books/&quot; + row.id).then((res) =&gt; &#123;                if (res.data.code == 20041) &#123;                    this.formData = res.data.data;                    this.dialogFormVisible4Edit = true;                &#125; else &#123;                    this.$message.error(res.data.msg);                &#125;            &#125;);        &#125;,        // 修改        handleEdit() &#123;            axios.put(&quot;/books&quot;, this.formData).then((res) =&gt; &#123;                if (res.data.code == 20021) &#123;                    this.dialogFormVisible4Edit = false;                    this.$message.success(&quot;修改成功&quot;)                &#125; else if (res.data.code == 20020) &#123;                    this.$message.error(&quot;修改失败&quot;)                &#125; else &#123;                    this.$message.error(res.data.msg);                &#125;            &#125;).finally(() =&gt; &#123;                this.getAll();            &#125;);        &#125;,        // 删除操作        deleteBook(row) &#123;            this.$confirm(&quot;此操作永久删除当前数据，是否继续？&quot;, &quot;提示&quot;, &#123;                type: &#39;info&#39;            &#125;).then(() =&gt; &#123;                axios.delete(&quot;/books/&quot; + row.id).then((res) =&gt; &#123;                    if (res.data.code == 20031) &#123;                        this.$message.success(&quot;删除成功&quot;)                    &#125; else if (res.data.code == 20030) &#123;                        this.$message.error(&quot;删除失败&quot;)                    &#125;                &#125;).finally(() =&gt; &#123;                    this.getAll();                &#125;);            &#125;).catch(() =&gt; &#123;                this.$message.info(&quot;取消删除操作&quot;)            &#125;)        &#125;    &#125;&#125;)</code></pre><p>&lt;/script&gt;<br>&lt;/html&gt;<br><figure class="highlight markdown"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><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><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="section">## 拦截器</span></span><br><span class="line"></span><br><span class="line"><span class="section">### 拦截器概念</span></span><br><span class="line"></span><br><span class="line">在学习拦截器的概念之前，我们先看一张图</span><br><span class="line">[<span class="string">![img</span>](<span class="link">data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7</span>)](<span class="link">https://pic.imgdb.cn/item/631e964416f2c2beb1ece79e.jpg</span>)</span><br><span class="line"></span><br><span class="line"><span class="bullet">1.</span> 浏览器发送一个请求，会先到Tomcat服务器的web服务器</span><br><span class="line"><span class="bullet">2.</span> Tomcat服务器接收到请求后，会先去判断请求的是<span class="code">`静态资源`</span>还是<span class="code">`动态资源`</span></span><br><span class="line"><span class="bullet">3.</span> 如果是静态资源，会直接到Tomcat的项目部署目录下直接访问</span><br><span class="line"><span class="bullet">4.</span> 如果是动态资源，就需要交给项目的后台代码进行处理</span><br><span class="line"><span class="bullet">5.</span> 在找到具体的方法之前，我们可以去配置过滤器（可以配置多个），按照顺序进行执行（在这里就可以进行权限校验）</span><br><span class="line"><span class="bullet">6.</span> 然后进入到中央处理器（SpringMVC中的内容），SpringMVC会根据配置的规则进行拦截</span><br><span class="line"><span class="bullet">7.</span> 如果满足规则，则进行处理，找到其对应的<span class="code">`Controller`</span>类中的方法进行之星，完成后返回结果</span><br><span class="line"><span class="bullet">8.</span> 如果不满足规则，则不进行处理</span><br><span class="line"><span class="bullet">9.</span> 这个时候，如果我们需要在每个Controller方法执行的前后添加业务，具体该如何来实现？</span><br><span class="line"><span class="bullet">   -</span> 这个就是拦截器要做的事</span><br><span class="line"></span><br><span class="line"><span class="bullet">-</span> 拦截器（Interceptor）是一种动态拦截方法调用的机制，在SpringMVC中动态拦截控制器方法的执行</span><br><span class="line"></span><br><span class="line"><span class="bullet">  -</span> <span class="code">```</span></span><br><span class="line"><span class="code">    作用：</span></span><br></pre></td></tr></table></figure></p><pre><code>- 在指定的方法调用前后执行预先设定的代码- 阻止原始方法的执行</code></pre><ul><li><code>总结：</code>拦截器就是用来作增强</li></ul><ul><li><p>但是这个拦截器貌似跟我们之前学的过滤器很像啊，不管是从作用上来看还是从执行顺序上来看</p><ul><li>那么拦截器和过滤器之间的区别是什么呢？<ul><li><code>归属不同：</code>Filter属于Servlet技术，而Interceptor属于SpringMVC技术</li><li><code>拦截内容不同：</code>Filter对所有访问进行增强，Interceptor仅对SpringMVC的访问进行增强</li></ul></li></ul></li></ul><p><a href="https://pic.imgdb.cn/item/631e9b5416f2c2beb1f1d381.jpg"><img src="data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7" alt="img"></a></p><h3 id="拦截器入门案例"><a href="#拦截器入门案例" class="headerlink" title="拦截器入门案例"></a>拦截器入门案例</h3><h3 id="环境准备"><a href="#环境准备" class="headerlink" title="环境准备"></a>环境准备</h3><ul><li><p>创建一个Web的Maven项目</p></li><li><p>导入坐标</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></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>javax.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>javax.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>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 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<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-webmvc<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.2.10.RELEASE<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>com.fasterxml.jackson.core<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>jackson-databind<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.9.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></li><li><p>创建对应的配置类</p><ul><li>SpringMvcConfig</li><li>ServletContainersInitConfig</li></ul><figure class="highlight less"><table><tr><td class="gutter"><pre><span class="line">1</span><br><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="variable">@Configuration</span></span><br><span class="line"><span class="variable">@ComponentScan</span>(<span class="string">&quot;com.blog.controller&quot;</span>)</span><br><span class="line"><span class="variable">@EnableWebMvc</span></span><br><span class="line">public class SpringMvcConfig &#123;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></li></ul><ul><li><p>编写Controller类</p><figure class="highlight typescript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><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="meta">@RestController</span></span><br><span class="line"><span class="meta">@RequestMapping</span>(<span class="string">&quot;/books&quot;</span>)</span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">BookController</span> &#123;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@PostMapping</span></span><br><span class="line">    <span class="keyword">public</span> <span class="title class_">String</span> <span class="title function_">save</span>(<span class="params"><span class="meta">@RequestBody</span> <span class="title class_">Book</span> book</span>)&#123;</span><br><span class="line">        <span class="title class_">System</span>.<span class="property">out</span>.<span class="title function_">println</span>(<span class="string">&quot;book save ..&quot;</span> + book);</span><br><span class="line">        <span class="keyword">return</span> <span class="string">&quot;&#123;&#x27;module&#x27;:&#x27;book save&#x27;&#125;&quot;</span>;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@DeleteMapping</span>(<span class="string">&quot;/&#123;id&#125;&quot;</span>)</span><br><span class="line">    <span class="keyword">public</span> <span class="title class_">String</span> <span class="title function_">delete</span>(<span class="params"><span class="meta">@PathVariable</span> <span class="title class_">Integer</span> id</span>)&#123;</span><br><span class="line">        <span class="title class_">System</span>.<span class="property">out</span>.<span class="title function_">println</span>(<span class="string">&quot;book delete ..&quot;</span> + id);</span><br><span class="line">        <span class="keyword">return</span> <span class="string">&quot;&#123;&#x27;module&#x27;:&#x27;book delete&#x27;&#125;&quot;</span>;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@PutMapping</span></span><br><span class="line">    <span class="keyword">public</span> <span class="title class_">String</span> <span class="title function_">update</span>(<span class="params"><span class="meta">@RequestBody</span> <span class="title class_">Book</span> book</span>)&#123;</span><br><span class="line">        <span class="title class_">System</span>.<span class="property">out</span>.<span class="title function_">println</span>(<span class="string">&quot;book update ..&quot;</span> + book);</span><br><span class="line">        <span class="keyword">return</span> <span class="string">&quot;&#123;&#x27;module&#x27;:&#x27;book update&#x27;&#125;&quot;</span>;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@GetMapping</span>(<span class="string">&quot;/&#123;id&#125;&quot;</span>)</span><br><span class="line">    <span class="keyword">public</span> <span class="title class_">String</span> <span class="title function_">getById</span>(<span class="params"><span class="meta">@PathVariable</span> <span class="title class_">Integer</span> id</span>)&#123;</span><br><span class="line">        <span class="title class_">System</span>.<span class="property">out</span>.<span class="title function_">println</span>(<span class="string">&quot;book getById ..&quot;</span> + id);</span><br><span class="line">        <span class="keyword">return</span> <span class="string">&quot;&#123;&#x27;module&#x27;:&#x27;book getById&#x27;&#125;&quot;</span>;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@GetMapping</span></span><br><span class="line">    <span class="keyword">public</span> <span class="title class_">String</span> <span class="title function_">getAll</span>(<span class="params"></span>)&#123;</span><br><span class="line">        <span class="title class_">System</span>.<span class="property">out</span>.<span class="title function_">println</span>(<span class="string">&quot;book getAll ..&quot;</span>);</span><br><span class="line">        <span class="keyword">return</span> <span class="string">&quot;&#123;&#x27;module&#x27;:&#x27;book getAll&#x27;&#125;&quot;</span>;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></li><li><p>使用PostMan测试，没有问题的话就可以继续往下看了</p></li></ul><h3 id="拦截器开发"><a href="#拦截器开发" class="headerlink" title="拦截器开发"></a>拦截器开发</h3><ul><li><p>```<br>步骤一：</p><figure class="highlight"><table><tr><td class="gutter"><pre><span class="line">1</span><br><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><br><span class="line">创建拦截器类</span><br><span class="line"></span><br><span class="line">在</span><br><span class="line"></span><br></pre></td></tr></table></figure><p>com.blog.controller.interceptor</p><figure class="highlight"><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><br><span class="line">下创建</span><br><span class="line"></span><br></pre></td></tr></table></figure><p>ProjectInterceptor</p><figure class="highlight"><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><br><span class="line">类，实现</span><br><span class="line"></span><br></pre></td></tr></table></figure><p>HandlerInterceptor</p><figure class="highlight"><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><br><span class="line">接口，并重写其中的三个方法</span><br><span class="line"></span><br></pre></td></tr></table></figure><p>@Component<br>//注意当前类必须受Spring容器控制<br>public class ProjectInterceptor implements HandlerInterceptor {</p><pre><code>//原始方法调用前执行的内容public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception &#123;    System.out.println(&quot;preHandle&quot;);    return true;&#125;//原始方法调用后执行的内容public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception &#123;    System.out.println(&quot;postHandle&quot;);&#125;//原始方法调用完成后执行的内容public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception &#123;    System.out.println(&quot;afterCompletion&quot;);&#125;</code></pre><p>}</p><figure class="highlight autohotkey"><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><br><span class="line">- `步骤二：`配置拦截器类</span><br><span class="line"></span><br></pre></td></tr></table></figure><p>public class SpringMvcSupport extends WebMvcConfigurationSupport {<br>  @Autowired<br>  private ProjectInterceptor projectInterceptor;</p><p>  @Override<br>  protected void addResourceHandlers(ResourceHandlerRegistry registry) {</p><pre><code>  registry.addResourceHandler(&quot;/pages/**&quot;).addResourceLocations(&quot;/pages/&quot;);</code></pre><p>  }</p><p>  @Override<br>  protected void addInterceptors(InterceptorRegistry registry) {</p><pre><code>  //配置拦截器,拦截路径是/books，只会拦截/books，拦截不到/books/1  registry.addInterceptor(projectInterceptor).addPathPatterns(&quot;/books&quot;);</code></pre><p>  }<br>}</p><figure class="highlight autohotkey"><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><br><span class="line">- ```</span><br><span class="line">  步骤三：</span><br></pre></td></tr></table></figure><p>SpringMvc添加SpringMvcSupport包扫描</p><figure class="highlight less"><table><tr><td class="gutter"><pre><span class="line">1</span><br><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="variable">@Configuration</span></span><br><span class="line"><span class="variable">@ComponentScan</span>(&#123;<span class="string">&quot;com.blog.controller&quot;</span>, <span class="string">&quot;com.blog.config&quot;</span>&#125;)</span><br><span class="line"><span class="variable">@EnableWebMvc</span></span><br><span class="line">public class SpringMvcConfig &#123;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></li><li><p><code>步骤四：</code>运行程序测试</p><ul><li><p>使用PostMan发送请求给</p><figure class="highlight apache"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="attribute">localhost</span>:<span class="number">8080</span>/books</span><br></pre></td></tr></table></figure><p>，控制台输出如下，说明已经成功拦截</p><blockquote><p>preHandle<br>book update ..Book{书名=’书名测试数据9527’, 价格=0.0}<br>postHandle<br>afterCompletion</p></blockquote></li><li><p>使用PostMan发送请求给</p><figure class="highlight apache"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="attribute">localhost</span>:<span class="number">8080</span>/books/<span class="number">9527</span></span><br></pre></td></tr></table></figure><p>，控制台输出如下，说明没有拦截，若想拦截，则继续修改拦截器的拦截规则</p><blockquote><p>book getById ..9527</p></blockquote></li></ul></li><li><p><code>步骤五：</code>修改拦截器拦截规则</p><figure class="highlight scala"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><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="meta">@Configuration</span></span><br><span class="line">public <span class="class"><span class="keyword">class</span> <span class="title">SpringMvcSupport</span> <span class="keyword">extends</span> <span class="title">WebMvcConfigurationSupport</span> </span>&#123;</span><br><span class="line">    <span class="meta">@Autowired</span></span><br><span class="line">    <span class="keyword">private</span> <span class="type">ProjectInterceptor</span> projectInterceptor;</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> void addResourceHandlers(<span class="type">ResourceHandlerRegistry</span> registry) &#123;</span><br><span class="line">        registry.addResourceHandler(<span class="string">&quot;/pages/**&quot;</span>).addResourceLocations(<span class="string">&quot;/pages/&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> void addInterceptors(<span class="type">InterceptorRegistry</span> registry) &#123;</span><br><span class="line">        <span class="comment">//配置拦截器，查看源码发现，参数是个可变形参，可以设置任意多个拦截路径</span></span><br><span class="line">        registry.addInterceptor(projectInterceptor).addPathPatterns(<span class="string">&quot;/books&quot;</span>,<span class="string">&quot;/books/*&quot;</span>);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></li><li><p>此时我们再次使用PostMan发送请求给</p><figure class="highlight apache"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="attribute">localhost</span>:<span class="number">8080</span>/books/<span class="number">9527</span></span><br></pre></td></tr></table></figure><p>，控制台输出如下，说明已经成功拦截</p><blockquote><p>preHandle<br>book getById ..9527<br>postHandle<br>afterCompletion</p></blockquote></li><li><p>最后说一件事，就是拦截器中的<code>preHandler</code>方法，如果返回true，则代表放行，会执行原始<code>Controller</code>类中要请求的方法，如果返回<code>false</code>，则代表拦截，后面的就不会再执行了。</p></li><li><p><code>步骤六：</code>简化SpringMvcSupport的编写<br>我们可以让<code>SpringMvcConfig</code>类实现<code>WebMvcConfigurer</code>接口，然后直接在<code>SpringMvcConfig</code>中写<code>SpringMvcSupport</code>的东西，这样我们就不用再写<code>SpringMvcSupport</code>类了，全都在<code>SpringMvcConfig</code>中写</p><figure class="highlight less"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><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="variable">@Configuration</span></span><br><span class="line"><span class="variable">@ComponentScan</span>(<span class="string">&quot;com.blog.controller&quot;</span>)</span><br><span class="line"><span class="variable">@EnableWebMvc</span></span><br><span class="line"><span class="comment">//实现WebMvcConfigurer接口可以简化开发，但具有一定的侵入性</span></span><br><span class="line">public class SpringMvcConfig implements WebMvcConfigurer &#123;</span><br><span class="line">    <span class="variable">@Autowired</span></span><br><span class="line">    private ProjectInterceptor projectInterceptor;</span><br><span class="line"></span><br><span class="line">    <span class="selector-tag">public</span> <span class="selector-tag">void</span> <span class="selector-tag">addResourceHandlers</span>(ResourceHandlerRegistry registry) &#123;</span><br><span class="line">        <span class="comment">//对静态资源放行</span></span><br><span class="line">        <span class="selector-tag">registry</span><span class="selector-class">.addResourceHandler</span>(<span class="string">&quot;/pages/**&quot;</span>)<span class="selector-class">.addResourceLocations</span>(<span class="string">&quot;/pages/&quot;</span>);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="selector-tag">public</span> <span class="selector-tag">void</span> <span class="selector-tag">addInterceptors</span>(InterceptorRegistry registry) &#123;</span><br><span class="line">        <span class="comment">//配置拦截器</span></span><br><span class="line">        <span class="selector-tag">registry</span><span class="selector-class">.addInterceptor</span>(projectInterceptor)<span class="selector-class">.addPathPatterns</span>(<span class="string">&quot;/books&quot;</span>, <span class="string">&quot;/books/*&quot;</span>);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></li><li><p>最后我们来看下拦截器的执行流程<br><a href="https://pic.imgdb.cn/item/631ebfbd16f2c2beb1165a09.jpg"><img src="data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7" alt="img"></a></p></li><li><p>当有拦截器后，请求会先进入</p><figure class="highlight ebnf"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="attribute">preHandle</span></span><br></pre></td></tr></table></figure><p>方法，</p><ul><li>如果方法返回<code>true</code>，则放行继续执行后面的handle(Controller的方法)和后面的方法</li><li>如果返回<code>false</code>，则直接跳过后面方法的执行。</li></ul></li></ul><h3 id="拦截器参数"><a href="#拦截器参数" class="headerlink" title="拦截器参数"></a>拦截器参数</h3><h3 id="前置处理方法"><a href="#前置处理方法" class="headerlink" title="前置处理方法"></a>前置处理方法</h3><p>原始方法之前运行preHandle</p><figure class="highlight pgsql"><table><tr><td class="gutter"><pre><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="built_in">public</span> <span class="type">boolean</span> preHandle(HttpServletRequest request, HttpServletResponse response, <span class="keyword">Object</span> <span class="keyword">handler</span>) throws <span class="keyword">Exception</span> &#123;</span><br><span class="line">    <span class="keyword">System</span>.<span class="keyword">out</span>.println(&quot;preHandle&quot;);</span><br><span class="line">    <span class="keyword">return</span> <span class="keyword">true</span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><ul><li><code>request:</code>请求对象</li><li><code>response:</code>响应对象</li><li><code>handler:</code>被调用的处理器对象，本质上是一个方法对象，对反射中的Method对象进行了再包装</li></ul><p>使用request对象可以获取请求数据中的内容，如获取请求头的<code>Content-Type</code></p><figure class="highlight pgsql"><table><tr><td class="gutter"><pre><span class="line">1</span><br><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="built_in">public</span> <span class="type">boolean</span> preHandle(HttpServletRequest request, HttpServletResponse response, <span class="keyword">Object</span> <span class="keyword">handler</span>) throws <span class="keyword">Exception</span> &#123;</span><br><span class="line">    String <span class="type">name</span> = request.getHeader(&quot;Content-Type&quot;);</span><br><span class="line">    <span class="keyword">System</span>.<span class="keyword">out</span>.println(&quot;preHandle..&quot; + <span class="type">name</span>);</span><br><span class="line">    <span class="keyword">return</span> <span class="keyword">true</span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>控制台输出如下，成功输出了Content-Type<code>application/json</code></p><blockquote><p>preHandle..application/json<br>book save ..Book{书名=’书名测试数据’, 价格=0.0}<br>postHandle<br>afterCompletion</p></blockquote><p>使用handler参数，可以获取方法的相关信息</p><figure class="highlight pgsql"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><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="built_in">public</span> <span class="type">boolean</span> preHandle(HttpServletRequest request, HttpServletResponse response, <span class="keyword">Object</span> <span class="keyword">handler</span>) throws <span class="keyword">Exception</span> &#123;</span><br><span class="line">    HandlerMethod handlerMethod = (HandlerMethod) <span class="keyword">handler</span>;</span><br><span class="line">    String methodName = handlerMethod.getMethod().getName();</span><br><span class="line">    <span class="keyword">System</span>.<span class="keyword">out</span>.println(&quot;preHandle..&quot; + methodName);</span><br><span class="line">    <span class="keyword">return</span> <span class="keyword">true</span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>控制台输出如下，成功输出了方法名<code>save</code></p><blockquote><p>preHandle..save<br>book save ..Book{书名=’书名测试数据’, 价格=0.0}<br>postHandle<br>afterCompletion</p></blockquote><h3 id="后置处理方法"><a href="#后置处理方法" class="headerlink" title="后置处理方法"></a>后置处理方法</h3><p>原始方法运行后运行，如果原始方法被拦截，则不执行</p><figure class="highlight pgsql"><table><tr><td class="gutter"><pre><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><br><span class="line"><span class="built_in">public</span> <span class="type">void</span> postHandle(HttpServletRequest request, HttpServletResponse response, <span class="keyword">Object</span> <span class="keyword">handler</span>, ModelAndView modelAndView) throws <span class="keyword">Exception</span> &#123;</span><br><span class="line">    <span class="keyword">System</span>.<span class="keyword">out</span>.println(&quot;postHandle&quot;);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>前三个参数和上面的是一致的。<br><code>modelAndView:</code>如果处理器执行完成具有返回结果，可以读取到对应数据与页面信息，并进行调整<br>因为我们现在都是返回json数据，所以该参数的使用率不高。</p><h3 id="完成处理方法"><a href="#完成处理方法" class="headerlink" title="完成处理方法"></a>完成处理方法</h3><p>拦截器最后执行的方法，无论原始方法是否执行</p><figure class="highlight pgsql"><table><tr><td class="gutter"><pre><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><br><span class="line"><span class="built_in">public</span> <span class="type">void</span> afterCompletion(HttpServletRequest request, HttpServletResponse response, <span class="keyword">Object</span> <span class="keyword">handler</span>, <span class="keyword">Exception</span> ex) throws <span class="keyword">Exception</span> &#123;</span><br><span class="line">    <span class="keyword">System</span>.<span class="keyword">out</span>.println(&quot;afterCompletion&quot;);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>前三个参数与上面的是一致的。<br><code>ex:</code>如果处理器执行过程中出现异常对象，可以针对异常情况进行单独处理<br>因为我们现在已经有全局异常处理器类，所以该参数的使用率也不高。<br>这三个方法中，最常用的是<code>preHandle</code>，在这个方法中可以通过返回值来决定是否要进行放行，我们可以把业务逻辑放在该方法中，如果满足业务则返回<code>true</code>放行，不满足则返回<code>false</code>拦截。</p><h3 id="拦截器链配置"><a href="#拦截器链配置" class="headerlink" title="拦截器链配置"></a>拦截器链配置</h3><p>目前，我们在项目中只添加了一个拦截器，如果有多个，该如何配置?配置多个后，执行顺序是什么?</p><h3 id="配置多个拦截器"><a href="#配置多个拦截器" class="headerlink" title="配置多个拦截器"></a>配置多个拦截器</h3><ul><li><pre><code>步骤一：<figure class="highlight"><table><tr><td class="gutter"><pre><span class="line">1</span><br><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><br><span class="line">创建拦截器类</span><br><span class="line"></span><br><span class="line">直接复制一份改个名，改个输出语句</span><br><span class="line"></span><br></pre></td></tr></table></figure>@Component//注意当前类必须受Spring容器控制public class ProjectInterceptor2 implements HandlerInterceptor &#123;    //原始方法调用前执行的内容    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception &#123;        System.out.println(&quot;preHandle..222&quot;);        return true;    &#125;    //原始方法调用后执行的内容    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception &#123;        System.out.println(&quot;postHandle..222&quot;);    &#125;    //原始方法调用完成后执行的内容    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception &#123;        System.out.println(&quot;afterCompletion..222&quot;);    &#125;&#125;<figure class="highlight autohotkey"><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><br><span class="line">- `步骤二：`配置拦截器类</span><br><span class="line"></span><br></pre></td></tr></table></figure>@Configuration@ComponentScan(&quot;com.blog.controller&quot;)@EnableWebMvcpublic class SpringMvcConfig implements WebMvcConfigurer &#123;    @Autowired    private ProjectInterceptor projectInterceptor;    @Autowired    private ProjectInterceptor2 projectInterceptor2;    public void addInterceptors(InterceptorRegistry registry) &#123;        registry.addInterceptor(projectInterceptor).addPathPatterns(&quot;/books&quot;, &quot;/books/*&quot;);        registry.addInterceptor(projectInterceptor2).addPathPatterns(&quot;/books&quot;, &quot;/books/*&quot;);    &#125;&#125;</code></pre></li><li><p>重新启动服务器，使用PostMan发送请求，控制台输出如下</p><blockquote><p>preHandle..<br>preHandle..222<br>book getById ..9527<br>postHandle..222<br>postHandle<br>afterCompletion..222<br>afterCompletion</p></blockquote></li><li><p>当配置多个拦截器时，形成拦截器链</p></li><li>拦截器链的运行顺序参照拦截器添加顺序为准</li><li>当拦截器中出现对原始处理器的拦截，后面的拦截器均终止运行</li><li>当拦截器运行中断，仅运行配置在前面的拦截器的afterCompletion操作</li></ul><p><a href="https://pic.imgdb.cn/item/631ecaa216f2c2beb121be79.jpg"><img src="data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7" alt="img"></a></p><ul><li><code>preHandle：</code>与配置顺序相同，必定运行</li><li><code>postHandle:</code>与配置顺序相反，可能不运行</li><li><code>afterCompletion:</code>与配置顺序相反，可能不运行。</li></ul><h2 id="完结撒花"><a href="#完结撒花" class="headerlink" title="完结撒花"></a>完结撒花</h2><p>至此SSM的学习就告一段落了，有空再回来复盘一遍，要继续学Maven高级、SpringBoot和MyBatis-Plus了</p>]]></content>
    
    
      
      
    <summary type="html">&lt;h1 id=&quot;SSM整合&quot;&gt;&lt;a href=&quot;#SSM整合&quot; class=&quot;headerlink&quot; title=&quot;SSM整合&quot;&gt;&lt;/a&gt;SSM整合&lt;/h1&gt;&lt;h2 id=&quot;流程分析&quot;&gt;&lt;a href=&quot;#流程分析&quot; class=&quot;headerlink&quot; title=&quot;流程分析&quot;</summary>
      
    
    
    
    
  </entry>
  
  <entry>
    <title></title>
    <link href="https://silvan.chat/2025/10/28/SSM-%E9%BB%91%E9%A9%AC-Mybatis/"/>
    <id>https://silvan.chat/2025/10/28/SSM-%E9%BB%91%E9%A9%AC-Mybatis/</id>
    <published>2025-10-28T11:42:57.650Z</published>
    <updated>2025-10-28T11:42:57.651Z</updated>
    
    <content type="html"><![CDATA[<h1 id="MyBatis入门"><a href="#MyBatis入门" class="headerlink" title="MyBatis入门"></a>MyBatis入门</h1><blockquote><p>MyBatis 是一款优秀的持久层框架，用于简化 JDBC 开发<br>MyBatis 本是 Apache 的一个开源项目iBatis, 2010年这个项目由apache software foundation 迁移到了google code，并且改名为MyBatis 。2013年11月迁移到Github<br>官网：<a href="https://mybatis.org/mybatis-3/zh_CN/index.html">https://mybatis.org/mybatis-3/zh_CN/index.html</a></p></blockquote><ul><li><strong>持久层：</strong><ul><li>负责将数据到保存到数据库的那一层代码。<br>以后开发我们会将操作数据库的Java代码作为持久层。而Mybatis就是对jdbc代码进行了封装。</li></ul></li><li><strong>框架：</strong><ul><li>框架就是一个半成品软件，是一套可重用的、通用的、软件基础代码模型</li><li>在框架的基础之上构建软件编写更加高效、规范、通用、可扩展</li></ul></li></ul><h2 id="JDBC不足"><a href="#JDBC不足" class="headerlink" title="JDBC不足"></a>JDBC不足</h2><div class="tabs" id="jdbc不足"><ul class="nav-tabs"><li class="tab active"><button type="button" data-href="#jdbc不足-1">示例代码1</button></li><li class="tab"><button type="button" data-href="#jdbc不足-2">示例代码2</button></li></ul><div class="tab-contents"><div class="tab-item-content active" id="jdbc不足-1"><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><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">// sql语句写死在java程序中</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 t_user(id,idCard,username,password,birth,gender,email,city,street,zipcode,phone,grade) values(?,?,?,?,?,?,?,?,?,?,?,?)&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"><span class="comment">// 繁琐的赋值：思考一下，这种有规律的代码能不能通过反射机制来做自动化。</span></span><br><span class="line">ps.setString(<span class="number">1</span>, <span class="string">&quot;1&quot;</span>);</span><br><span class="line">ps.setString(<span class="number">2</span>, <span class="string">&quot;123456789&quot;</span>);</span><br><span class="line">ps.setString(<span class="number">3</span>, <span class="string">&quot;zhangsan&quot;</span>);</span><br><span class="line">ps.setString(<span class="number">4</span>, <span class="string">&quot;123456&quot;</span>);</span><br><span class="line">ps.setString(<span class="number">5</span>, <span class="string">&quot;1980-10-11&quot;</span>);</span><br><span class="line">ps.setString(<span class="number">6</span>, <span class="string">&quot;男&quot;</span>);</span><br><span class="line">ps.setString(<span class="number">7</span>, <span class="string">&quot;zhangsan@126.com&quot;</span>);</span><br><span class="line">ps.setString(<span class="number">8</span>, <span class="string">&quot;北京&quot;</span>);</span><br><span class="line">ps.setString(<span class="number">9</span>, <span class="string">&quot;大兴区凉水河二街&quot;</span>);</span><br><span class="line">ps.setString(<span class="number">10</span>, <span class="string">&quot;1000000&quot;</span>);</span><br><span class="line">ps.setString(<span class="number">11</span>, <span class="string">&quot;16398574152&quot;</span>);</span><br><span class="line">ps.setString(<span class="number">12</span>, <span class="string">&quot;A&quot;</span>);</span><br><span class="line"><span class="comment">// 执行SQL</span></span><br><span class="line"><span class="type">int</span> <span class="variable">count</span> <span class="operator">=</span> ps.executeUpdate();</span><br><span class="line"><span class="comment">// ......</span></span><br></pre></td></tr></table></figure><button type="button" class="tab-to-top" aria-label="scroll to top"><i class="fas fa-arrow-up"></i></button></div><div class="tab-item-content" id="jdbc不足-2"><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><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">// sql语句写死在java程序中</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 id,idCard,username,password,birth,gender,email,city,street,zipcode,phone,grade from t_user&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"><span class="type">ResultSet</span> <span class="variable">rs</span> <span class="operator">=</span> ps.executeQuery();</span><br><span class="line">List&lt;User&gt; userList = <span class="keyword">new</span> <span class="title class_">ArrayList</span>&lt;&gt;();</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">    <span class="comment">// 获取数据</span></span><br><span class="line">    <span class="type">String</span> <span class="variable">id</span> <span class="operator">=</span> rs.getString(<span class="string">&quot;id&quot;</span>);</span><br><span class="line">    <span class="type">String</span> <span class="variable">idCard</span> <span class="operator">=</span> rs.getString(<span class="string">&quot;idCard&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">password</span> <span class="operator">=</span> rs.getString(<span class="string">&quot;password&quot;</span>);</span><br><span class="line">    <span class="type">String</span> <span class="variable">birth</span> <span class="operator">=</span> rs.getString(<span class="string">&quot;birth&quot;</span>);</span><br><span class="line">    <span class="type">String</span> <span class="variable">gender</span> <span class="operator">=</span> rs.getString(<span class="string">&quot;gender&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 class="type">String</span> <span class="variable">city</span> <span class="operator">=</span> rs.getString(<span class="string">&quot;city&quot;</span>);</span><br><span class="line">    <span class="type">String</span> <span class="variable">street</span> <span class="operator">=</span> rs.getString(<span class="string">&quot;street&quot;</span>);</span><br><span class="line">    <span class="type">String</span> <span class="variable">zipcode</span> <span class="operator">=</span> rs.getString(<span class="string">&quot;zipcode&quot;</span>);</span><br><span class="line">    <span class="type">String</span> <span class="variable">phone</span> <span class="operator">=</span> rs.getString(<span class="string">&quot;phone&quot;</span>);</span><br><span class="line">    <span class="type">String</span> <span class="variable">grade</span> <span class="operator">=</span> rs.getString(<span class="string">&quot;grade&quot;</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">    <span class="comment">// 给对象属性赋值</span></span><br><span class="line">    user.setId(id);</span><br><span class="line">    user.setIdCard(idCard);</span><br><span class="line">    user.setUsername(username);</span><br><span class="line">    user.setPassword(password);</span><br><span class="line">    user.setBirth(birth);</span><br><span class="line">    user.setGender(gender);</span><br><span class="line">    user.setEmail(email);</span><br><span class="line">    user.setCity(city);</span><br><span class="line">    user.setStreet(street);</span><br><span class="line">    user.setZipcode(zipcode);</span><br><span class="line">    user.setPhone(phone);</span><br><span class="line">    user.setGrade(grade);</span><br><span class="line">    <span class="comment">// 添加到集合</span></span><br><span class="line">    userList.add(user);</span><br><span class="line">&#125;</span><br><span class="line"><span class="comment">// ......</span></span><br></pre></td></tr></table></figure><button type="button" class="tab-to-top" aria-label="scroll to top"><i class="fas fa-arrow-up"></i></button></div></div></div><div class="note info no-icon flat"><p>JDBC不足：</p><ul><li>SQL语句写死在Java程序中，不灵活。改SQL的话就要改Java代码。违背OCP开闭原则。</li><li>给?传值是繁琐的。</li><li>将结果集封装成Java对象是繁琐的。</li></ul></div><h2 id="了解MyBatis"><a href="#了解MyBatis" class="headerlink" title="了解MyBatis"></a>了解MyBatis</h2><ul><li>MyBatis本质上是对JDBC的封装，通过MyBatis完成CRUD。</li><li><p>MyBatis在三层架构中负责持久层的，属于持久层框架。</p></li><li><p>ORM：对象关系映射</p><ul><li>O（Object）：Java虚拟机中的Java对象</li><li>R（Relational）：关系型数据库</li><li>M（Mapping）：将Java虚拟机中的Java对象映射到数据库表中一行记录，或是将数据库表中一行记录映射成Java虚拟机中的一个Java对象。</li></ul></li></ul><div class="img-wrap"><div class="img-bg"><img class="img" src="https://raw.githubusercontent.com/silvan2077/markdown_pic/main/Picgo/20251018151048.png"/></div></div><ul><li>MyBatis框架特点：<ul><li>支持定制化 SQL、存储过程、基本映射以及高级映射</li><li>支持XML开发，也支持注解式开发。【为了保证sql语句的灵活，所以mybatis大部分是采用XML方式开发。】</li><li>避免了几乎所有的 JDBC 代码中手动设置参数以及获取结果集</li><li>将接口和 Java 的 POJOs(Plain Ordinary Java Object，简单普通的Java对象)映射成数据库中的记录</li><li>体积小好学：两个jar包，两个XML配置文件。</li><li>完全做到sql解耦合。</li><li>提供基本映射标签。</li><li>提供高级映射标签。</li><li>提供XML标签，支持动态SQL的编写。</li></ul></li></ul><h2 id="MyBatis入门程序"><a href="#MyBatis入门程序" class="headerlink" title="MyBatis入门程序"></a>MyBatis入门程序</h2><ul><li><p>步骤1：汽车表t_car，字段包括：</p><ul><li>id：主键（自增）【bigint】</li><li>car_num：汽车编号【varchar】</li><li>brand：品牌【varchar】</li><li>guide_price：厂家指导价【decimal类型，专门为财务数据准备的类型】</li><li>produce_time：生产时间【char，年月日即可，10个长度，’2022-10-11’】</li><li>car_type：汽车类型（燃油车、电车、氢能源）【varchar】</li></ul></li><li><p>使用navicat for mysql工具建表</p></li></ul><div class="img-wrap"><div class="img-bg"><img class="img" src="https://raw.githubusercontent.com/silvan2077/markdown_pic/main/Picgo/20251018151459.png"/></div></div><ul><li>使用navicat for mysql工具向t_car表中插入两条数据，如下：</li></ul><div class="img-wrap"><div class="img-bg"><img class="img" src="https://raw.githubusercontent.com/silvan2077/markdown_pic/main/Picgo/20251018151538.png"/></div></div><ul><li>步骤2：创建项目并引入依赖（mybatis依赖 + mysql驱动依赖）<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;!--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>3.5.10<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="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>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-java<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.30<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></li><li>步骤3：在resources根目录下新建mybatis-config.xml配置文件</li></ul><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></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></span><br><span class="line"><span class="meta">        <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 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 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="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;com.mysql.cj.jdbc.Driver&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;jdbc:mysql://localhost:3306/powernode&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;root&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;root&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 class="tag">&lt;<span class="name">mappers</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">mapper</span> <span class="attr">resource</span>=<span class="string">&quot;&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 class="tag">&lt;/<span class="name">configuration</span>&gt;</span></span><br></pre></td></tr></table></figure><div class="note warning no-icon flat"><p>注意：</p><ol><li>mybatis核心配置文件的文件名不一定是mybatis-config.xml，可以是其它名字。</li><li>mybatis核心配置文件存放的位置也可以随意。这里选择放在resources根下，相当于放到了类的根路径下。</li></ol></div><ul><li>步骤4：在resources根目录下新建CarMapper.xml配置文件<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></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></span><br><span class="line"><span class="meta">        <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 class="comment">&lt;!--namespace先随意写一个--&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;car&quot;</span>&gt;</span></span><br><span class="line">    <span class="comment">&lt;!--insert sql：保存一个汽车信息--&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;insertCar&quot;</span>&gt;</span></span><br><span class="line">        insert into t_car</span><br><span class="line">            (id,car_num,brand,guide_price,produce_time,car_type) </span><br><span class="line">        values</span><br><span class="line">            (null,&#x27;102&#x27;,&#x27;丰田mirai&#x27;,40.30,&#x27;2014-10-05&#x27;,&#x27;氢能源&#x27;)</span><br><span class="line">    <span class="tag">&lt;/<span class="name">insert</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;/<span class="name">mapper</span>&gt;</span></span><br></pre></td></tr></table></figure><div class="note warning no-icon flat"><p>注意：</p><ol><li><strong>sql语句最后结尾可以不写<code>;</code></strong></li><li>CarMapper.xml文件的名字不是固定的。可以使用其它名字。</li><li>CarMapper.xml文件的位置也是随意的。需要在mybatis-config.xml文件中指定CarMapper.xml文件的路径。<figure class="highlight xml"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="tag">&lt;<span class="name">mapper</span> <span class="attr">resource</span>=<span class="string">&quot;CarMapper.xml&quot;</span>/&gt;</span></span><br></pre></td></tr></table></figure></li></ol></div></li></ul><ul><li>步骤5：编写MyBatisIntroductionTest代码</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></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">MyBatisIntroductionTest</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. 创建SqlSessionFactoryBuilder对象</span></span><br><span class="line">        <span class="type">SqlSessionFactoryBuilder</span> <span class="variable">sqlSessionFactoryBuilder</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">SqlSessionFactoryBuilder</span>();</span><br><span class="line">        <span class="comment">// 2. 创建SqlSessionFactory对象</span></span><br><span class="line">        <span class="type">InputStream</span> <span class="variable">is</span> <span class="operator">=</span> Thread.currentThread().getContextClassLoader().getResourceAsStream(<span class="string">&quot;mybatis-config.xml&quot;</span>);</span><br><span class="line">        <span class="type">SqlSessionFactory</span> <span class="variable">sqlSessionFactory</span> <span class="operator">=</span> sqlSessionFactoryBuilder.build(is);</span><br><span class="line">        <span class="comment">// 3. 创建SqlSession对象</span></span><br><span class="line">        <span class="type">SqlSession</span> <span class="variable">sqlSession</span> <span class="operator">=</span> sqlSessionFactory.openSession();</span><br><span class="line">        <span class="comment">// 4. 执行sql</span></span><br><span class="line">        <span class="type">int</span> <span class="variable">count</span> <span class="operator">=</span> sqlSession.insert(<span class="string">&quot;insertCar&quot;</span>); <span class="comment">// 这个&quot;insertCar&quot;必须是sql的id</span></span><br><span class="line">        System.out.println(<span class="string">&quot;插入几条数据：&quot;</span> + count);</span><br><span class="line">        <span class="comment">// 5. 提交（mybatis默认采用的事务管理器是JDBC，默认是不提交的，需要手动提交。）</span></span><br><span class="line">        sqlSession.commit();</span><br><span class="line">        <span class="comment">// 6. 关闭资源（只关闭是不会提交的）</span></span><br><span class="line">        sqlSession.close();</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><div class="note warning no-icon flat"><p>默认采用的事务管理器是：<code>JDBC</code>。JDBC事务默认是不提交的，需要手动提交。</p></div><ul><li>步骤6：运行程序，查看运行结果，以及数据库表中的数据</li></ul><div class="img-wrap"><div class="img-bg"><img class="img" src="https://raw.githubusercontent.com/silvan2077/markdown_pic/main/Picgo/20251018163721.png"/></div></div><h2 id="引入JUnit"><a href="#引入JUnit" class="headerlink" title="引入JUnit"></a>引入JUnit</h2><ul><li>JUnit是专门做单元测试的组件。<ul><li>在实际开发中，单元测试一般是由我们Java程序员来完成的。</li><li>我们要对我们自己写的每一个业务方法负责任，要保证每个业务方法在进行测试的时候都能通过。</li></ul></li><li>测试的过程中涉及到两个概念：<ul><li>期望值</li><li>实际值</li></ul></li><li><p>期望值和实际值相同表示测试通过，期望值和实际值不同则单元测试执行时会报错。</p></li><li><p>这里引入JUnit是为了代替main方法。</p></li><li><p>第一步：引入依赖</p></li></ul><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;!-- 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></pre></td></tr></table></figure><ul><li><p>第二步：编写单元测试类【测试用例】，测试用例中每一个测试方法上使用<code>@Test</code>注解进行标注。</p><ul><li>测试用例的名字以及每个测试方法的定义都是有规范的：<ul><li>测试用例的名字：XxxTest</li><li>测试方法声明格式：public void test业务方法名(){}<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><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">public</span> <span class="keyword">class</span> <span class="title class_">CarMapperTest</span>&#123;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 测试方法</span></span><br><span class="line">    <span class="meta">@Test</span></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">testInsert</span><span class="params">()</span>&#123;&#125;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Test</span></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">testUpdate</span><span class="params">()</span>&#123;&#125;</span><br><span class="line"></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></li></ul></li></ul></li><li><p>第三步：可以在类上执行，也可以在方法上执行</p><ul><li>在类上执行时，该类中所有的测试方法都会执行。</li><li>在方法上执行时，只执行当前的测试方法。</li></ul></li><li><p>编写一个测试用例，来测试insertCar业务</p><div class="img-wrap"><div class="img-bg"><img class="img" src="https://raw.githubusercontent.com/silvan2077/markdown_pic/main/Picgo/20251018164858.png"/></div></div></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></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">CarMapperTest</span> &#123;</span><br><span class="line">    </span><br><span class="line">    <span class="meta">@Test</span></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">testInsertCar</span><span class="params">()</span>&#123;</span><br><span class="line">        <span class="type">SqlSession</span> <span class="variable">sqlSession</span> <span class="operator">=</span> <span class="literal">null</span>;</span><br><span class="line">        <span class="keyword">try</span> &#123;</span><br><span class="line">            <span class="comment">// 1.创建SqlSessionFactoryBuilder对象</span></span><br><span class="line">            <span class="type">SqlSessionFactoryBuilder</span> <span class="variable">sqlSessionFactoryBuilder</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">SqlSessionFactoryBuilder</span>();</span><br><span class="line">            <span class="comment">// 2.创建SqlSessionFactory对象</span></span><br><span class="line">            <span class="type">SqlSessionFactory</span> <span class="variable">sqlSessionFactory</span> <span class="operator">=</span> sqlSessionFactoryBuilder.build(Resources.getResourceAsStream(<span class="string">&quot;mybatis-config.xml&quot;</span>));</span><br><span class="line">            <span class="comment">// 3.创建SqlSession对象</span></span><br><span class="line">            sqlSession = sqlSessionFactory.openSession();</span><br><span class="line">            <span class="comment">// 4.执行SQL</span></span><br><span class="line">            <span class="type">int</span> <span class="variable">count</span> <span class="operator">=</span> sqlSession.insert(<span class="string">&quot;insertCar&quot;</span>);</span><br><span class="line">            System.out.println(<span class="string">&quot;更新了几条记录：&quot;</span> + count);</span><br><span class="line">            <span class="comment">// 5.提交</span></span><br><span class="line">            sqlSession.commit();</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> (sqlSession != <span class="literal">null</span>) &#123;</span><br><span class="line">                sqlSession.rollback();</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="comment">// 6.关闭</span></span><br><span class="line">            <span class="keyword">if</span> (sqlSession != <span class="literal">null</span>) &#123;</span><br><span class="line">                sqlSession.close();</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><h2 id="引入日志框架logback"><a href="#引入日志框架logback" class="headerlink" title="引入日志框架logback"></a>引入日志框架logback</h2><ul><li><strong>引入日志框架的目的是为了看清楚mybatis执行的具体sql。</strong></li><li>启用标准日志组件，只需要在<strong>mybatis-config.xml</strong>文件中添加以下配置：【可参考mybatis手册】</li></ul><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="tag">&lt;<span class="name">settings</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;STDOUT_LOGGING&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><p>标准日志也可以用，但是配置不够灵活，可以集成其他的日志组件，例如：log4j，logback等。</p><ul><li><p>logback是目前日志框架中性能较好的，较流行的，所以我们选它。</p></li><li><p>第一步：引入<code>logback</code>相关依赖</p></li></ul><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="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>ch.qos.logback<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>logback-classic<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.11<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></pre></td></tr></table></figure><ul><li>第二步：引入<code>logback</code>相关配置文件（文件名叫做logback.xml或logback-test.xml，放到类路径当中）    </li></ul><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><br><span class="line"><span class="tag">&lt;<span class="name">configuration</span> <span class="attr">debug</span>=<span class="string">&quot;false&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">appender</span> <span class="attr">name</span>=<span class="string">&quot;STDOUT&quot;</span> <span class="attr">class</span>=<span class="string">&quot;ch.qos.logback.core.ConsoleAppender&quot;</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">encoder</span> <span class="attr">class</span>=<span class="string">&quot;ch.qos.logback.classic.encoder.PatternLayoutEncoder&quot;</span>&gt;</span></span><br><span class="line">      <span class="comment">&lt;!--格式化输出：%d表示日期，%thread表示线程名，%-5level：级别从左显示5个字符宽度%msg：日志消息，%n是换行符--&gt;</span></span><br><span class="line">      <span class="tag">&lt;<span class="name">pattern</span>&gt;</span>%d&#123;yyyy-MM-dd HH:mm:ss.SSS&#125; [%thread] %-5level %logger&#123;50&#125; - %msg%n<span class="tag">&lt;/<span class="name">pattern</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;/<span class="name">encoder</span>&gt;</span></span><br><span class="line">  <span class="tag">&lt;/<span class="name">appender</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">appender</span> <span class="attr">name</span>=<span class="string">&quot;FILE&quot;</span> <span class="attr">class</span>=<span class="string">&quot;ch.qos.logback.core.rolling.RollingFileAppender&quot;</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">rollingPolicy</span> <span class="attr">class</span>=<span class="string">&quot;ch.qos.logback.core.rolling.TimeBasedRollingPolicy&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">FileNamePattern</span>&gt;</span>$&#123;LOG_HOME&#125;/TestWeb.log.%d&#123;yyyy-MM-dd&#125;.log<span class="tag">&lt;/<span class="name">FileNamePattern</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">MaxHistory</span>&gt;</span>30<span class="tag">&lt;/<span class="name">MaxHistory</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;/<span class="name">rollingPolicy</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">encoder</span> <span class="attr">class</span>=<span class="string">&quot;ch.qos.logback.classic.encoder.PatternLayoutEncoder&quot;</span>&gt;</span></span><br><span class="line">      <span class="comment">&lt;!--格式化输出：%d表示日期，%thread表示线程名，%-5level：级别从左显示5个字符宽度%msg：日志消息，%n是换行符--&gt;</span></span><br><span class="line">      <span class="tag">&lt;<span class="name">pattern</span>&gt;</span>%d&#123;yyyy-MM-dd HH:mm:ss.SSS&#125; [%thread] %-5level %logger&#123;50&#125; - %msg%n<span class="tag">&lt;/<span class="name">pattern</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;/<span class="name">encoder</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">triggeringPolicy</span> <span class="attr">class</span>=<span class="string">&quot;ch.qos.logback.core.rolling.SizeBasedTriggeringPolicy&quot;</span>&gt;</span></span><br><span class="line">      <span class="tag">&lt;<span class="name">MaxFileSize</span>&gt;</span>100MB<span class="tag">&lt;/<span class="name">MaxFileSize</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;/<span class="name">triggeringPolicy</span>&gt;</span></span><br><span class="line">  <span class="tag">&lt;/<span class="name">appender</span>&gt;</span></span><br><span class="line"></span><br><span class="line">  <span class="comment">&lt;!--mybatis log configure--&gt;</span></span><br><span class="line">  <span class="tag">&lt;<span class="name">logger</span> <span class="attr">name</span>=<span class="string">&quot;com.apache.ibatis&quot;</span> <span class="attr">level</span>=<span class="string">&quot;TRACE&quot;</span>/&gt;</span></span><br><span class="line">  <span class="tag">&lt;<span class="name">logger</span> <span class="attr">name</span>=<span class="string">&quot;java.sql.Connection&quot;</span> <span class="attr">level</span>=<span class="string">&quot;DEBUG&quot;</span>/&gt;</span></span><br><span class="line">  <span class="tag">&lt;<span class="name">logger</span> <span class="attr">name</span>=<span class="string">&quot;java.sql.Statement&quot;</span> <span class="attr">level</span>=<span class="string">&quot;DEBUG&quot;</span>/&gt;</span></span><br><span class="line">  <span class="tag">&lt;<span class="name">logger</span> <span class="attr">name</span>=<span class="string">&quot;java.sql.PreparedStatement&quot;</span> <span class="attr">level</span>=<span class="string">&quot;DEBUG&quot;</span>/&gt;</span></span><br><span class="line"></span><br><span class="line">  <span class="comment">&lt;!-- 日志输出级别,logback日志级别包括五个：TRACE &lt; DEBUG &lt; INFO &lt; WARN &lt; ERROR --&gt;</span></span><br><span class="line">  <span class="tag">&lt;<span class="name">root</span> <span class="attr">level</span>=<span class="string">&quot;DEBUG&quot;</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">appender-ref</span> <span class="attr">ref</span>=<span class="string">&quot;STDOUT&quot;</span>/&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">appender-ref</span> <span class="attr">ref</span>=<span class="string">&quot;FILE&quot;</span>/&gt;</span></span><br><span class="line">  <span class="tag">&lt;/<span class="name">root</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><ul><li>再次执行单元测试方法testInsertCar，查看控制台是否有sql语句输出<div class="img-wrap"><div class="img-bg"><img class="img" src="https://raw.githubusercontent.com/silvan2077/markdown_pic/main/Picgo/20251018165124.png"/></div></div></li></ul><h2 id="封装SqlSessionUtil"><a href="#封装SqlSessionUtil" class="headerlink" title="封装SqlSessionUtil"></a>封装SqlSessionUtil</h2><ul><li>每一次获取SqlSession对象代码太繁琐，因此将其封装成一个工具类</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></pre></td><td class="code"><pre><span class="line"></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">SqlSessionUtil</span> &#123;</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="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">static</span> &#123;</span><br><span class="line">        <span class="keyword">try</span> &#123;</span><br><span class="line">            <span class="type">SqlSessionFactoryBuilder</span> <span class="variable">sqlSessionFactoryBuilder</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">SqlSessionFactoryBuilder</span>();</span><br><span class="line">            sqlSessionFactory = sqlSessionFactoryBuilder.build(Resources.getResourceAsStream(<span class="string">&quot;mybatis-config.xml&quot;</span>));</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"></span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     * 每调用一次openSession()可获取一个新的会话，该会话支持自动提交。</span></span><br><span class="line"><span class="comment">     *</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@return</span> 新的会话对象</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 class="literal">true</span>);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><ul><li>测试工具类，改造testInsertCar()<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><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">@Test</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">testInsertCar</span><span class="params">()</span>&#123;</span><br><span class="line">    <span class="type">SqlSession</span> <span class="variable">sqlSession</span> <span class="operator">=</span> SqlSessionUtil.openSession();</span><br><span class="line">    <span class="comment">// 执行SQL</span></span><br><span class="line">    <span class="type">int</span> <span class="variable">count</span> <span class="operator">=</span> sqlSession.insert(<span class="string">&quot;insertCar&quot;</span>);</span><br><span class="line">    System.out.println(<span class="string">&quot;插入了几条记录:&quot;</span> + count);</span><br><span class="line">    sqlSession.close();</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></li></ul><h1 id="使用MyBatis完成CRUD"><a href="#使用MyBatis完成CRUD" class="headerlink" title="使用MyBatis完成CRUD"></a>使用MyBatis完成CRUD</h1><ul><li>准备工作<ul><li>创建一个普通的Maven工程：mybatis-002-crud</li></ul></li><li>pom.xml<ul><li>依赖：<ul><li>mybatis依赖</li><li>mysql驱动依赖</li><li>junit依赖</li><li>logback依赖</li><li>Lombok依赖</li></ul></li></ul></li><li>mybatis-config.xml放在类的根路径下</li><li>CarMapper.xml放在类的根路径下</li><li>logback.xml放在类的根路径下</li><li>封装SqlSessionUtil工具类</li><li>创建测试用例：com.powernode.mybatis.CarMapperTest</li></ul><h2 id="insert（Create）"><a href="#insert（Create）" class="headerlink" title="insert（Create）"></a>insert（Create）</h2><p>分析以下SQL映射文件中SQL语句存在的问题</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></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></span><br><span class="line"><span class="meta">        <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先随便写--&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;car&quot;</span>&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;insertCar&quot;</span>&gt;</span></span><br><span class="line">        insert into t_car(car_num,brand,guide_price,produce_time,car_type) values(&#x27;103&#x27;, &#x27;奔驰E300L&#x27;, 50.3, &#x27;2022-01-01&#x27;, &#x27;燃油车&#x27;)</span><br><span class="line">    <span class="tag">&lt;/<span class="name">insert</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;/<span class="name">mapper</span>&gt;</span></span><br></pre></td></tr></table></figure><p>存在的问题是：在文件中将SQL语句写死了，如果想要更换别的值，那么就需要修改文件中的SQL语句，对于这个问题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></pre></td><td class="code"><pre><span class="line"><span class="comment">// JDBC中使用 ? 作为占位符。</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 t_car(car_num,brand,guide_price,produce_time,car_type) values(?,?,?,?,?)&quot;</span>;</span><br><span class="line"><span class="comment">// ......</span></span><br><span class="line"><span class="comment">// 给 ? 传值。</span></span><br><span class="line">ps.setString(<span class="number">1</span>,<span class="string">&quot;103&quot;</span>);</span><br><span class="line">ps.setString(<span class="number">2</span>,<span class="string">&quot;奔驰E300L&quot;</span>);</span><br><span class="line">ps.setDouble(<span class="number">3</span>,<span class="number">50.3</span>);</span><br><span class="line">ps.setString(<span class="number">4</span>,<span class="string">&quot;2022-01-01&quot;</span>);</span><br><span class="line">ps.setString(<span class="number">5</span>,<span class="string">&quot;燃油车&quot;</span>);</span><br></pre></td></tr></table></figure><p>MyBatis既然封装了JDBC，那么我们就可以使用MyBatis提供的占位符，将SQL语句中的参数用占位符代替。<br>MaBatis中给出的做法是：将数据放到Map集合中，在SQL语句中使用${}，将Map集合中的key作为${}中的值。<br><div class="tabs" id="insert"><ul class="nav-tabs"><li class="tab active"><button type="button" data-href="#insert-1">CarMapperTest.java</button></li><li class="tab"><button type="button" data-href="#insert-2">CarMapper.xml</button></li></ul><div class="tab-contents"><div class="tab-item-content active" id="insert-1"><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><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_">CarMapperTest</span> &#123;</span><br><span class="line">    <span class="meta">@Test</span></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">testInsertCar</span><span class="params">()</span>&#123;</span><br><span class="line">        <span class="comment">// 准备数据</span></span><br><span class="line">        Map&lt;String, Object&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;k1&quot;</span>, <span class="string">&quot;103&quot;</span>);</span><br><span class="line">        map.put(<span class="string">&quot;k2&quot;</span>, <span class="string">&quot;奔驰E300L&quot;</span>);</span><br><span class="line">        map.put(<span class="string">&quot;k3&quot;</span>, <span class="number">50.3</span>);</span><br><span class="line">        map.put(<span class="string">&quot;k4&quot;</span>, <span class="string">&quot;2020-10-01&quot;</span>);</span><br><span class="line">        map.put(<span class="string">&quot;k5&quot;</span>, <span class="string">&quot;燃油车&quot;</span>);</span><br><span class="line">        <span class="comment">// 获取SqlSession对象</span></span><br><span class="line">        <span class="type">SqlSession</span> <span class="variable">sqlSession</span> <span class="operator">=</span> SqlSessionUtil.openSession();</span><br><span class="line">        <span class="comment">// 执行SQL语句（使用map集合给sql语句传递数据）</span></span><br><span class="line">        <span class="type">int</span> <span class="variable">count</span> <span class="operator">=</span> sqlSession.insert(<span class="string">&quot;insertCar&quot;</span>, map);</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">&#125;</span><br></pre></td></tr></table></figure><button type="button" class="tab-to-top" aria-label="scroll to top"><i class="fas fa-arrow-up"></i></button></div><div class="tab-item-content" id="insert-2"><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></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></span><br><span class="line"><span class="meta">        <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先随便写--&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;car&quot;</span>&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;insertCar&quot;</span>&gt;</span></span><br><span class="line">        insert into t_car(car_num,brand,guide_price,produce_time,car_type) values(#&#123;k1&#125;,#&#123;k2&#125;,#&#123;k3&#125;,#&#123;k4&#125;,#&#123;k5&#125;)</span><br><span class="line">    <span class="tag">&lt;/<span class="name">insert</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;/<span class="name">mapper</span>&gt;</span></span><br></pre></td></tr></table></figure><button type="button" class="tab-to-top" aria-label="scroll to top"><i class="fas fa-arrow-up"></i></button></div></div></div></p><ul><li><code>#&#123;&#125;</code>里面必须填写map集合的key，不能随便写,如果<code>#&#123;&#125;</code>里写的是map集合中不存在的key会导致属性注入错误。</li></ul><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="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></span><br><span class="line"><span class="meta">        <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="tag">&lt;<span class="name">mapper</span> <span class="attr">namespace</span>=<span class="string">&quot;car&quot;</span>&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;insertCar&quot;</span>&gt;</span></span><br><span class="line">        insert into t_car(car_num,brand,guide_price,produce_time,car_type) values(#&#123;kk&#125;,#&#123;k2&#125;,#&#123;k3&#125;,#&#123;k4&#125;,#&#123;k5&#125;)</span><br><span class="line">    <span class="tag">&lt;/<span class="name">insert</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;/<span class="name">mapper</span>&gt;</span></span><br></pre></td></tr></table></figure><p>运行程序：<br><div class="img-wrap"><div class="img-bg"><img class="img" src="https://raw.githubusercontent.com/silvan2077/markdown_pic/main/Picgo/20251018170117.png"/></div></div><br><div class="img-wrap"><div class="img-bg"><img class="img" src="https://raw.githubusercontent.com/silvan2077/markdown_pic/main/Picgo/20251018170134.png"/></div></div></p><div class="note info no-icon flat"><p>通过测试，看到程序并没有报错。正常执行。不过 #{kk} 的写法导致无法获取到map集合中的数据，最终导致数据库表car_num插入了NULL。</p></div><p>使用Map集合可以传参，那使用<strong>pojo</strong>（简单普通的java对象）可以完成传参吗？测试一下：</p><ul><li>第一步：定义一个pojo类Car，提供相关属性。</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="meta">@Data</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">Car</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 carNum;</span><br><span class="line">    <span class="keyword">private</span> String brand;</span><br><span class="line">    <span class="keyword">private</span> Double guidePrice;</span><br><span class="line">    <span class="keyword">private</span> String produceTime;</span><br><span class="line">    <span class="keyword">private</span> String carType;</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;Car&#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;, carNum=&#x27;&quot;</span> + carNum + <span class="string">&#x27;\&#x27;&#x27;</span> +</span><br><span class="line">                <span class="string">&quot;, brand=&#x27;&quot;</span> + brand + <span class="string">&#x27;\&#x27;&#x27;</span> +</span><br><span class="line">                <span class="string">&quot;, guidePrice=&quot;</span> + guidePrice +</span><br><span class="line">                <span class="string">&quot;, produceTime=&#x27;&quot;</span> + produceTime + <span class="string">&#x27;\&#x27;&#x27;</span> +</span><br><span class="line">                <span class="string">&quot;, carType=&#x27;&quot;</span> + carType + <span class="string">&#x27;\&#x27;&#x27;</span> +</span><br><span class="line">                <span class="string">&#x27;&#125;&#x27;</span>;</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_">Car</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_">Car</span><span class="params">(Long id, String carNum, String brand, Double guidePrice, String produceTime, String carType)</span> &#123;</span><br><span class="line">        <span class="built_in">this</span>.id = id;</span><br><span class="line">        <span class="built_in">this</span>.carNum = carNum;</span><br><span class="line">        <span class="built_in">this</span>.brand = brand;</span><br><span class="line">        <span class="built_in">this</span>.guidePrice = guidePrice;</span><br><span class="line">        <span class="built_in">this</span>.produceTime = produceTime;</span><br><span class="line">        <span class="built_in">this</span>.carType = carType;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><ul><li>第二步：Java程序</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="meta">@Test</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">testInsertCarByPOJO</span><span class="params">()</span>&#123;</span><br><span class="line">    <span class="comment">// 创建POJO，封装数据</span></span><br><span class="line">    <span class="type">Car</span> <span class="variable">car</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">Car</span>();</span><br><span class="line">    car.setCarNum(<span class="string">&quot;103&quot;</span>);</span><br><span class="line">    car.setBrand(<span class="string">&quot;奔驰C200&quot;</span>);</span><br><span class="line">    car.setGuidePrice(<span class="number">33.23</span>);</span><br><span class="line">    car.setProduceTime(<span class="string">&quot;2020-10-11&quot;</span>);</span><br><span class="line">    car.setCarType(<span class="string">&quot;燃油车&quot;</span>);</span><br><span class="line">    <span class="comment">// 获取SqlSession对象</span></span><br><span class="line">    <span class="type">SqlSession</span> <span class="variable">sqlSession</span> <span class="operator">=</span> SqlSessionUtil.openSession();</span><br><span class="line">    <span class="comment">// 执行SQL，传数据</span></span><br><span class="line">    <span class="type">int</span> <span class="variable">count</span> <span class="operator">=</span> sqlSession.insert(<span class="string">&quot;insertCarByPOJO&quot;</span>, car);</span><br><span class="line">    System.out.println(<span class="string">&quot;插入了几条记录&quot;</span> + count);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><ul><li>第三步：SQL语句<br>使用实体类的属性名作为值插入数据库表。<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">insert</span> <span class="attr">id</span>=<span class="string">&quot;insertCarByPOJO&quot;</span>&gt;</span></span><br><span class="line">  <span class="comment">&lt;!--#&#123;&#125; 里写的是POJO的属性名--&gt;</span></span><br><span class="line">  insert into t_car(car_num,brand,guide_price,produce_time,car_type) values(#&#123;carNum&#125;,#&#123;brand&#125;,#&#123;guidePrice&#125;,#&#123;produceTime&#125;,#&#123;carType&#125;)</span><br><span class="line"><span class="tag">&lt;/<span class="name">insert</span>&gt;</span></span><br></pre></td></tr></table></figure></li></ul><p>#{} 里写的是POJO的属性名，如果写成其他的会有问题吗？</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></pre></td><td class="code"><pre><span class="line"><span class="tag">&lt;<span class="name">insert</span> <span class="attr">id</span>=<span class="string">&quot;insertCarByPOJO&quot;</span>&gt;</span></span><br><span class="line">  insert into t_car(car_num,brand,guide_price,produce_time,car_type) values(#&#123;a&#125;,#&#123;brand&#125;,#&#123;guidePrice&#125;,#&#123;produceTime&#125;,#&#123;carType&#125;)</span><br><span class="line"><span class="tag">&lt;/<span class="name">insert</span>&gt;</span></span><br></pre></td></tr></table></figure><p>运行程序，出现了以下异常：<br><div class="img-wrap"><div class="img-bg"><img class="img" src="https://raw.githubusercontent.com/silvan2077/markdown_pic/main/Picgo/20251018170819.png"/></div></div><br>错误信息中描述：在Car类中没有找到a属性的getter方法。</p><p>修改POJO类Car的代码，<strong>只将getCarNum()方法名修改为getA()，其他代码不变</strong>，如下：<br><div class="img-wrap"><div class="img-bg"><img class="img" src="https://raw.githubusercontent.com/silvan2077/markdown_pic/main/Picgo/20251018170903.png"/></div></div></p><div class="note danger no-icon flat"><p><strong>经过测试得出结论：</strong></p><ul><li>如果采用map集合传参，#{} 里写的是map集合的key，如果key不存在不会报错，数据库表中会插入NULL。</li><li>如果采用POJO传参，#{} 里写的是get方法的方法名去掉get之后将剩下的单词首字母变小写（例如：getAge对应的是#{age}，getUserName对应的是#{userName}），如果这样的get方法不存在会报错。</li></ul></div><h2 id="delete（Delete）"><a href="#delete（Delete）" class="headerlink" title="delete（Delete）"></a>delete（Delete）</h2><div class="note info no-icon flat"><p>需求：根据car_num进行删除。</p></div><p>SQL语句这样写：</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></pre></td><td class="code"><pre><span class="line"><span class="tag">&lt;<span class="name">delete</span> <span class="attr">id</span>=<span class="string">&quot;deleteByCarNum&quot;</span>&gt;</span></span><br><span class="line">  delete from t_car where car_num = #&#123;SuiBianXie&#125;</span><br><span class="line"><span class="tag">&lt;/<span class="name">delete</span>&gt;</span></span><br></pre></td></tr></table></figure><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></pre></td><td class="code"><pre><span class="line"><span class="meta">@Test</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">testDeleteByCarNum</span><span class="params">()</span>&#123;</span><br><span class="line">    <span class="comment">// 获取SqlSession对象</span></span><br><span class="line">    <span class="type">SqlSession</span> <span class="variable">sqlSession</span> <span class="operator">=</span> SqlSessionUtil.openSession();</span><br><span class="line">    <span class="comment">// 执行SQL语句</span></span><br><span class="line">    <span class="type">int</span> <span class="variable">count</span> <span class="operator">=</span> sqlSession.delete(<span class="string">&quot;deleteByCarNum&quot;</span>, <span class="string">&quot;102&quot;</span>);</span><br><span class="line">    System.out.println(<span class="string">&quot;删除了几条记录：&quot;</span> + count);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>运行结果：<br><div class="img-wrap"><div class="img-bg"><img class="img" src="https://raw.githubusercontent.com/silvan2077/markdown_pic/main/Picgo/20251018172643.png"/></div></div></p><div class="note danger no-icon flat"><p>结论：</p><ul><li>如果Mapper接口方法只有一个参数，那么Mybatis会直接获取方法的参数并作为占位符的内容，因此无论占位符内写什么都能正常操作</li><li>如果Mapper接口方法有多个参数或者参数是一个对象，那么占位符中的内容必须与参数的属性名对应</li></ul></div><h2 id="update（Update）"><a href="#update（Update）" class="headerlink" title="update（Update）"></a>update（Update）</h2><div class="note info no-icon flat"><p>修改id=34的Car信息，car_num为102，brand为比亚迪汉，guide_price为30.23，produce_time为2018-09-10，car_type为电车</p></div><p>修改前：<br><div class="img-wrap"><div class="img-bg"><img class="img" src="https://raw.githubusercontent.com/silvan2077/markdown_pic/main/Picgo/20251018172822.png"/></div></div></p><p>SQL语句如下：</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">&lt;update id=<span class="string">&quot;updateCarByPOJO&quot;</span>&gt;</span><br><span class="line">  update t_car <span class="type">set</span> </span><br><span class="line">    <span class="variable">car_num</span> <span class="operator">=</span> #&#123;carNum&#125;, brand = #&#123;brand&#125;, </span><br><span class="line">    guide_price = #&#123;guidePrice&#125;, produce_time = #&#123;produceTime&#125;, </span><br><span class="line">    car_type = #&#123;carType&#125; </span><br><span class="line">  <span class="type">where</span> <span class="variable">id</span> <span class="operator">=</span> #&#123;id&#125;</span><br><span class="line">&lt;/update&gt;</span><br></pre></td></tr></table></figure><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></pre></td><td class="code"><pre><span class="line"><span class="meta">@Test</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">testUpdateCarByPOJO</span><span class="params">()</span>&#123;</span><br><span class="line">    <span class="comment">// 准备数据</span></span><br><span class="line">    <span class="type">Car</span> <span class="variable">car</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">Car</span>();</span><br><span class="line">    car.setId(<span class="number">34L</span>);</span><br><span class="line">    car.setCarNum(<span class="string">&quot;102&quot;</span>);</span><br><span class="line">    car.setBrand(<span class="string">&quot;比亚迪汉&quot;</span>);</span><br><span class="line">    car.setGuidePrice(<span class="number">30.23</span>);</span><br><span class="line">    car.setProduceTime(<span class="string">&quot;2018-09-10&quot;</span>);</span><br><span class="line">    car.setCarType(<span class="string">&quot;电车&quot;</span>);</span><br><span class="line">    <span class="comment">// 获取SqlSession对象</span></span><br><span class="line">    <span class="type">SqlSession</span> <span class="variable">sqlSession</span> <span class="operator">=</span> SqlSessionUtil.openSession();</span><br><span class="line">    <span class="comment">// 执行SQL语句</span></span><br><span class="line">    <span class="type">int</span> <span class="variable">count</span> <span class="operator">=</span> sqlSession.update(<span class="string">&quot;updateCarByPOJO&quot;</span>, car);</span><br><span class="line">    System.out.println(<span class="string">&quot;更新了几条记录：&quot;</span> + count);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>运行结果：<br><div class="img-wrap"><div class="img-bg"><img class="img" src="https://raw.githubusercontent.com/silvan2077/markdown_pic/main/Picgo/20251018172900.png"/></div></div></p><h2 id="select（Retrieve）"><a href="#select（Retrieve）" class="headerlink" title="select（Retrieve）"></a>select（Retrieve）</h2><p>select语句和其它语句不同的是：查询可能会有一个结果集。</p><h3 id="查询一条数据"><a href="#查询一条数据" class="headerlink" title="查询一条数据"></a>查询一条数据</h3><p>需求：查询id为1的Car信息</p><p>SQL语句如下：<br><div class="tabs" id="查询一条数据"><ul class="nav-tabs"><li class="tab active"><button type="button" data-href="#查询一条数据-1">SQL语句</button></li><li class="tab"><button type="button" data-href="#查询一条数据-2">Java语句</button></li><li class="tab"><button type="button" data-href="#查询一条数据-3">运行结果</button></li></ul><div class="tab-contents"><div class="tab-item-content active" id="查询一条数据-1"><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="tag">&lt;<span class="name">select</span> <span class="attr">id</span>=<span class="string">&quot;selectCarById&quot;</span>&gt;</span></span><br><span class="line">  select * from t_car where 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><button type="button" class="tab-to-top" aria-label="scroll to top"><i class="fas fa-arrow-up"></i></button></div><div class="tab-item-content" id="查询一条数据-2"><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><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">@Test</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">testSelectCarById</span><span class="params">()</span>&#123;</span><br><span class="line">    <span class="comment">// 获取SqlSession对象</span></span><br><span class="line">    <span class="type">SqlSession</span> <span class="variable">sqlSession</span> <span class="operator">=</span> SqlSessionUtil.openSession();</span><br><span class="line">    <span class="comment">// 执行SQL语句</span></span><br><span class="line">    <span class="type">Object</span> <span class="variable">car</span> <span class="operator">=</span> sqlSession.selectOne(<span class="string">&quot;selectCarById&quot;</span>, <span class="number">1</span>);</span><br><span class="line">    System.out.println(car);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><button type="button" class="tab-to-top" aria-label="scroll to top"><i class="fas fa-arrow-up"></i></button></div><div class="tab-item-content" id="查询一条数据-3"><figure class="highlight cmd"><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">### Error querying database.  Cause: org.apache.ibatis.executor.ExecutorException: </span><br><span class="line">    A query was run and no Result Maps were found <span class="keyword">for</span> the Mapped Statement &#x27;car.selectCarById&#x27;.  【翻译】：对于一个查询语句来说，没有找到查询的结果映射。</span><br><span class="line">    It&#x27;s likely that neither a Result <span class="built_in">Type</span> nor a Result Map was specified. 【翻译】：很可能既没有指定结果类型，也没有指定结果映射。</span><br></pre></td></tr></table></figure><button type="button" class="tab-to-top" aria-label="scroll to top"><i class="fas fa-arrow-up"></i></button></div></div></div></p><p>运行结果的大致意思是：对于一个查询语句来说，需要指定它的<code>结果类型</code>或者<code>结果映射</code>。</p><p>所以如果想让mybatis查询之后返回一个Java对象的话，要告诉mybatis返回一个什么类型的Java对象，可以在<code>&lt;select&gt;</code>标签中添加<code>resultType</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></pre></td><td class="code"><pre><span class="line"><span class="tag">&lt;<span class="name">select</span> <span class="attr">id</span>=<span class="string">&quot;selectCarById&quot;</span> <span class="attr">resultType</span>=<span class="string">&quot;com.powernode.mybatis.pojo.Car&quot;</span>&gt;</span></span><br><span class="line">  select * from t_car where 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><p>运行结果：<br><div class="img-wrap"><div class="img-bg"><img class="img" src="https://raw.githubusercontent.com/silvan2077/markdown_pic/main/Picgo/20251018230843.png"/></div></div></p><p>此时就能够成功运行了，但通过控制台的日志信息可以发现Car对象只有id和brand两个属性有值，其他均为null。</p><p>查询结果集的列名：id, car_num, brand, guide_price, produce_time, car_type<br>Car类的属性名：id, carNum, brand, guidePrice, produceTime, carType</p><p>可以发现：只有id和brand是一致的，其他字段名和属性名对应不上，通过起别名来测试是不是可以解决这个问题。</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></pre></td><td class="code"><pre><span class="line"><span class="tag">&lt;<span class="name">select</span> <span class="attr">id</span>=<span class="string">&quot;selectCarById&quot;</span> <span class="attr">resultType</span>=<span class="string">&quot;com.powernode.mybatis.pojo.Car&quot;</span>&gt;</span></span><br><span class="line">  select </span><br><span class="line">    id, car_num as carNum, brand, guide_price as guidePrice, produce_time as produceTime, car_type as carType </span><br><span class="line">  from </span><br><span class="line">    t_car </span><br><span class="line">  where </span><br><span class="line">    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><p>运行结果如下：</p><div class="img-wrap"><div class="img-bg"><img class="img" src="https://raw.githubusercontent.com/silvan2077/markdown_pic/main/Picgo/20251018231215.png"/></div></div><p>通过测试得知，如果当查询结果的字段名和java类的属性名对应不上的话，可以采用as关键字起别名，<strong>之后再学习其他方法</strong>。</p><h3 id="查询多条数据"><a href="#查询多条数据" class="headerlink" title="查询多条数据"></a>查询多条数据</h3><div class="note info no-icon flat"><p>需求：查询所有的Car信息。</p></div><p>SQL语句如下：</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></pre></td><td class="code"><pre><span class="line"><span class="comment">&lt;!--虽然结果是List集合，但是resultType属性需要指定的是List集合中元素的类型。--&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;selectCarAll&quot;</span> <span class="attr">resultType</span>=<span class="string">&quot;com.powernode.mybatis.pojo.Car&quot;</span>&gt;</span></span><br><span class="line">  <span class="comment">&lt;!--记得使用as起别名，让查询结果的字段名和java类的属性名对应上。--&gt;</span></span><br><span class="line">  select</span><br><span class="line">  id, car_num as carNum, brand, guide_price as guidePrice, produce_time as produceTime, car_type as carType</span><br><span class="line">  from</span><br><span class="line">  t_car</span><br><span class="line"><span class="tag">&lt;/<span class="name">select</span>&gt;</span></span><br></pre></td></tr></table></figure><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></pre></td><td class="code"><pre><span class="line"><span class="meta">@Test</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">testSelectCarAll</span><span class="params">()</span>&#123;</span><br><span class="line">    <span class="comment">// 获取SqlSession对象</span></span><br><span class="line">    <span class="type">SqlSession</span> <span class="variable">sqlSession</span> <span class="operator">=</span> SqlSessionUtil.openSession();</span><br><span class="line">    <span class="comment">// 执行SQL语句</span></span><br><span class="line">    List&lt;Object&gt; cars = sqlSession.selectList(<span class="string">&quot;selectCarAll&quot;</span>);</span><br><span class="line">    <span class="comment">// 输出结果</span></span><br><span class="line">    cars.forEach(car -&gt; System.out.println(car));</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>运行结果如下：</p><div class="img-wrap"><div class="img-bg"><img class="img" src="https://raw.githubusercontent.com/silvan2077/markdown_pic/main/Picgo/20251018231337.png"/></div></div><h2 id="Mapper的namespace"><a href="#Mapper的namespace" class="headerlink" title="Mapper的namespace"></a>Mapper的namespace</h2><p>在Mapper配置文件中<code>&lt;mapper&gt;</code>标签的namespace属性可以翻译为命名空间，这个命名空间主要是为了<strong>防止Id冲突</strong>。</p><p>创建CarMapper2.xml文件，代码如下：</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></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></span><br><span class="line"><span class="meta"><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="tag">&lt;<span class="name">mapper</span> <span class="attr">namespace</span>=<span class="string">&quot;car2&quot;</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;selectCarAll&quot;</span> <span class="attr">resultType</span>=<span class="string">&quot;com.powernode.mybatis.pojo.Car&quot;</span>&gt;</span></span><br><span class="line">    select</span><br><span class="line">    id, car_num as carNum, brand, guide_price as guidePrice, produce_time as produceTime, car_type as carType</span><br><span class="line">    from</span><br><span class="line">    t_car</span><br><span class="line">  <span class="tag">&lt;/<span class="name">select</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;/<span class="name">mapper</span>&gt;</span></span><br></pre></td></tr></table></figure><p>此时CarMapper.xml和CarMapper2.xml文件中都有 id=”selectCarAll”。将<code>CarMapper2.xml</code>配置到<code>mybatis-config.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></pre></td><td class="code"><pre><span class="line"><span class="tag">&lt;<span class="name">mappers</span>&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;CarMapper.xml&quot;</span>/&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;CarMapper2.xml&quot;</span>/&gt;</span></span><br><span class="line"><span class="tag">&lt;/<span class="name">mappers</span>&gt;</span></span><br></pre></td></tr></table></figure><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></pre></td><td class="code"><pre><span class="line"><span class="meta">@Test</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">testNamespace</span><span class="params">()</span>&#123;</span><br><span class="line">    <span class="comment">// 获取SqlSession对象</span></span><br><span class="line">    <span class="type">SqlSession</span> <span class="variable">sqlSession</span> <span class="operator">=</span> SqlSessionUtil.openSession();</span><br><span class="line">    <span class="comment">// 执行SQL语句</span></span><br><span class="line">    List&lt;Object&gt; cars = sqlSession.selectList(<span class="string">&quot;selectCarAll&quot;</span>);</span><br><span class="line">    <span class="comment">// 输出结果</span></span><br><span class="line">    cars.forEach(car -&gt; System.out.println(car));</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>运行结果如下：</p><figure class="highlight cmd"><table><tr><td class="gutter"><pre><span class="line">1</span><br><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="function">org.apache.ibatis.exceptions.PersistenceException: </span></span><br><span class="line"><span class="function">### <span class="title">Error</span> <span class="title">querying</span> <span class="title">database</span>.  <span class="title">Cause</span>: <span class="title">java.lang.IllegalArgumentException</span>: </span></span><br><span class="line"><span class="function">  <span class="title">selectCarAll</span> <span class="title">is</span> <span class="title">ambiguous</span> <span class="title">in</span> <span class="title">Mapped</span> <span class="title">Statements</span> <span class="title">collection</span> (<span class="title">try</span> <span class="title">using</span> <span class="title">the</span> <span class="title">full</span> <span class="title">name</span> <span class="title">including</span> <span class="title">the</span> <span class="title">namespace</span>, <span class="title">or</span> <span class="title">rename</span> <span class="title">one</span> <span class="title">of</span> <span class="title">the</span> <span class="title">entries</span>) </span></span><br><span class="line"><span class="function">  【翻译】<span class="title">selectCarAll</span>在<span class="title">Mapped</span> <span class="title">Statements</span>集合中不明确（请尝试使用包含名称空间的全名，或重命名其中一个条目）</span></span><br><span class="line"><span class="function">  【大致意思是】<span class="title">selectCarAll</span>重名了，要么在<span class="title">selectCarAll</span>前添加一个名称空间，要么改个其它名字。</span></span><br></pre></td></tr></table></figure><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></pre></td><td class="code"><pre><span class="line"><span class="meta">@Test</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">testNamespace</span><span class="params">()</span>&#123;</span><br><span class="line">    <span class="comment">// 获取SqlSession对象</span></span><br><span class="line">    <span class="type">SqlSession</span> <span class="variable">sqlSession</span> <span class="operator">=</span> SqlSessionUtil.openSession();</span><br><span class="line">    <span class="comment">// 执行SQL语句</span></span><br><span class="line">    <span class="comment">//List&lt;Object&gt; cars = sqlSession.selectList(&quot;car.selectCarAll&quot;);</span></span><br><span class="line">    List&lt;Object&gt; cars = sqlSession.selectList(<span class="string">&quot;car2.selectCarAll&quot;</span>);</span><br><span class="line">    <span class="comment">// 输出结果</span></span><br><span class="line">    cars.forEach(car -&gt; System.out.println(car));</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>运行结果如下：</p><div class="img-wrap"><div class="img-bg"><img class="img" src="https://raw.githubusercontent.com/silvan2077/markdown_pic/main/Picgo/20251018231825.png"/></div></div><h1 id="MyBatis核心配置文件详解"><a href="#MyBatis核心配置文件详解" class="headerlink" title="MyBatis核心配置文件详解"></a>MyBatis核心配置文件详解</h1><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></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></span><br><span class="line"><span class="meta"><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 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 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="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;com.mysql.cj.jdbc.Driver&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;jdbc:mysql://localhost:3306/powernode&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;root&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;root&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 class="tag">&lt;<span class="name">mappers</span>&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;CarMapper.xml&quot;</span>/&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;CarMapper2.xml&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 class="tag">&lt;/<span class="name">configuration</span>&gt;</span></span><br></pre></td></tr></table></figure><ul><li><code>configuration</code>：根标签，表示配置信息。</li><li><code>environments</code>：环境（多个），以“s”结尾表示复数，也就是说mybatis的环境可以配置多个数据源。<ul><li><code>default</code>属性：表示默认使用的是哪个环境，default后面填写的是environment的id。<strong>default的值只需要和environment的id值一致即可</strong>。</li></ul></li><li><code>environment</code>：具体的环境配置（<strong>主要包括：事务管理器的配置 + 数据源的配置</strong>）<ul><li><code>id</code>属性：给当前环境一个唯一标识，该标识用在environments的default后面，用来指定默认环境的选择。</li></ul></li><li><p><code>transactionManager</code>：配置事务管理器</p><ul><li><code>type</code>属性：指定事务管理器具体使用什么方式，可选值包括两个  <ul><li><strong><code>JDBC</code></strong>：使用JDBC原生的事务管理机制。<strong>底层原理：事务开启conn.setAutoCommit(false); …处理业务…事务提交conn.commit();</strong></li><li><strong><code>MANAGED</code></strong>：交给其它容器来管理事务，比如WebLogic、JBOSS等。如果没有管理事务的容器，则没有事务。<strong>没有事务的含义：只要执行一条DML语句，则提交一次</strong>。</li></ul></li></ul></li><li><p><code>dataSource</code>：指定数据源</p><ul><li><p><code>type</code>属性：用来指定具体使用的数据库连接池的策略，可选值包括三个</p><ul><li><strong><code>UNPOOLED</code></strong>：采用传统的获取连接的方式，虽然也实现Javax.sql.DataSource接口，但是并没有使用池的思想。<ul><li>property可以是：<ul><li>driver 这是 JDBC 驱动的 Java 类全限定名。</li><li>url 这是数据库的 JDBC URL 地址。</li><li>username 登录数据库的用户名。</li><li>password 登录数据库的密码。</li><li>defaultTransactionIsolationLevel 默认的连接事务隔离级别。</li><li>defaultNetworkTimeout 等待数据库操作完成的默认网络超时时间（单位：毫秒）</li></ul></li></ul></li><li><p><strong>POOLED</strong>：采用传统的javax.sql.DataSource规范中的连接池，mybatis中有针对规范的实现。</p><ul><li>property可以是（除了包含<strong>UNPOOLED</strong>中之外）：<ul><li>poolMaximumActiveConnections 在任意时间可存在的活动（正在使用）连接数量，默认值：10</li><li>poolMaximumIdleConnections 任意时间可能存在的空闲连接数。</li><li>其它….</li></ul></li></ul></li><li><p><strong>JNDI</strong>：采用服务器提供的JNDI技术实现，来获取DataSource对象，不同的服务器所能拿到DataSource是不一样。如果不是web或者maven的war工程，JNDI是不能使用的。</p><ul><li>property可以是（最多只包含以下两个属性）：<ul><li>initial_context 这个属性用来在 InitialContext 中寻找上下文（即，initialContext.lookup(initial_context)）这是个可选属性，如果忽略，那么将会直接从 InitialContext 中寻找 data_source 属性。</li><li>data_source 这是引用数据源实例位置的上下文路径。提供了 initial_context 配置时会在其返回的上下文中进行查找，没有提供时则直接在 InitialContext 中查找。</li></ul></li></ul></li></ul></li></ul></li><li>mappers：在mappers标签中可以配置多个sql映射文件的路径。</li><li>mapper：配置某个sql映射文件的路径<ul><li>resource属性：使用相对于类路径的资源引用方式</li><li>url属性：使用完全限定资源定位符（URL）方式</li></ul></li></ul><h2 id="environment"><a href="#environment" class="headerlink" title="environment"></a>environment</h2><p>mybatis-003-configuration<br><div class="tabs" id="environment"><ul class="nav-tabs"><li class="tab active"><button type="button" data-href="#environment-1">mybatis-config.xml</button></li><li class="tab"><button type="button" data-href="#environment-2">CarMapper.xml</button></li><li class="tab"><button type="button" data-href="#environment-3">ConfigurationTest</button></li></ul><div class="tab-contents"><div class="tab-item-content active" id="environment-1"><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></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></span><br><span class="line"><span class="meta">        <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 class="comment">&lt;!--默认使用开发环境--&gt;</span></span><br><span class="line">    <span class="comment">&lt;!--&lt;environments default=&quot;dev&quot;&gt;--&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">environments</span> <span class="attr">default</span>=<span class="string">&quot;production&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">environment</span> <span class="attr">id</span>=<span class="string">&quot;dev&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;com.mysql.cj.jdbc.Driver&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;jdbc:mysql://localhost:3306/powernode&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;root&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;root&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="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;production&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;com.mysql.cj.jdbc.Driver&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;jdbc:mysql://localhost:3306/mybatis&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;root&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;root&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 class="tag">&lt;<span class="name">mappers</span>&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;CarMapper.xml&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 class="tag">&lt;/<span class="name">configuration</span>&gt;</span></span><br></pre></td></tr></table></figure><button type="button" class="tab-to-top" aria-label="scroll to top"><i class="fas fa-arrow-up"></i></button></div><div class="tab-item-content" id="environment-2"><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="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></span><br><span class="line"><span class="meta">        <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="tag">&lt;<span class="name">mapper</span> <span class="attr">namespace</span>=<span class="string">&quot;car&quot;</span>&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;insertCar&quot;</span>&gt;</span></span><br><span class="line">        insert into t_car(id,car_num,brand,guide_price,produce_time,car_type) values(null,#&#123;carNum&#125;,#&#123;brand&#125;,#&#123;guidePrice&#125;,#&#123;produceTime&#125;,#&#123;carType&#125;)</span><br><span class="line">    <span class="tag">&lt;/<span class="name">insert</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;/<span class="name">mapper</span>&gt;</span></span><br></pre></td></tr></table></figure><button type="button" class="tab-to-top" aria-label="scroll to top"><i class="fas fa-arrow-up"></i></button></div><div class="tab-item-content" id="environment-3"><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><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="keyword">public</span> <span class="keyword">class</span> <span class="title class_">ConfigurationTest</span> &#123;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Test</span></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">testEnvironment</span><span class="params">()</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">Car</span> <span class="variable">car</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">Car</span>();</span><br><span class="line">        car.setCarNum(<span class="string">&quot;133&quot;</span>);</span><br><span class="line">        car.setBrand(<span class="string">&quot;丰田霸道&quot;</span>);</span><br><span class="line">        car.setGuidePrice(<span class="number">50.3</span>);</span><br><span class="line">        car.setProduceTime(<span class="string">&quot;2020-01-10&quot;</span>);</span><br><span class="line">        car.setCarType(<span class="string">&quot;燃油车&quot;</span>);</span><br><span class="line"></span><br><span class="line">        <span class="comment">// 一个数据库对应一个SqlSessionFactory对象</span></span><br><span class="line">        <span class="comment">// 两个数据库对应两个SqlSessionFactory对象，以此类推</span></span><br><span class="line">        <span class="type">SqlSessionFactoryBuilder</span> <span class="variable">sqlSessionFactoryBuilder</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">SqlSessionFactoryBuilder</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">sqlSessionFactory</span> <span class="operator">=</span> sqlSessionFactoryBuilder.build(Resources.getResourceAsStream(<span class="string">&quot;mybatis-config.xml&quot;</span>));</span><br><span class="line">        <span class="type">SqlSession</span> <span class="variable">sqlSession</span> <span class="operator">=</span> sqlSessionFactory.openSession(<span class="literal">true</span>);</span><br><span class="line">        <span class="type">int</span> <span class="variable">count</span> <span class="operator">=</span> sqlSession.insert(<span class="string">&quot;insertCar&quot;</span>, car);</span><br><span class="line">        System.out.println(<span class="string">&quot;插入了几条记录：&quot;</span> + count);</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">sqlSessionFactory1</span> <span class="operator">=</span> sqlSessionFactoryBuilder.build(Resources.getResourceAsStream(<span class="string">&quot;mybatis-config.xml&quot;</span>), <span class="string">&quot;dev&quot;</span>);</span><br><span class="line">        <span class="type">SqlSession</span> <span class="variable">sqlSession1</span> <span class="operator">=</span> sqlSessionFactory1.openSession(<span class="literal">true</span>);</span><br><span class="line">        <span class="type">int</span> <span class="variable">count1</span> <span class="operator">=</span> sqlSession1.insert(<span class="string">&quot;insertCar&quot;</span>, car);</span><br><span class="line">        System.out.println(<span class="string">&quot;插入了几条记录：&quot;</span> + count1);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><button type="button" class="tab-to-top" aria-label="scroll to top"><i class="fas fa-arrow-up"></i></button></div></div></div></p><p>执行结果：</p><div class="img-wrap"><div class="img-bg"><img class="img" src="https://raw.githubusercontent.com/silvan2077/markdown_pic/main/Picgo/20251018232646.png"/></div></div><h2 id="transactionManager"><a href="#transactionManager" class="headerlink" title="transactionManager"></a>transactionManager</h2><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><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">&lt;?xml version=<span class="string">&quot;1.0&quot;</span> encoding=<span class="string">&quot;UTF-8&quot;</span> ?&gt;</span><br><span class="line">&lt;!DOCTYPE configuration</span><br><span class="line">        PUBLIC <span class="string">&quot;-//mybatis.org//DTD Config 3.0//EN&quot;</span></span><br><span class="line">        <span class="string">&quot;http://mybatis.org/dtd/mybatis-3-config.dtd&quot;</span>&gt;</span><br><span class="line">&lt;configuration&gt;</span><br><span class="line">    &lt;environments <span class="keyword">default</span>=<span class="string">&quot;dev&quot;</span>&gt;</span><br><span class="line">        &lt;environment id=<span class="string">&quot;dev&quot;</span>&gt;</span><br><span class="line">            &lt;transactionManager type=<span class="string">&quot;MANAGED&quot;</span>/&gt;</span><br><span class="line">            &lt;dataSource type=<span class="string">&quot;POOLED&quot;</span>&gt;</span><br><span class="line">                &lt;property name=<span class="string">&quot;driver&quot;</span> value=<span class="string">&quot;com.mysql.cj.jdbc.Driver&quot;</span>/&gt;</span><br><span class="line">                &lt;property name=<span class="string">&quot;url&quot;</span> value=<span class="string">&quot;jdbc:mysql://localhost:3306/powernode&quot;</span>/&gt;</span><br><span class="line">                &lt;property name=<span class="string">&quot;username&quot;</span> value=<span class="string">&quot;root&quot;</span>/&gt;</span><br><span class="line">                &lt;property name=<span class="string">&quot;password&quot;</span> value=<span class="string">&quot;root&quot;</span>/&gt;</span><br><span class="line">            &lt;/dataSource&gt;</span><br><span class="line">        &lt;/environment&gt;</span><br><span class="line">    &lt;/environments&gt;</span><br><span class="line">    &lt;mappers&gt;</span><br><span class="line">        &lt;mapper resource=<span class="string">&quot;CarMapper.xml&quot;</span>/&gt;</span><br><span class="line">    &lt;/mappers&gt;</span><br><span class="line">&lt;/configuration&gt;</span><br><span class="line"><span class="meta">@Test</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">testTransactionManager</span><span class="params">()</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">Car</span> <span class="variable">car</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">Car</span>();</span><br><span class="line">    car.setCarNum(<span class="string">&quot;133&quot;</span>);</span><br><span class="line">    car.setBrand(<span class="string">&quot;丰田霸道&quot;</span>);</span><br><span class="line">    car.setGuidePrice(<span class="number">50.3</span>);</span><br><span class="line">    car.setProduceTime(<span class="string">&quot;2020-01-10&quot;</span>);</span><br><span class="line">    car.setCarType(<span class="string">&quot;燃油车&quot;</span>);</span><br><span class="line">    <span class="comment">// 获取SqlSessionFactory对象</span></span><br><span class="line">    <span class="type">SqlSessionFactoryBuilder</span> <span class="variable">sqlSessionFactoryBuilder</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">SqlSessionFactoryBuilder</span>();</span><br><span class="line">    <span class="type">SqlSessionFactory</span> <span class="variable">sqlSessionFactory</span> <span class="operator">=</span> sqlSessionFactoryBuilder.build(Resources.getResourceAsStream(<span class="string">&quot;mybatis-config2.xml&quot;</span>));</span><br><span class="line">    <span class="comment">// 获取SqlSession对象</span></span><br><span class="line">    <span class="type">SqlSession</span> <span class="variable">sqlSession</span> <span class="operator">=</span> sqlSessionFactory.openSession();</span><br><span class="line">    <span class="comment">// 执行SQL</span></span><br><span class="line">    <span class="type">int</span> <span class="variable">count</span> <span class="operator">=</span> sqlSession.insert(<span class="string">&quot;insertCar&quot;</span>, car);</span><br><span class="line">    System.out.println(<span class="string">&quot;插入了几条记录：&quot;</span> + count);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p><strong>当事务管理器是：JDBC</strong></p><ul><li><p>采用JDBC的原生事务机制：</p></li><li><p>开启事务：conn.setAutoCommit(false);</p></li><li>处理业务……</li><li>提交事务：conn.commit();</li></ul><p>使用JDBC事务管理器的话，底层创建的事务管理器对象: JdbcTransaction对象。</p><p>如果你编写的代码是下面的代码:</p><p>SqlSession sqlSession = sqlSessionFactory.openSession(true);</p><p>表示没有开启事务。因为这种方式压根不会执行: conn.setAutoCommit(false);</p><p>在JDBC事务中，没有执行conn.setAutoCommit(false);那么autoCommit就是true。</p><p>如果autoCommit是true，就表示没有开启事务。只要执行任意一条DML语句就提交一次。</p><p><strong>当事务管理器是：MANAGED</strong></p><ul><li>交给容器去管理事务，但目前使用的是本地程序，没有容器的支持，<strong>当mybatis找不到容器的支持时：没有事务</strong>。也就是说只要执行一条DML语句，则提交一次。</li></ul><p><strong>JDBC中的事务:</strong></p><p>如果你没有在JDBC代码中执行: conn.setAutoCommit(fase);的话，默认的autoCommit是true。</p><p>重点:</p><p>以后注意了，只要你的autoCommit是true，就表示没有开启事务。</p><p>只有你的autoCommit是false的时候，就表示开启了事务。</p><h2 id="dataSource"><a href="#dataSource" class="headerlink" title="dataSource"></a>dataSource</h2><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></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></span><br><span class="line"><span class="meta"><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><br><span class="line"><span class="comment">&lt;!--</span></span><br><span class="line"><span class="comment">dataSource配置:</span></span><br><span class="line"><span class="comment">1.dataSource被称为数据源。</span></span><br><span class="line"><span class="comment">2.dataSource作用是什么? 为程序提供Connection对象。(但凡是给程序提供Connection对象的，</span></span><br><span class="line"><span class="comment">  都叫做数据源。)</span></span><br><span class="line"><span class="comment">3.数据源实际上是一套规范。JDK中有这套规范: javax.sgl.DataSource</span></span><br><span class="line"><span class="comment">(这个数据源的规范，这套接口实际上是JDK规定的。)</span></span><br><span class="line"><span class="comment">4.我们自己也可以编写数据源组件，只要实现avax.sgl.Datasource接口就行了。</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">5.常见的数据源组件有哪些呢[常见的数据库连接池有哪些呢] ?</span></span><br><span class="line"><span class="comment">    阿里巴巴的德鲁伊连接池: druid</span></span><br><span class="line"><span class="comment">    c3p0</span></span><br><span class="line"><span class="comment">    dbcp</span></span><br><span class="line"><span class="comment">6.type属性用来指定数据源的类型，就是指定具体使用什么方式来获取Connection对象:</span></span><br><span class="line"><span class="comment">    type属性有三个值:必须是三选一。</span></span><br><span class="line"><span class="comment">    type=&quot;[UNPOOLEDIPOOLEDIJNDI]&quot;</span></span><br><span class="line"><span class="comment">    UNPOOLED: 不使用数据库连接池技术。每一次请求过来之后，都是创建新的Connection对象。</span></span><br><span class="line"><span class="comment">    POOLED: 使用mybatis自己实现的数据库连接池。</span></span><br><span class="line"><span class="comment">    JNDI:集成其它第三方的数据库连接池。</span></span><br><span class="line"><span class="comment"></span></span><br><span class="line"><span class="comment">    JNDI是一套规范。谁实现了这套规范呢? 大部分的web容器都实现了JNDI规范:</span></span><br><span class="line"><span class="comment">    例如: Tomcat、Jetty、WebLogic、WebSphere，这些服务器(容器)都实现了JNDI规范。</span></span><br><span class="line"><span class="comment">    JNDI是: java命名目录接口。Tomcat服务器实现了这个规范</span></span><br><span class="line"><span class="comment">--&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">environments</span> <span class="attr">default</span>=<span class="string">&quot;dev&quot;</span>&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;dev&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;UNPOOLED&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;com.mysql.cj.jdbc.Driver&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;jdbc:mysql://localhost:3306/powernode&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;root&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;root&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 class="tag">&lt;<span class="name">mappers</span>&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;CarMapper.xml&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 class="tag">&lt;/<span class="name">configuration</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><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><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="meta">@Test</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">testDataSource</span><span class="params">()</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">Car</span> <span class="variable">car</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">Car</span>();</span><br><span class="line">    car.setCarNum(<span class="string">&quot;133&quot;</span>);</span><br><span class="line">    car.setBrand(<span class="string">&quot;丰田霸道&quot;</span>);</span><br><span class="line">    car.setGuidePrice(<span class="number">50.3</span>);</span><br><span class="line">    car.setProduceTime(<span class="string">&quot;2020-01-10&quot;</span>);</span><br><span class="line">    car.setCarType(<span class="string">&quot;燃油车&quot;</span>);</span><br><span class="line">    <span class="comment">// 获取SqlSessionFactory对象</span></span><br><span class="line">    <span class="type">SqlSessionFactoryBuilder</span> <span class="variable">sqlSessionFactoryBuilder</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">SqlSessionFactoryBuilder</span>();</span><br><span class="line">    <span class="type">SqlSessionFactory</span> <span class="variable">sqlSessionFactory</span> <span class="operator">=</span> sqlSessionFactoryBuilder.build(Resources.getResourceAsStream(<span class="string">&quot;mybatis-config3.xml&quot;</span>));</span><br><span class="line">    <span class="comment">// 获取SqlSession对象</span></span><br><span class="line">    <span class="type">SqlSession</span> <span class="variable">sqlSession</span> <span class="operator">=</span> sqlSessionFactory.openSession(<span class="literal">true</span>);</span><br><span class="line">    <span class="comment">// 执行SQL</span></span><br><span class="line">    <span class="type">int</span> <span class="variable">count</span> <span class="operator">=</span> sqlSession.insert(<span class="string">&quot;insertCar&quot;</span>, car);</span><br><span class="line">    System.out.println(<span class="string">&quot;插入了几条记录：&quot;</span> + count);</span><br><span class="line">    <span class="comment">// 关闭会话</span></span><br><span class="line">    sqlSession.close();</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>当type是UNPOOLED，控制台输出：</p><p><img src="https://cdn.nlark.com/yuque/0/2022/png/21376908/1660125487038-29390827-4e20-431a-8d62-f56a40bf0349.png?x-oss-process=image%2Fwatermark%2Ctype_d3F5LW1pY3JvaGVp%2Csize_47%2Ctext_5Yqo5Yqb6IqC54K5%2Ccolor_FFFFFF%2Cshadow_50%2Ct_80%2Cg_se%2Cx_10%2Cy_10" alt="img"></p><p>修改配置文件mybatis-config3.xml中的配置：</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">&lt;dataSource type=<span class="string">&quot;POOLED&quot;</span>&gt;</span><br></pre></td></tr></table></figure><p>Java测试程序不需要修改，直接执行，看控制台输出：</p><p><img src="https://cdn.nlark.com/yuque/0/2022/png/21376908/1660125701333-b14f63d8-c442-4211-ad77-884f21162357.png?x-oss-process=image%2Fwatermark%2Ctype_d3F5LW1pY3JvaGVp%2Csize_46%2Ctext_5Yqo5Yqb6IqC54K5%2Ccolor_FFFFFF%2Cshadow_50%2Ct_80%2Cg_se%2Cx_10%2Cy_10" alt="img"></p><p>通过测试得出：UNPOOLED不会使用连接池，每一次都会新建JDBC连接对象。POOLED会使用数据库连接池。【这个连接池是mybatis自己实现的。】</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">&lt;dataSource type=<span class="string">&quot;JNDI&quot;</span>&gt;</span><br></pre></td></tr></table></figure><p>JNDI的方式：表示对接JNDI服务器中的连接池。这种方式给了我们可以使用第三方连接池的接口。如果想使用dbcp、c3p0、druid（德鲁伊）等，需要使用这种方式。</p><p>这种再重点说一下type=”POOLED”的时候，它的属性有哪些？</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></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></span><br><span class="line"><span class="meta"><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 class="tag">&lt;<span class="name">environments</span> <span class="attr">default</span>=<span class="string">&quot;dev&quot;</span>&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;dev&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;com.mysql.cj.jdbc.Driver&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;jdbc:mysql://localhost:3306/powernode&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;root&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;root&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">property</span> <span class="attr">name</span>=<span class="string">&quot;poolMaximumActiveConnections&quot;</span> <span class="attr">value</span>=<span class="string">&quot;3&quot;</span>/&gt;</span></span><br><span class="line">        <span class="comment">&lt;!--这是一个底层设置，如果获取连接花费了相当长的时间，连接池会打印状态日志并重新尝试获取一个连接（避免在误配置的情况下一直失败且不打印日志），默认值：20000 毫秒（即 20 秒）。--&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="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;poolMaximumCheckoutTime&quot;</span> <span class="attr">value</span>=<span class="string">&quot;20000&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">property</span> <span class="attr">name</span>=<span class="string">&quot;poolMaximumIdleConnections&quot;</span> <span class="attr">value</span>=<span class="string">&quot;1&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 class="tag">&lt;<span class="name">mappers</span>&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;CarMapper.xml&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 class="tag">&lt;/<span class="name">configuration</span>&gt;</span></span><br></pre></td></tr></table></figure><p>poolMaximumActiveConnections：最大的活动的连接数量。默认值10</p><p>poolMaximumIdleConnections：最大的空闲连接数量。默认值5</p><p>poolMaximumCheckoutTime：强行回归池的时间。默认值20秒。</p><p>poolTimeToWait：当无法获取到空闲连接时，每隔20秒打印一次日志，避免因代码配置有误，导致傻等。（时长是可以配置的）</p><p>当然，还有其他属性。对于连接池来说，以上几个属性比较重要。</p><p>最大的活动的连接数量就是连接池连接数量的上限。默认值10，如果有10个请求正在使用这10个连接，第11个请求只能等待空闲连接。</p><p>最大的空闲连接数量。默认值5，如何已经有了5个空闲连接，当第6个连接要空闲下来的时候，连接池会选择关闭该连接对象。来减少数据库的开销。</p><p>需要根据系统的并发情况，来合理调整连接池最大连接数以及最多空闲数量。充分发挥数据库连接池的性能。【可以根据实际情况进行测试，然后调整一个合理的数量。】</p><p>下图是默认配置：</p><p><img src="https://cdn.nlark.com/yuque/0/2022/png/21376908/1660126871013-88f83ea4-94e9-4088-bdb4-06a4fd73866a.png?x-oss-process=image%2Fwatermark%2Ctype_d3F5LW1pY3JvaGVp%2Csize_18%2Ctext_5Yqo5Yqb6IqC54K5%2Ccolor_FFFFFF%2Cshadow_50%2Ct_80%2Cg_se%2Cx_10%2Cy_10" alt="img"></p><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></pre></td><td class="code"><pre><span class="line"><span class="meta">@Test</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">testPool</span><span class="params">()</span> <span class="keyword">throws</span> Exception&#123;</span><br><span class="line"><span class="type">SqlSessionFactoryBuilder</span> <span class="variable">sqlSessionFactoryBuilder</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">SqlSessionFactoryBuilder</span>();</span><br><span class="line"><span class="type">SqlSessionFactory</span> <span class="variable">sqlSessionFactory</span> <span class="operator">=</span> sqlSessionFactoryBuilder.build(Resources.getResourceAsStream(<span class="string">&quot;mybatis-config3.xml&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; <span class="number">4</span>; i++) &#123;</span><br><span class="line">    <span class="type">SqlSession</span> <span class="variable">sqlSession</span> <span class="operator">=</span> sqlSessionFactory.openSession();</span><br><span class="line">    <span class="type">Object</span> <span class="variable">selectCarByCarNum</span> <span class="operator">=</span> sqlSession.selectOne(<span class="string">&quot;selectCarByCarNum&quot;</span>);</span><br><span class="line">&#125;</span><br><span class="line">&#125;</span><br><span class="line">&lt;select id=<span class="string">&quot;selectCarByCarNum&quot;</span> resultType=<span class="string">&quot;com.powernode.mybatis.pojo.Car&quot;</span>&gt;</span><br><span class="line">  select id,car_num carNum,brand,guide_price guidePrice,produce_time produceTime,car_type carType from t_car <span class="type">where</span> <span class="variable">car_num</span> <span class="operator">=</span> <span class="string">&#x27;100&#x27;</span></span><br><span class="line">&lt;/select&gt;</span><br></pre></td></tr></table></figure><p><img src="https://cdn.nlark.com/yuque/0/2022/png/21376908/1660128646511-8df1006a-dd85-48d1-9b88-ce7969d9b546.png?x-oss-process=image%2Fwatermark%2Ctype_d3F5LW1pY3JvaGVp%2Csize_43%2Ctext_5Yqo5Yqb6IqC54K5%2Ccolor_FFFFFF%2Cshadow_50%2Ct_80%2Cg_se%2Cx_10%2Cy_10%2Fresize%2Cw_937%2Climit_0" alt="img"></p><h2 id="4-4-properties"><a href="#4-4-properties" class="headerlink" title="4.4 properties"></a>4.4 properties</h2><p>mybatis提供了更加灵活的配置，连接数据库的信息可以单独写到一个属性资源文件中，假设在类的根路径下创建jdbc.properties文件，配置如下：</p><figure class="highlight properties"><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="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/powernode</span></span><br></pre></td></tr></table></figure><p>在mybatis核心配置文件中引入并使用：</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></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></span><br><span class="line"><span class="meta"><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 class="tag">&lt;<span class="name">property</span> <span class="attr">name</span>=<span class="string">&quot;jdbc.username&quot;</span> <span class="attr">value</span>=<span class="string">&quot;root&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;jdbc.password&quot;</span> <span class="attr">value</span>=<span class="string">&quot;root&quot;</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">environments</span> <span class="attr">default</span>=<span class="string">&quot;dev&quot;</span>&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;dev&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="comment">&lt;!--$&#123;key&#125;使用--&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 class="tag">&lt;<span class="name">mappers</span>&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;CarMapper.xml&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 class="tag">&lt;/<span class="name">configuration</span>&gt;</span></span><br></pre></td></tr></table></figure><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></pre></td><td class="code"><pre><span class="line"><span class="meta">@Test</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">testProperties</span><span class="params">()</span> <span class="keyword">throws</span> Exception&#123;</span><br><span class="line"><span class="type">SqlSessionFactoryBuilder</span> <span class="variable">sqlSessionFactoryBuilder</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">SqlSessionFactoryBuilder</span>();</span><br><span class="line"><span class="type">SqlSessionFactory</span> <span class="variable">sqlSessionFactory</span> <span class="operator">=</span> sqlSessionFactoryBuilder.build(Resources.getResourceAsStream(<span class="string">&quot;mybatis-config4.xml&quot;</span>));</span><br><span class="line"><span class="type">SqlSession</span> <span class="variable">sqlSession</span> <span class="operator">=</span> sqlSessionFactory.openSession();</span><br><span class="line"><span class="type">Object</span> <span class="variable">car</span> <span class="operator">=</span> sqlSession.selectOne(<span class="string">&quot;selectCarByCarNum&quot;</span>);</span><br><span class="line">System.out.println(car);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p><strong>properties两个属性：</strong></p><p><strong>resource：这个属性从类的根路径下开始加载。【常用的。】</strong></p><p><strong>url：从指定的url加载，假设文件放在d:/jdbc.properties，这个url可以写成：file:///d:/jdbc.properties。注意是三个斜杠哦。</strong></p><p>注意：如果不知道mybatis-config.xml文件中标签的编写顺序的话，可以有两种方式知道它的顺序：</p><ul><li>第一种方式：查看dtd约束文件。</li><li>第二种方式：通过idea的报错提示信息。【一般采用这种方式】</li></ul><h2 id="4-5-mapper"><a href="#4-5-mapper" class="headerlink" title="4.5 mapper"></a>4.5 mapper</h2><p>mapper标签用来指定SQL映射文件的路径，包含多种指定方式，这里先主要看其中两种：</p><p>第一种：resource，从类的根路径下开始加载【比url常用】</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></pre></td><td class="code"><pre><span class="line"><span class="tag">&lt;<span class="name">mappers</span>&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;CarMapper.xml&quot;</span>/&gt;</span></span><br><span class="line"><span class="tag">&lt;/<span class="name">mappers</span>&gt;</span></span><br></pre></td></tr></table></figure><p>如果是这样写的话，必须保证类的根下有CarMapper.xml文件。</p><p>如果类的根路径下有一个包叫做test，CarMapper.xml如果放在test包下的话，这个配置应该是这样写：</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></pre></td><td class="code"><pre><span class="line"><span class="tag">&lt;<span class="name">mappers</span>&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;test/CarMapper.xml&quot;</span>/&gt;</span></span><br><span class="line"><span class="tag">&lt;/<span class="name">mappers</span>&gt;</span></span><br></pre></td></tr></table></figure><p>第二种：url，从指定的url位置加载</p><p>假设CarMapper.xml文件放在d盘的根下，这个配置就需要这样写：</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></pre></td><td class="code"><pre><span class="line"><span class="tag">&lt;<span class="name">mappers</span>&gt;</span></span><br><span class="line">  <span class="tag">&lt;<span class="name">mapper</span> <span class="attr">url</span>=<span class="string">&quot;file:///d:/CarMapper.xml&quot;</span>/&gt;</span></span><br><span class="line"><span class="tag">&lt;/<span class="name">mappers</span>&gt;</span></span><br></pre></td></tr></table></figure><p><strong>mapper还有其他的指定方式，后面再看！！！</strong></p><h1 id="五、手写MyBatis框架（掌握原理）"><a href="#五、手写MyBatis框架（掌握原理）" class="headerlink" title="五、手写MyBatis框架（掌握原理）"></a>五、手写MyBatis框架（掌握原理）</h1><p>警示：该部分内容有难度，基础较弱的程序员可能有些部分是听不懂的，如果无法跟下来，可直接跳过，不影响后续知识点的学习。当然，如果你要能够跟下来，必然会让你加深对MyBatis框架的理解。</p><p><strong>我们给自己的框架起个名：GodBatis（起名灵感来源于：my god!!! 我的天呢！）</strong></p><h2 id="5-1-dom4j解析XML文件"><a href="#5-1-dom4j解析XML文件" class="headerlink" title="5.1 dom4j解析XML文件"></a>5.1 dom4j解析XML文件</h2><p>该部分内容不再赘述，不会解析XML的，请观看老杜前面讲解的dom4j解析XML文件的视频。</p><p>模块名：parse-xml-by-dom4j（普通的Java Maven模块）</p><p>第一步：引入dom4j的依赖</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><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">&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 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>org.group<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>parse-xml-by-dom4j<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">dependencies</span>&gt;</span></span><br><span class="line">    <span class="comment">&lt;!--dom4j依赖--&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.dom4j<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>dom4j<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.1.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><span class="line">    <span class="comment">&lt;!--jaxen依赖--&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>jaxen<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>jaxen<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.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="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 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">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">properties</span>&gt;</span></span><br><span class="line"></span><br><span class="line"><span class="tag">&lt;/<span class="name">project</span>&gt;</span></span><br></pre></td></tr></table></figure><p>第二步：编写配置文件godbatis-config.xml</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></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><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">environments</span> <span class="attr">default</span>=<span class="string">&quot;dev&quot;</span>&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;dev&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;com.mysql.cj.jdbc.Driver&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;jdbc:mysql://localhost:3306/powernode&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;root&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;root&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">mappers</span>&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;sqlmapper.xml&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 class="tag">&lt;/<span class="name">environments</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;/<span class="name">configuration</span>&gt;</span></span><br></pre></td></tr></table></figure><p>第三步：解析godbatis-config.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><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="keyword">package</span> com.powernode.dom4j;</span><br><span class="line"></span><br><span class="line"><span class="keyword">import</span> org.dom4j.Document;</span><br><span class="line"><span class="keyword">import</span> org.dom4j.Element;</span><br><span class="line"><span class="keyword">import</span> org.dom4j.Node;</span><br><span class="line"><span class="keyword">import</span> org.dom4j.io.SAXReader;</span><br><span class="line"><span class="keyword">import</span> org.junit.Test;</span><br><span class="line"></span><br><span class="line"><span class="keyword">import</span> java.util.HashMap;</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"> * 使用dom4j解析XML文件</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_">ParseXMLByDom4j</span> &#123;</span><br><span class="line">    <span class="meta">@Test</span></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">testGodBatisConfig</span><span class="params">()</span> <span class="keyword">throws</span> Exception&#123;</span><br><span class="line"></span><br><span class="line">        <span class="comment">// 读取xml，获取document对象</span></span><br><span class="line">        <span class="type">SAXReader</span> <span class="variable">saxReader</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">SAXReader</span>();</span><br><span class="line">        <span class="type">Document</span> <span class="variable">document</span> <span class="operator">=</span> saxReader.read(Thread.currentThread().getContextClassLoader().getResourceAsStream(<span class="string">&quot;godbatis-config.xml&quot;</span>));</span><br><span class="line"></span><br><span class="line">        <span class="comment">// 获取&lt;environments&gt;标签的default属性的值</span></span><br><span class="line">        <span class="type">Element</span> <span class="variable">environmentsElt</span> <span class="operator">=</span> (Element)document.selectSingleNode(<span class="string">&quot;/configuration/environments&quot;</span>);</span><br><span class="line">        <span class="type">String</span> <span class="variable">defaultId</span> <span class="operator">=</span> environmentsElt.attributeValue(<span class="string">&quot;default&quot;</span>);</span><br><span class="line">        System.out.println(defaultId);</span><br><span class="line"></span><br><span class="line">        <span class="comment">// 获取environment标签</span></span><br><span class="line">        <span class="type">Element</span> <span class="variable">environmentElt</span> <span class="operator">=</span> (Element)document.selectSingleNode(<span class="string">&quot;/configuration/environments/environment[@id=&#x27;&quot;</span> + defaultId + <span class="string">&quot;&#x27;]&quot;</span>);</span><br><span class="line"></span><br><span class="line">        <span class="comment">// 获取事务管理器类型</span></span><br><span class="line">        <span class="type">Element</span> <span class="variable">transactionManager</span> <span class="operator">=</span> environmentElt.element(<span class="string">&quot;transactionManager&quot;</span>);</span><br><span class="line">        <span class="type">String</span> <span class="variable">transactionManagerType</span> <span class="operator">=</span> transactionManager.attributeValue(<span class="string">&quot;type&quot;</span>);</span><br><span class="line">        System.out.println(transactionManagerType);</span><br><span class="line"></span><br><span class="line">        <span class="comment">// 获取数据源类型</span></span><br><span class="line">        <span class="type">Element</span> <span class="variable">dataSource</span> <span class="operator">=</span> environmentElt.element(<span class="string">&quot;dataSource&quot;</span>);</span><br><span class="line">        <span class="type">String</span> <span class="variable">dataSourceType</span> <span class="operator">=</span> dataSource.attributeValue(<span class="string">&quot;type&quot;</span>);</span><br><span class="line">        System.out.println(dataSourceType);</span><br><span class="line"></span><br><span class="line">        <span class="comment">// 将数据源信息封装到Map集合</span></span><br><span class="line">        Map&lt;String,String&gt; dataSourceMap = <span class="keyword">new</span> <span class="title class_">HashMap</span>&lt;&gt;();</span><br><span class="line">        dataSource.elements().forEach(propertyElt -&gt; &#123;</span><br><span class="line">            dataSourceMap.put(propertyElt.attributeValue(<span class="string">&quot;name&quot;</span>), propertyElt.attributeValue(<span class="string">&quot;value&quot;</span>));</span><br><span class="line">        &#125;);</span><br><span class="line"></span><br><span class="line">        dataSourceMap.forEach((k, v) -&gt; System.out.println(k + <span class="string">&quot;:&quot;</span> + v));</span><br><span class="line"></span><br><span class="line">        <span class="comment">// 获取sqlmapper.xml文件的路径</span></span><br><span class="line">        <span class="type">Element</span> <span class="variable">mappersElt</span> <span class="operator">=</span> (Element) document.selectSingleNode(<span class="string">&quot;/configuration/environments/mappers&quot;</span>);</span><br><span class="line">        mappersElt.elements().forEach(mapper -&gt; &#123;</span><br><span class="line">            System.out.println(mapper.attributeValue(<span class="string">&quot;resource&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><p>执行结果：</p><p><img src="瑞吉外卖项目实战说明/1660182883453-edbe3196-015a-4c7e-b318-1593089d5496.png" alt="img"></p><p>第四步：编写配置文件sqlmapper.xml</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="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><br><span class="line"><span class="tag">&lt;<span class="name">mapper</span> <span class="attr">namespace</span>=<span class="string">&quot;car&quot;</span>&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;insertCar&quot;</span>&gt;</span></span><br><span class="line">    insert into t_car(id,car_num,brand,guide_price,produce_time,car_type) values(null,#&#123;carNum&#125;,#&#123;brand&#125;,#&#123;guidePrice&#125;,#&#123;produceTime&#125;,#&#123;carType&#125;)</span><br><span class="line">  <span class="tag">&lt;/<span class="name">insert</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;selectCarByCarNum&quot;</span> <span class="attr">resultType</span>=<span class="string">&quot;com.powernode.mybatis.pojo.Car&quot;</span>&gt;</span></span><br><span class="line">    select id,car_num carNum,brand,guide_price guidePrice,produce_time produceTime,car_type carType from t_car where car_num = #&#123;carNum&#125;</span><br><span class="line">  <span class="tag">&lt;/<span class="name">select</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;/<span class="name">mapper</span>&gt;</span></span><br></pre></td></tr></table></figure><p>第五步：解析sqlmapper.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></pre></td><td class="code"><pre><span class="line"><span class="meta">@Test</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">testSqlMapper</span><span class="params">()</span> <span class="keyword">throws</span> Exception&#123;</span><br><span class="line"><span class="comment">// 读取xml，获取document对象</span></span><br><span class="line"><span class="type">SAXReader</span> <span class="variable">saxReader</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">SAXReader</span>();</span><br><span class="line"><span class="type">Document</span> <span class="variable">document</span> <span class="operator">=</span> saxReader.read(Thread.currentThread().getContextClassLoader().getResourceAsStream(<span class="string">&quot;sqlmapper.xml&quot;</span>));</span><br><span class="line"></span><br><span class="line"><span class="comment">// 获取namespace</span></span><br><span class="line"><span class="type">Element</span> <span class="variable">mapperElt</span> <span class="operator">=</span> (Element) document.selectSingleNode(<span class="string">&quot;/mapper&quot;</span>);</span><br><span class="line"><span class="type">String</span> <span class="variable">namespace</span> <span class="operator">=</span> mapperElt.attributeValue(<span class="string">&quot;namespace&quot;</span>);</span><br><span class="line">System.out.println(namespace);</span><br><span class="line"></span><br><span class="line"><span class="comment">// 获取sql id</span></span><br><span class="line">mapperElt.elements().forEach(statementElt -&gt; &#123;</span><br><span class="line">    <span class="comment">// 标签名</span></span><br><span class="line">    <span class="type">String</span> <span class="variable">name</span> <span class="operator">=</span> statementElt.getName();</span><br><span class="line">    System.out.println(<span class="string">&quot;name:&quot;</span> + name);</span><br><span class="line">    <span class="comment">// 如果是select标签，还要获取它的resultType</span></span><br><span class="line">    <span class="keyword">if</span> (<span class="string">&quot;select&quot;</span>.equals(name)) &#123;</span><br><span class="line">        <span class="type">String</span> <span class="variable">resultType</span> <span class="operator">=</span> statementElt.attributeValue(<span class="string">&quot;resultType&quot;</span>);</span><br><span class="line">        System.out.println(<span class="string">&quot;resultType:&quot;</span> + resultType);</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="comment">// sql id</span></span><br><span class="line">    <span class="type">String</span> <span class="variable">id</span> <span class="operator">=</span> statementElt.attributeValue(<span class="string">&quot;id&quot;</span>);</span><br><span class="line">    System.out.println(<span class="string">&quot;sqlId:&quot;</span> + id);</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> statementElt.getTextTrim();</span><br><span class="line">    System.out.println(<span class="string">&quot;sql:&quot;</span> + sql);</span><br><span class="line">&#125;);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>执行结果：</p><p><img src="瑞吉外卖项目实战说明/1660183887361-baffabf0-4caa-4e64-86d0-5d5dcfb1edd6.png" alt="img"></p><h2 id="5-2-GodBatis"><a href="#5-2-GodBatis" class="headerlink" title="5.2 GodBatis"></a>5.2 GodBatis</h2><p>手写框架之前，如果没有思路，可以先参考一下mybatis的客户端程序，通过客户端程序来逆推需要的类，参考代码：</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></pre></td><td class="code"><pre><span class="line"><span class="meta">@Test</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">testInsert</span><span class="params">()</span>&#123;</span><br><span class="line">    <span class="type">SqlSession</span> <span class="variable">sqlSession</span> <span class="operator">=</span> <span class="literal">null</span>;</span><br><span class="line">    <span class="keyword">try</span> &#123;</span><br><span class="line">        <span class="comment">// 1.创建SqlSessionFactoryBuilder对象</span></span><br><span class="line">        <span class="type">SqlSessionFactoryBuilder</span> <span class="variable">sqlSessionFactoryBuilder</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">SqlSessionFactoryBuilder</span>();</span><br><span class="line">        <span class="comment">// 2.创建SqlSessionFactory对象</span></span><br><span class="line">        <span class="type">SqlSessionFactory</span> <span class="variable">sqlSessionFactory</span> <span class="operator">=</span> sqlSessionFactoryBuilder.build(Resources.getResourceAsStream(<span class="string">&quot;mybatis-config.xml&quot;</span>));</span><br><span class="line">        <span class="comment">// 3.创建SqlSession对象</span></span><br><span class="line">        sqlSession = sqlSessionFactory.openSession();</span><br><span class="line">        <span class="comment">// 4.执行SQL</span></span><br><span class="line">        <span class="type">Car</span> <span class="variable">car</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">Car</span>(<span class="literal">null</span>, <span class="string">&quot;111&quot;</span>, <span class="string">&quot;宝马X7&quot;</span>, <span class="string">&quot;70.3&quot;</span>, <span class="string">&quot;2010-10-11&quot;</span>, <span class="string">&quot;燃油车&quot;</span>);</span><br><span class="line">        <span class="type">int</span> <span class="variable">count</span> <span class="operator">=</span> sqlSession.insert(<span class="string">&quot;insertCar&quot;</span>,car);</span><br><span class="line">        System.out.println(<span class="string">&quot;更新了几条记录：&quot;</span> + count);</span><br><span class="line">        <span class="comment">// 5.提交</span></span><br><span class="line">        sqlSession.commit();</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> (sqlSession != <span class="literal">null</span>) &#123;</span><br><span class="line">            sqlSession.rollback();</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="comment">// 6.关闭</span></span><br><span class="line">        <span class="keyword">if</span> (sqlSession != <span class="literal">null</span>) &#123;</span><br><span class="line">            sqlSession.close();</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="meta">@Test</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">testSelectOne</span><span class="params">()</span>&#123;</span><br><span class="line">    <span class="type">SqlSession</span> <span class="variable">sqlSession</span> <span class="operator">=</span> <span class="literal">null</span>;</span><br><span class="line">    <span class="keyword">try</span> &#123;</span><br><span class="line">        <span class="comment">// 1.创建SqlSessionFactoryBuilder对象</span></span><br><span class="line">        <span class="type">SqlSessionFactoryBuilder</span> <span class="variable">sqlSessionFactoryBuilder</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">SqlSessionFactoryBuilder</span>();</span><br><span class="line">        <span class="comment">// 2.创建SqlSessionFactory对象</span></span><br><span class="line">        <span class="type">SqlSessionFactory</span> <span class="variable">sqlSessionFactory</span> <span class="operator">=</span> sqlSessionFactoryBuilder.build(Resources.getResourceAsStream(<span class="string">&quot;mybatis-config.xml&quot;</span>));</span><br><span class="line">        <span class="comment">// 3.创建SqlSession对象</span></span><br><span class="line">        sqlSession = sqlSessionFactory.openSession();</span><br><span class="line">        <span class="comment">// 4.执行SQL</span></span><br><span class="line">        <span class="type">Car</span> <span class="variable">car</span> <span class="operator">=</span> (Car)sqlSession.selectOne(<span class="string">&quot;selectCarByCarNum&quot;</span>, <span class="string">&quot;111&quot;</span>);</span><br><span class="line">        System.out.println(car);</span><br><span class="line">        <span class="comment">// 5.提交</span></span><br><span class="line">        sqlSession.commit();</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> (sqlSession != <span class="literal">null</span>) &#123;</span><br><span class="line">            sqlSession.rollback();</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="comment">// 6.关闭</span></span><br><span class="line">        <span class="keyword">if</span> (sqlSession != <span class="literal">null</span>) &#123;</span><br><span class="line">            sqlSession.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><h3 id="第一步：IDEA中创建模块"><a href="#第一步：IDEA中创建模块" class="headerlink" title="第一步：IDEA中创建模块"></a>第一步：IDEA中创建模块</h3><p>模块：godbatis（创建普通的Java Maven模块，打包方式jar），引入相关依赖</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><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">&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 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>org.god<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>godbatis<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 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">dependencies</span>&gt;</span></span><br><span class="line">        <span class="comment">&lt;!--dom4j依赖--&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.dom4j<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>dom4j<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.1.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><span class="line">        <span class="comment">&lt;!--jaxen依赖--&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>jaxen<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>jaxen<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.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="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 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">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">properties</span>&gt;</span></span><br><span class="line"></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="第二步：资源工具类，方便获取指向配置文件的输入流"><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="keyword">package</span> org.god.core;</span><br><span class="line"></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"> * 资源工具类</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@author</span> 老杜</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@version</span> 1.0</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@since</span> 1.0</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_">Resources</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 class="doctag">@param</span> config</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@return</span> 输入流，该输入流指向类路径中的配置文件</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> InputStream <span class="title function_">getResourcesAsStream</span><span class="params">(String config)</span>&#123;</span><br><span class="line">        <span class="keyword">return</span> Thread.currentThread().getContextClassLoader().getResourceAsStream(config);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h3 id="第三步：定义SqlSessionFactoryBuilder类"><a href="#第三步：定义SqlSessionFactoryBuilder类" class="headerlink" title="第三步：定义SqlSessionFactoryBuilder类"></a>第三步：定义SqlSessionFactoryBuilder类</h3><p>提供一个无参数构造方法，再提供一个build方法，该build方法要返回SqlSessionFactory对象</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="keyword">package</span> org.god.core;</span><br><span class="line"></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"> * SqlSessionFactory对象构建器</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@author</span> 老杜</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@version</span> 1.0</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@since</span> 1.0</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_">SqlSessionFactoryBuilder</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="title function_">SqlSessionFactoryBuilder</span><span class="params">()</span> &#123;</span><br><span class="line">    &#125;</span><br><span class="line"></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">     * 该方法主要功能是：读取godbatis核心配置文件，并构建SqlSessionFactory对象</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@param</span> inputStream 指向核心配置文件的输入流</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@return</span> SqlSessionFactory对象</span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    <span class="keyword">public</span> SqlSessionFactory <span class="title function_">build</span><span class="params">(InputStream inputStream)</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">        <span class="comment">// 解析配置文件，获取所有的SQL映射对象</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">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="第四步：分析SqlSessionFactory类中有哪些属性"><a href="#第四步：分析SqlSessionFactory类中有哪些属性" class="headerlink" title="第四步：分析SqlSessionFactory类中有哪些属性"></a>第四步：分析SqlSessionFactory类中有哪些属性</h3><ul><li><p>事务管理器</p></li><li><p>GodJDBCTransaction</p></li><li><p>SQL映射对象集合</p></li><li><p>Map<String, GodMappedStatement></p></li></ul><h3 id="第五步：定义GodJDBCTransaction"><a href="#第五步：定义GodJDBCTransaction" class="headerlink" title="第五步：定义GodJDBCTransaction"></a>第五步：定义GodJDBCTransaction</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><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></pre></td><td class="code"><pre><span class="line"><span class="keyword">package</span> org.god.core;</span><br><span class="line"></span><br><span class="line"><span class="keyword">import</span> java.sql.Connection;</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 class="doctag">@author</span> 老杜</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@version</span> 1.0</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@since</span> 1.0</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_">TransactionManager</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"><span class="comment">     */</span></span><br><span class="line">    <span class="keyword">void</span> <span class="title function_">commit</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">    <span class="keyword">void</span> <span class="title function_">rollback</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">    <span class="keyword">void</span> <span class="title function_">close</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">    <span class="keyword">void</span> <span class="title function_">openConnection</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 class="doctag">@return</span> 连接对象</span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    Connection <span class="title function_">getConnection</span><span class="params">()</span>;</span><br><span class="line">&#125;</span><br><span class="line"><span class="keyword">package</span> org.god.core;</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.sql.Connection;</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="comment">/**</span></span><br><span class="line"><span class="comment"> * 事务管理器</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@author</span> 老杜</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@version</span> 1.0</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@since</span> 1.0</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_">GodJDBCTransaction</span> <span class="keyword">implements</span> <span class="title class_">TransactionManager</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"><span class="comment">     */</span></span><br><span class="line">    <span class="keyword">private</span> Connection conn;</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">private</span> DataSource dataSource;</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">     * true表示自动提交</span></span><br><span class="line"><span class="comment">     * false表示不自动提交</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> autoCommit;</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 class="doctag">@param</span> autoCommit</span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    <span class="keyword">public</span> <span class="title function_">GodJDBCTransaction</span><span class="params">(DataSource dataSource, <span class="type">boolean</span> autoCommit)</span> &#123;</span><br><span class="line">        <span class="built_in">this</span>.dataSource = dataSource;</span><br><span class="line">        <span class="built_in">this</span>.autoCommit = autoCommit;</span><br><span 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_">commit</span><span class="params">()</span>&#123;</span><br><span class="line">        <span class="keyword">try</span> &#123;</span><br><span class="line">            conn.commit();</span><br><span class="line">        &#125; <span class="keyword">catch</span> (SQLException e) &#123;</span><br><span class="line">            <span class="keyword">throw</span> <span class="keyword">new</span> <span class="title class_">RuntimeException</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">void</span> <span class="title function_">rollback</span><span class="params">()</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 e) &#123;</span><br><span class="line">            <span class="keyword">throw</span> <span class="keyword">new</span> <span class="title class_">RuntimeException</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="meta">@Override</span></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">close</span><span class="params">()</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">            <span class="keyword">throw</span> <span class="keyword">new</span> <span class="title class_">RuntimeException</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="meta">@Override</span></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">openConnection</span><span class="params">()</span> &#123;</span><br><span class="line">        <span class="keyword">try</span> &#123;</span><br><span class="line">            <span class="built_in">this</span>.conn = dataSource.getConnection();</span><br><span class="line">            <span class="built_in">this</span>.conn.setAutoCommit(<span class="built_in">this</span>.autoCommit);</span><br><span class="line">        &#125; <span class="keyword">catch</span> (SQLException e) &#123;</span><br><span class="line">            <span class="keyword">throw</span> <span class="keyword">new</span> <span class="title class_">RuntimeException</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="meta">@Override</span></span><br><span class="line">    <span class="keyword">public</span> Connection <span class="title function_">getConnection</span><span class="params">()</span> &#123;</span><br><span class="line">        <span class="keyword">return</span> conn;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h3 id="第六步：事务管理器中需要数据源，定义GodUNPOOLEDDataSource"><a href="#第六步：事务管理器中需要数据源，定义GodUNPOOLEDDataSource" class="headerlink" title="第六步：事务管理器中需要数据源，定义GodUNPOOLEDDataSource"></a>第六步：事务管理器中需要数据源，定义GodUNPOOLEDDataSource</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="keyword">package</span> org.god.core;</span><br><span class="line"></span><br><span class="line"><span class="keyword">import</span> java.io.PrintWriter;</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.DriverManager;</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.SQLFeatureNotSupportedException;</span><br><span class="line"><span class="keyword">import</span> java.util.logging.Logger;</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 class="doctag">@author</span> 老杜</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@version</span> 1.0</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@since</span> 1.0</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_">GodUNPOOLEDDataSource</span> <span class="keyword">implements</span> <span class="title class_">javax</span>.sql.DataSource&#123;</span><br><span class="line">    <span class="keyword">private</span> String url;</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><br><span class="line">    <span class="keyword">public</span> <span class="title function_">GodUNPOOLEDDataSource</span><span class="params">(String driver, String url, String username, String password)</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">            Class.forName(driver);</span><br><span class="line">        &#125; <span class="keyword">catch</span> (ClassNotFoundException e) &#123;</span><br><span class="line">            <span class="keyword">throw</span> <span class="keyword">new</span> <span class="title class_">RuntimeException</span>(e);</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="built_in">this</span>.url = url;</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">    &#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> 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="keyword">return</span> DriverManager.getConnection(url, username, 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> Connection <span class="title function_">getConnection</span><span class="params">(String username, String password)</span> <span class="keyword">throws</span> SQLException &#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"></span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="keyword">public</span> PrintWriter <span class="title function_">getLogWriter</span><span class="params">()</span> <span class="keyword">throws</span> SQLException &#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"></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_">setLogWriter</span><span class="params">(PrintWriter out)</span> <span class="keyword">throws</span> SQLException &#123;</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_">setLoginTimeout</span><span class="params">(<span class="type">int</span> seconds)</span> <span class="keyword">throws</span> SQLException &#123;</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="type">int</span> <span class="title function_">getLoginTimeout</span><span class="params">()</span> <span class="keyword">throws</span> SQLException &#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="meta">@Override</span></span><br><span class="line">    <span class="keyword">public</span> Logger <span class="title function_">getParentLogger</span><span class="params">()</span> <span class="keyword">throws</span> SQLFeatureNotSupportedException &#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"></span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="keyword">public</span> &lt;T&gt; T <span class="title function_">unwrap</span><span class="params">(Class&lt;T&gt; iface)</span> <span class="keyword">throws</span> SQLException &#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"></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_">isWrapperFor</span><span class="params">(Class&lt;?&gt; iface)</span> <span class="keyword">throws</span> SQLException &#123;</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">&#125;</span><br></pre></td></tr></table></figure><h3 id="第七步：定义GodMappedStatement"><a href="#第七步：定义GodMappedStatement" class="headerlink" title="第七步：定义GodMappedStatement"></a>第七步：定义GodMappedStatement</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="keyword">package</span> org.god.core;</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 class="doctag">@author</span> 老杜</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@version</span> 1.0</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@since</span> 1.0</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_">GodMappedStatement</span> &#123;</span><br><span class="line">    <span class="keyword">private</span> String sqlId;</span><br><span class="line">    <span class="keyword">private</span> String resultType;</span><br><span class="line">    <span class="keyword">private</span> String sql;</span><br><span class="line">    <span class="keyword">private</span> String parameterType;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">private</span> String sqlType;</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;GodMappedStatement&#123;&quot;</span> +</span><br><span class="line">                <span class="string">&quot;sqlId=&#x27;&quot;</span> + sqlId + <span class="string">&#x27;\&#x27;&#x27;</span> +</span><br><span class="line">                <span class="string">&quot;, resultType=&#x27;&quot;</span> + resultType + <span class="string">&#x27;\&#x27;&#x27;</span> +</span><br><span class="line">                <span class="string">&quot;, sql=&#x27;&quot;</span> + sql + <span class="string">&#x27;\&#x27;&#x27;</span> +</span><br><span class="line">                <span class="string">&quot;, parameterType=&#x27;&quot;</span> + parameterType + <span class="string">&#x27;\&#x27;&#x27;</span> +</span><br><span class="line">                <span class="string">&quot;, sqlType=&#x27;&quot;</span> + sqlType + <span class="string">&#x27;\&#x27;&#x27;</span> +</span><br><span class="line">                <span class="string">&#x27;&#125;&#x27;</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_">getSqlId</span><span class="params">()</span> &#123;</span><br><span class="line">        <span class="keyword">return</span> sqlId;</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_">setSqlId</span><span class="params">(String sqlId)</span> &#123;</span><br><span class="line">        <span class="built_in">this</span>.sqlId = sqlId;</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_">getResultType</span><span class="params">()</span> &#123;</span><br><span class="line">        <span class="keyword">return</span> resultType;</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_">setResultType</span><span class="params">(String resultType)</span> &#123;</span><br><span class="line">        <span class="built_in">this</span>.resultType = resultType;</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_">getSql</span><span class="params">()</span> &#123;</span><br><span class="line">        <span class="keyword">return</span> sql;</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_">setSql</span><span class="params">(String sql)</span> &#123;</span><br><span class="line">        <span class="built_in">this</span>.sql = sql;</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_">getParameterType</span><span class="params">()</span> &#123;</span><br><span class="line">        <span class="keyword">return</span> parameterType;</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_">setParameterType</span><span class="params">(String parameterType)</span> &#123;</span><br><span class="line">        <span class="built_in">this</span>.parameterType = parameterType;</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_">getSqlType</span><span class="params">()</span> &#123;</span><br><span class="line">        <span class="keyword">return</span> sqlType;</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_">setSqlType</span><span class="params">(String sqlType)</span> &#123;</span><br><span class="line">        <span class="built_in">this</span>.sqlType = sqlType;</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_">GodMappedStatement</span><span class="params">(String sqlId, String resultType, String sql, String parameterType, String sqlType)</span> &#123;</span><br><span class="line">        <span class="built_in">this</span>.sqlId = sqlId;</span><br><span class="line">        <span class="built_in">this</span>.resultType = resultType;</span><br><span class="line">        <span class="built_in">this</span>.sql = sql;</span><br><span class="line">        <span class="built_in">this</span>.parameterType = parameterType;</span><br><span class="line">        <span class="built_in">this</span>.sqlType = sqlType;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h3 id="第八步：完善SqlSessionFactory类"><a href="#第八步：完善SqlSessionFactory类" class="headerlink" title="第八步：完善SqlSessionFactory类"></a>第八步：完善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></pre></td><td class="code"><pre><span class="line"><span class="keyword">package</span> org.god.core;</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.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"> * SqlSession工厂对象，使用SqlSessionFactory可以获取会话对象</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@author</span> 老杜</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@version</span> 1.0</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@since</span> 1.0</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_">SqlSessionFactory</span> &#123;</span><br><span class="line">    <span class="keyword">private</span> TransactionManager transactionManager;</span><br><span class="line">    <span class="keyword">private</span> Map&lt;String, GodMappedStatement&gt; mappedStatements;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">public</span> <span class="title function_">SqlSessionFactory</span><span class="params">(TransactionManager transactionManager, Map&lt;String, GodMappedStatement&gt; mappedStatements)</span> &#123;</span><br><span class="line">        <span class="built_in">this</span>.transactionManager = transactionManager;</span><br><span class="line">        <span class="built_in">this</span>.mappedStatements = mappedStatements;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">public</span> TransactionManager <span class="title function_">getTransactionManager</span><span class="params">()</span> &#123;</span><br><span class="line">        <span class="keyword">return</span> transactionManager;</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_">setTransactionManager</span><span class="params">(TransactionManager transactionManager)</span> &#123;</span><br><span class="line">        <span class="built_in">this</span>.transactionManager = transactionManager;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">public</span> Map&lt;String, GodMappedStatement&gt; <span class="title function_">getMappedStatements</span><span class="params">()</span> &#123;</span><br><span class="line">        <span class="keyword">return</span> mappedStatements;</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_">setMappedStatements</span><span class="params">(Map&lt;String, GodMappedStatement&gt; mappedStatements)</span> &#123;</span><br><span class="line">        <span class="built_in">this</span>.mappedStatements = mappedStatements;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h3 id="第九步：完善SqlSessionFactoryBuilder中的build方法"><a href="#第九步：完善SqlSessionFactoryBuilder中的build方法" class="headerlink" title="第九步：完善SqlSessionFactoryBuilder中的build方法"></a>第九步：完善SqlSessionFactoryBuilder中的build方法</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></pre></td><td class="code"><pre><span class="line"><span class="keyword">package</span> org.god.core;</span><br><span class="line"></span><br><span class="line"><span class="keyword">import</span> org.dom4j.Document;</span><br><span class="line"><span class="keyword">import</span> org.dom4j.DocumentException;</span><br><span class="line"><span class="keyword">import</span> org.dom4j.Element;</span><br><span class="line"><span class="keyword">import</span> org.dom4j.io.SAXReader;</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.util.HashMap;</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"> * SqlSessionFactory对象构建器</span></span><br><span class="line"><span class="comment"> *</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@author</span> 老杜</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@version</span> 1.0</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@since</span> 1.0</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_">SqlSessionFactoryBuilder</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="title function_">SqlSessionFactoryBuilder</span><span class="params">()</span> &#123;</span><br><span class="line">    &#125;</span><br><span class="line"></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">     * 该方法主要功能是：读取godbatis核心配置文件，并构建SqlSessionFactory对象</span></span><br><span class="line"><span class="comment">     *</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@param</span> inputStream 指向核心配置文件的输入流</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@return</span> SqlSessionFactory对象</span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    <span class="keyword">public</span> SqlSessionFactory <span class="title function_">build</span><span class="params">(InputStream inputStream)</span> <span class="keyword">throws</span> DocumentException &#123;</span><br><span class="line">        <span class="type">SAXReader</span> <span class="variable">saxReader</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">SAXReader</span>();</span><br><span class="line">        <span class="type">Document</span> <span class="variable">document</span> <span class="operator">=</span> saxReader.read(inputStream);</span><br><span class="line">        <span class="type">Element</span> <span class="variable">environmentsElt</span> <span class="operator">=</span> (Element) document.selectSingleNode(<span class="string">&quot;/configuration/environments&quot;</span>);</span><br><span class="line">        <span class="type">String</span> <span class="variable">defaultEnv</span> <span class="operator">=</span> environmentsElt.attributeValue(<span class="string">&quot;default&quot;</span>);</span><br><span class="line">        <span class="type">Element</span> <span class="variable">environmentElt</span> <span class="operator">=</span> (Element) document.selectSingleNode(<span class="string">&quot;/configuration/environments/environment[@id=&#x27;&quot;</span> + defaultEnv + <span class="string">&quot;&#x27;]&quot;</span>);</span><br><span class="line">        <span class="comment">// 解析配置文件，创建数据源对象</span></span><br><span class="line">        <span class="type">Element</span> <span class="variable">dataSourceElt</span> <span class="operator">=</span> environmentElt.element(<span class="string">&quot;dataSource&quot;</span>);</span><br><span class="line">        <span class="type">DataSource</span> <span class="variable">dataSource</span> <span class="operator">=</span> getDataSource(dataSourceElt);</span><br><span class="line">        <span class="comment">// 解析配置文件，创建事务管理器对象</span></span><br><span class="line">        <span class="type">Element</span> <span class="variable">transactionManagerElt</span> <span class="operator">=</span> environmentElt.element(<span class="string">&quot;transactionManager&quot;</span>);</span><br><span class="line">        <span class="type">TransactionManager</span> <span class="variable">transactionManager</span> <span class="operator">=</span> getTransactionManager(transactionManagerElt, dataSource);</span><br><span class="line">        <span class="comment">// 解析配置文件，获取所有的SQL映射对象</span></span><br><span class="line">        <span class="type">Element</span> <span class="variable">mappers</span> <span class="operator">=</span> environmentsElt.element(<span class="string">&quot;mappers&quot;</span>);</span><br><span class="line">        Map&lt;String, GodMappedStatement&gt; mappedStatements = getMappedStatements(mappers);</span><br><span class="line">        <span class="comment">// 将以上信息封装到SqlSessionFactory对象中</span></span><br><span class="line">        <span class="type">SqlSessionFactory</span> <span class="variable">sqlSessionFactory</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">SqlSessionFactory</span>(transactionManager, mappedStatements);</span><br><span class="line">        <span class="comment">// 返回</span></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="keyword">private</span> Map&lt;String, GodMappedStatement&gt; <span class="title function_">getMappedStatements</span><span class="params">(Element mappers)</span> &#123;</span><br><span class="line">        Map&lt;String, GodMappedStatement&gt; mappedStatements = <span class="keyword">new</span> <span class="title class_">HashMap</span>&lt;&gt;();</span><br><span class="line">        mappers.elements().forEach(mapperElt -&gt; &#123;</span><br><span class="line">            <span class="keyword">try</span> &#123;</span><br><span class="line">                <span class="type">String</span> <span class="variable">resource</span> <span class="operator">=</span> mapperElt.attributeValue(<span class="string">&quot;resource&quot;</span>);</span><br><span class="line">                <span class="type">SAXReader</span> <span class="variable">saxReader</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">SAXReader</span>();</span><br><span class="line">                <span class="type">Document</span> <span class="variable">document</span> <span class="operator">=</span> saxReader.read(Resources.getResourcesAsStream(resource));</span><br><span class="line">                <span class="type">Element</span> <span class="variable">mapper</span> <span class="operator">=</span> (Element) document.selectSingleNode(<span class="string">&quot;/mapper&quot;</span>);</span><br><span class="line">                <span class="type">String</span> <span class="variable">namespace</span> <span class="operator">=</span> mapper.attributeValue(<span class="string">&quot;namespace&quot;</span>);</span><br><span class="line"></span><br><span class="line">                mapper.elements().forEach(sqlMapper -&gt; &#123;</span><br><span class="line">                    <span class="type">String</span> <span class="variable">sqlId</span> <span class="operator">=</span> sqlMapper.attributeValue(<span class="string">&quot;id&quot;</span>);</span><br><span class="line">                    <span class="type">String</span> <span class="variable">sql</span> <span class="operator">=</span> sqlMapper.getTextTrim();</span><br><span class="line">                    <span class="type">String</span> <span class="variable">parameterType</span> <span class="operator">=</span> sqlMapper.attributeValue(<span class="string">&quot;parameterType&quot;</span>);</span><br><span class="line">                    <span class="type">String</span> <span class="variable">resultType</span> <span class="operator">=</span> sqlMapper.attributeValue(<span class="string">&quot;resultType&quot;</span>);</span><br><span class="line">                    <span class="type">String</span> <span class="variable">sqlType</span> <span class="operator">=</span> sqlMapper.getName().toLowerCase();</span><br><span class="line">                    <span class="comment">// 封装GodMappedStatement对象</span></span><br><span class="line">                    <span class="type">GodMappedStatement</span> <span class="variable">godMappedStatement</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">GodMappedStatement</span>(sqlId, resultType, sql, parameterType, sqlType);</span><br><span class="line">                    mappedStatements.put(namespace + <span class="string">&quot;.&quot;</span> + sqlId, godMappedStatement);</span><br><span class="line">                &#125;);</span><br><span class="line"></span><br><span class="line">            &#125; <span class="keyword">catch</span> (DocumentException e) &#123;</span><br><span class="line">                <span class="keyword">throw</span> <span class="keyword">new</span> <span class="title class_">RuntimeException</span>(e);</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;);</span><br><span class="line">        <span class="keyword">return</span> mappedStatements;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line"></span><br><span class="line">    <span class="keyword">private</span> TransactionManager <span class="title function_">getTransactionManager</span><span class="params">(Element transactionManagerElt, DataSource dataSource)</span> &#123;</span><br><span class="line">        <span class="type">String</span> <span class="variable">type</span> <span class="operator">=</span> transactionManagerElt.attributeValue(<span class="string">&quot;type&quot;</span>).toUpperCase();</span><br><span class="line">        <span class="type">TransactionManager</span> <span class="variable">transactionManager</span> <span class="operator">=</span> <span class="literal">null</span>;</span><br><span class="line">        <span class="keyword">if</span> (<span class="string">&quot;JDBC&quot;</span>.equals(type)) &#123;</span><br><span class="line">            <span class="comment">// 使用JDBC事务</span></span><br><span class="line">            transactionManager = <span class="keyword">new</span> <span class="title class_">GodJDBCTransaction</span>(dataSource, <span class="literal">false</span>);</span><br><span class="line">        &#125; <span class="keyword">else</span> <span class="keyword">if</span> (<span class="string">&quot;MANAGED&quot;</span>.equals(type)) &#123;</span><br><span class="line">            <span class="comment">// 事务管理器是交给JEE容器的</span></span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">return</span> transactionManager;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">private</span> DataSource <span class="title function_">getDataSource</span><span class="params">(Element dataSourceElt)</span> &#123;</span><br><span class="line">        <span class="comment">// 获取所有数据源的属性配置</span></span><br><span class="line">        Map&lt;String, String&gt; dataSourceMap = <span class="keyword">new</span> <span class="title class_">HashMap</span>&lt;&gt;();</span><br><span class="line">        dataSourceElt.elements().forEach(propertyElt -&gt; &#123;</span><br><span class="line">            dataSourceMap.put(propertyElt.attributeValue(<span class="string">&quot;name&quot;</span>), propertyElt.attributeValue(<span class="string">&quot;value&quot;</span>));</span><br><span class="line">        &#125;);</span><br><span class="line"></span><br><span class="line">        <span class="type">String</span> <span class="variable">dataSourceType</span> <span class="operator">=</span> dataSourceElt.attributeValue(<span class="string">&quot;type&quot;</span>).toUpperCase();</span><br><span class="line">        <span class="type">DataSource</span> <span class="variable">dataSource</span> <span class="operator">=</span> <span class="literal">null</span>;</span><br><span class="line">        <span class="keyword">if</span> (<span class="string">&quot;POOLED&quot;</span>.equals(dataSourceType)) &#123;</span><br><span class="line"></span><br><span class="line">        &#125; <span class="keyword">else</span> <span class="keyword">if</span> (<span class="string">&quot;UNPOOLED&quot;</span>.equals(dataSourceType)) &#123;</span><br><span class="line">            dataSource = <span class="keyword">new</span> <span class="title class_">GodUNPOOLEDDataSource</span>(dataSourceMap.get(<span class="string">&quot;driver&quot;</span>), dataSourceMap.get(<span class="string">&quot;url&quot;</span>), dataSourceMap.get(<span class="string">&quot;username&quot;</span>), dataSourceMap.get(<span class="string">&quot;password&quot;</span>));</span><br><span class="line">        &#125; <span class="keyword">else</span> <span class="keyword">if</span> (<span class="string">&quot;JNDI&quot;</span>.equals(dataSourceType)) &#123;</span><br><span class="line"></span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">return</span> dataSource;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h3 id="第十步：在SqlSessionFactory中添加openSession方法"><a href="#第十步：在SqlSessionFactory中添加openSession方法" class="headerlink" title="第十步：在SqlSessionFactory中添加openSession方法"></a>第十步：在SqlSessionFactory中添加openSession方法</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> SqlSession <span class="title function_">openSession</span><span class="params">()</span>&#123;</span><br><span class="line">    transactionManager.openConnection();</span><br><span class="line">    <span class="type">SqlSession</span> <span class="variable">sqlSession</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">SqlSession</span>(transactionManager, mappedStatements);</span><br><span class="line">    <span class="keyword">return</span> sqlSession;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h3 id="第十一步：编写SqlSession类中commit-rollback-close方法"><a href="#第十一步：编写SqlSession类中commit-rollback-close方法" class="headerlink" title="第十一步：编写SqlSession类中commit rollback close方法"></a>第十一步：编写SqlSession类中commit rollback close方法</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> org.god.core;</span><br><span class="line"></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.Map;</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 class="doctag">@author</span> 老杜</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@version</span> 1.0</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@since</span> 1.0</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_">SqlSession</span> &#123;</span><br><span class="line">    <span class="keyword">private</span> TransactionManager transactionManager;</span><br><span class="line">    <span class="keyword">private</span> Map&lt;String, GodMappedStatement&gt; mappedStatements;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">public</span> <span class="title function_">SqlSession</span><span class="params">(TransactionManager transactionManager, Map&lt;String, GodMappedStatement&gt; mappedStatements)</span> &#123;</span><br><span class="line">        <span class="built_in">this</span>.transactionManager = transactionManager;</span><br><span class="line">        <span class="built_in">this</span>.mappedStatements = mappedStatements;</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_">commit</span><span class="params">()</span>&#123;</span><br><span class="line">        <span class="keyword">try</span> &#123;</span><br><span class="line">            transactionManager.getConnection().commit();</span><br><span class="line">        &#125; <span class="keyword">catch</span> (SQLException e) &#123;</span><br><span class="line">            <span class="keyword">throw</span> <span class="keyword">new</span> <span class="title class_">RuntimeException</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">public</span> <span class="keyword">void</span> <span class="title function_">rollback</span><span class="params">()</span>&#123;</span><br><span class="line">        <span class="keyword">try</span> &#123;</span><br><span class="line">            transactionManager.getConnection().rollback();</span><br><span class="line">        &#125; <span class="keyword">catch</span> (SQLException e) &#123;</span><br><span class="line">            <span class="keyword">throw</span> <span class="keyword">new</span> <span class="title class_">RuntimeException</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">public</span> <span class="keyword">void</span> <span class="title function_">close</span><span class="params">()</span>&#123;</span><br><span class="line">        <span class="keyword">try</span> &#123;</span><br><span class="line">            transactionManager.getConnection().close();</span><br><span class="line">        &#125; <span class="keyword">catch</span> (SQLException e) &#123;</span><br><span class="line">            <span class="keyword">throw</span> <span class="keyword">new</span> <span class="title class_">RuntimeException</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="第十二步：编写SqlSession类中的insert方法"><a href="#第十二步：编写SqlSession类中的insert方法" class="headerlink" title="第十二步：编写SqlSession类中的insert方法"></a>第十二步：编写SqlSession类中的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></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">@param</span> sqlId 要执行的sqlId</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@param</span> obj   插入的数据</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@return</span></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">(String sqlId, Object obj)</span> &#123;</span><br><span class="line">    <span class="type">GodMappedStatement</span> <span class="variable">godMappedStatement</span> <span class="operator">=</span> mappedStatements.get(sqlId);</span><br><span class="line">    <span class="type">Connection</span> <span class="variable">connection</span> <span class="operator">=</span> transactionManager.getConnection();</span><br><span class="line">    <span class="comment">// 获取sql语句</span></span><br><span class="line">    <span class="comment">// insert into t_car(id,car_num,brand,guide_price,produce_time,car_type) values(null,#&#123;carNum&#125;,#&#123;brand&#125;,#&#123;guidePrice&#125;,#&#123;produceTime&#125;,#&#123;carType&#125;)</span></span><br><span class="line">    <span class="type">String</span> <span class="variable">godbatisSql</span> <span class="operator">=</span> godMappedStatement.getSql();</span><br><span class="line">    <span class="comment">// insert into t_car(id,car_num,brand,guide_price,produce_time,car_type) values(null,?,?,?,?,?)</span></span><br><span class="line">    <span class="type">String</span> <span class="variable">sql</span> <span class="operator">=</span> godbatisSql.replaceAll(<span class="string">&quot;#\\&#123;[a-zA-Z0-9_\\$]*&#125;&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">    Map&lt;Integer, String&gt; map = <span class="keyword">new</span> <span class="title class_">HashMap</span>&lt;&gt;();</span><br><span class="line">    <span class="type">int</span> <span class="variable">index</span> <span class="operator">=</span> <span class="number">1</span>;</span><br><span class="line">    <span class="keyword">while</span> (godbatisSql.indexOf(<span class="string">&quot;#&quot;</span>) &gt;= <span class="number">0</span>) &#123;</span><br><span class="line">        <span class="type">int</span> <span class="variable">beginIndex</span> <span class="operator">=</span> godbatisSql.indexOf(<span class="string">&quot;#&quot;</span>) + <span class="number">2</span>;</span><br><span class="line">        <span class="type">int</span> <span class="variable">endIndex</span> <span class="operator">=</span> godbatisSql.indexOf(<span class="string">&quot;&#125;&quot;</span>);</span><br><span class="line">        map.put(index++, godbatisSql.substring(beginIndex, endIndex).trim());</span><br><span class="line">        godbatisSql = godbatisSql.substring(endIndex + <span class="number">1</span>);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">final</span> PreparedStatement ps;</span><br><span class="line">    <span class="keyword">try</span> &#123;</span><br><span class="line">        ps = connection.prepareStatement(sql);</span><br><span class="line"></span><br><span class="line">        <span class="comment">// 给?赋值</span></span><br><span class="line">        map.forEach((k, v) -&gt; &#123;</span><br><span class="line">            <span class="keyword">try</span> &#123;</span><br><span class="line">                <span class="comment">// 获取java实体类的get方法名</span></span><br><span class="line">                <span class="type">String</span> <span class="variable">getMethodName</span> <span class="operator">=</span> <span class="string">&quot;get&quot;</span> + v.toUpperCase().charAt(<span class="number">0</span>) + v.substring(<span class="number">1</span>);</span><br><span class="line">                <span class="type">Method</span> <span class="variable">getMethod</span> <span class="operator">=</span> obj.getClass().getDeclaredMethod(getMethodName);</span><br><span class="line">                ps.setString(k, getMethod.invoke(obj).toString());</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>(e);</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;);</span><br><span class="line">        <span class="type">int</span> <span class="variable">count</span> <span class="operator">=</span> ps.executeUpdate();</span><br><span class="line">        ps.close();</span><br><span class="line">        <span class="keyword">return</span> count;</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>(e);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h3 id="第十三步：编写SqlSession类中的selectOne方法"><a href="#第十三步：编写SqlSession类中的selectOne方法" class="headerlink" title="第十三步：编写SqlSession类中的selectOne方法"></a>第十三步：编写SqlSession类中的selectOne方法</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="comment">/**</span></span><br><span class="line"><span class="comment"> * 查询一个对象</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@param</span> sqlId</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@param</span> parameterObj</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@return</span></span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="keyword">public</span> Object <span class="title function_">selectOne</span><span class="params">(String sqlId, Object parameterObj)</span>&#123;</span><br><span class="line">    <span class="type">GodMappedStatement</span> <span class="variable">godMappedStatement</span> <span class="operator">=</span> mappedStatements.get(sqlId);</span><br><span class="line">    <span class="type">Connection</span> <span class="variable">connection</span> <span class="operator">=</span> transactionManager.getConnection();</span><br><span class="line">    <span class="comment">// 获取sql语句</span></span><br><span class="line">    <span class="type">String</span> <span class="variable">godbatisSql</span> <span class="operator">=</span> godMappedStatement.getSql();</span><br><span class="line">    <span class="type">String</span> <span class="variable">sql</span> <span class="operator">=</span> godbatisSql.replaceAll(<span class="string">&quot;#\\&#123;[a-zA-Z0-9_\\$]*&#125;&quot;</span>, <span class="string">&quot;?&quot;</span>);</span><br><span class="line">    <span class="comment">// 执行sql</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">Object</span> <span class="variable">obj</span> <span class="operator">=</span> <span class="literal">null</span>;</span><br><span class="line">    <span class="keyword">try</span> &#123;</span><br><span class="line">        ps = connection.prepareStatement(sql);</span><br><span class="line">        ps.setString(<span class="number">1</span>, parameterObj.toString());</span><br><span class="line">        rs = ps.executeQuery();</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">            <span class="type">String</span> <span class="variable">resultType</span> <span class="operator">=</span> godMappedStatement.getResultType();</span><br><span class="line">            Class&lt;?&gt; aClass = Class.forName(resultType);</span><br><span class="line">            Constructor&lt;?&gt; con = aClass.getDeclaredConstructor();</span><br><span class="line">            obj = con.newInstance();</span><br><span class="line">            <span class="comment">// 给对象obj属性赋值</span></span><br><span class="line">            <span class="type">ResultSetMetaData</span> <span class="variable">rsmd</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> rsmd.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">                <span class="type">String</span> <span class="variable">columnName</span> <span class="operator">=</span> rsmd.getColumnName(i);</span><br><span class="line">                <span class="type">String</span> <span class="variable">setMethodName</span> <span class="operator">=</span> <span class="string">&quot;set&quot;</span> + columnName.toUpperCase().charAt(<span class="number">0</span>) + columnName.substring(<span class="number">1</span>);</span><br><span class="line">                <span class="type">Method</span> <span class="variable">setMethod</span> <span class="operator">=</span> aClass.getDeclaredMethod(setMethodName, aClass.getDeclaredField(columnName).getType());</span><br><span class="line">                setMethod.invoke(obj, rs.getString(columnName));</span><br><span class="line">            &#125;</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">        <span class="keyword">throw</span> <span class="keyword">new</span> <span class="title class_">RuntimeException</span>(e);</span><br><span class="line">    &#125; <span class="keyword">finally</span> &#123;</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">                <span class="keyword">throw</span> <span class="keyword">new</span> <span class="title class_">RuntimeException</span>(e);</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;</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">            <span class="keyword">throw</span> <span class="keyword">new</span> <span class="title class_">RuntimeException</span>(e);</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">return</span> obj;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h2 id="5-3-GodBatis使用Maven打包"><a href="#5-3-GodBatis使用Maven打包" class="headerlink" title="5.3 GodBatis使用Maven打包"></a>5.3 GodBatis使用Maven打包</h2><p><img src="瑞吉外卖项目实战说明/1660206374772-d21754cb-0c5f-4a14-921e-d083b4a791c3.png" alt="img"></p><p>查看本地仓库中是否已经有jar包：</p><p><img src="瑞吉外卖项目实战说明/1660206446577-20e8dd84-6b05-48c8-a693-cb0dadfb7979.png" alt="img"></p><h2 id="5-4-使用GodBatis"><a href="#5-4-使用GodBatis" class="headerlink" title="5.4 使用GodBatis"></a>5.4 使用GodBatis</h2><p>使用GodBatis就和使用MyBatis是一样的。</p><p>第一步：准备数据库表t_user</p><p><img src="瑞吉外卖项目实战说明/1660206701289-4ed4661b-7c85-4157-b09c-3a7fe5207399.png" alt="img"></p><p>第二步：创建模块，普通的Java Maven模块：godbatis-test</p><p>第三步：引入依赖</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><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">&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 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.powernode<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>godbatis-test<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">dependencies</span>&gt;</span></span><br><span class="line">    <span class="comment">&lt;!--godbatis依赖--&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.god<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>godbatis<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 class="tag">&lt;/<span class="name">dependency</span>&gt;</span></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>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-java<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.30<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="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 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">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">properties</span>&gt;</span></span><br><span class="line">  </span><br><span class="line"><span class="tag">&lt;/<span class="name">project</span>&gt;</span></span><br></pre></td></tr></table></figure><p>第四步：编写pojo类</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></pre></td><td class="code"><pre><span class="line"><span class="keyword">package</span> com.powernode.godbatis.pojo;</span><br><span class="line"></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> String id;</span><br><span class="line">    <span class="keyword">private</span> String name;</span><br><span class="line">    <span class="keyword">private</span> String email;</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">@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=&#x27;&quot;</span> + id + <span class="string">&#x27;\&#x27;&#x27;</span> +</span><br><span class="line">                <span class="string">&quot;, name=&#x27;&quot;</span> + name + <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;, address=&#x27;&quot;</span> + address + <span class="string">&#x27;\&#x27;&#x27;</span> +</span><br><span class="line">                <span class="string">&#x27;&#125;&#x27;</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_">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">(String 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_">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> 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> String <span class="title function_">getAddress</span><span class="params">()</span> &#123;</span><br><span class="line">        <span class="keyword">return</span> address;</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_">setAddress</span><span class="params">(String address)</span> &#123;</span><br><span class="line">        <span class="built_in">this</span>.address = address;</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">()</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 id, String name, String email, String address)</span> &#123;</span><br><span class="line">        <span class="built_in">this</span>.id = id;</span><br><span class="line">        <span class="built_in">this</span>.name = name;</span><br><span class="line">        <span class="built_in">this</span>.email = email;</span><br><span class="line">        <span class="built_in">this</span>.address = address;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>第五步：编写核心配置文件：godbatis-config.xml</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></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><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">environments</span> <span class="attr">default</span>=<span class="string">&quot;dev&quot;</span>&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;dev&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;UNPOOLED&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;com.mysql.cj.jdbc.Driver&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;jdbc:mysql://localhost:3306/powernode&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;root&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;root&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">mappers</span>&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;UserMapper.xml&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 class="tag">&lt;/<span class="name">environments</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;/<span class="name">configuration</span>&gt;</span></span><br></pre></td></tr></table></figure><p>第六步：编写sql映射文件：UserMapper.xml</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="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><br><span class="line"><span class="tag">&lt;<span class="name">mapper</span> <span class="attr">namespace</span>=<span class="string">&quot;user&quot;</span>&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;insertUser&quot;</span>&gt;</span></span><br><span class="line">        insert into t_user(id,name,email,address) values(#&#123;id&#125;,#&#123;name&#125;,#&#123;email&#125;,#&#123;address&#125;)</span><br><span class="line">    <span class="tag">&lt;/<span class="name">insert</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;selectUserById&quot;</span> <span class="attr">resultType</span>=<span class="string">&quot;com.powernode.godbatis.pojo.User&quot;</span>&gt;</span></span><br><span class="line">        select * from t_user 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 class="tag">&lt;/<span class="name">mapper</span>&gt;</span></span><br></pre></td></tr></table></figure><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="keyword">package</span> com.powernode.godbatis.test;</span><br><span class="line"></span><br><span class="line"><span class="keyword">import</span> com.powernode.godbatis.pojo.User;</span><br><span class="line"><span class="keyword">import</span> org.god.core.Resources;</span><br><span class="line"><span class="keyword">import</span> org.god.core.SqlSession;</span><br><span class="line"><span class="keyword">import</span> org.god.core.SqlSessionFactory;</span><br><span class="line"><span class="keyword">import</span> org.god.core.SqlSessionFactoryBuilder;</span><br><span class="line"><span class="keyword">import</span> org.junit.Test;</span><br><span class="line"></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">GodBatisTest</span> &#123;</span><br><span class="line">    </span><br><span class="line">    <span class="meta">@Test</span></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">testInsertUser</span><span class="params">()</span> <span class="keyword">throws</span> Exception&#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 class="string">&quot;1&quot;</span>, <span class="string">&quot;zhangsan&quot;</span>, <span class="string">&quot;zhangsan@1234.com&quot;</span>, <span class="string">&quot;北京大兴区&quot;</span>);</span><br><span class="line">        <span class="type">SqlSessionFactoryBuilder</span> <span class="variable">sqlSessionFactoryBuilder</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">SqlSessionFactoryBuilder</span>();</span><br><span class="line">        <span class="type">SqlSessionFactory</span> <span class="variable">sqlSessionFactory</span> <span class="operator">=</span> sqlSessionFactoryBuilder.build(Resources.getResourcesAsStream(<span class="string">&quot;godbatis-config.xml&quot;</span>));</span><br><span class="line">        <span class="type">SqlSession</span> <span class="variable">sqlSession</span> <span class="operator">=</span> sqlSessionFactory.openSession();</span><br><span class="line">        <span class="type">int</span> <span class="variable">count</span> <span class="operator">=</span> sqlSession.insert(<span class="string">&quot;user.insertUser&quot;</span>, user);</span><br><span class="line">        System.out.println(<span class="string">&quot;插入了几条记录：&quot;</span> + count);</span><br><span class="line">        sqlSession.commit();</span><br><span class="line">        sqlSession.close();</span><br><span class="line">    &#125;</span><br><span class="line">    </span><br><span class="line">    <span class="meta">@Test</span></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">testSelectUserById</span><span class="params">()</span> <span class="keyword">throws</span> Exception&#123;</span><br><span class="line">        <span class="type">SqlSessionFactoryBuilder</span> <span class="variable">sqlSessionFactoryBuilder</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">SqlSessionFactoryBuilder</span>();</span><br><span class="line">        <span class="type">SqlSessionFactory</span> <span class="variable">sqlSessionFactory</span> <span class="operator">=</span> sqlSessionFactoryBuilder.build(Resources.getResourcesAsStream(<span class="string">&quot;godbatis-config.xml&quot;</span>));</span><br><span class="line">        <span class="type">SqlSession</span> <span class="variable">sqlSession</span> <span class="operator">=</span> sqlSessionFactory.openSession();</span><br><span class="line">        <span class="type">Object</span> <span class="variable">user</span> <span class="operator">=</span> sqlSession.selectOne(<span class="string">&quot;user.selectUserById&quot;</span>, <span class="string">&quot;1&quot;</span>);</span><br><span class="line">        System.out.println(user);</span><br><span class="line">        sqlSession.close();</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>第八步：运行结果</p><p><img src="瑞吉外卖项目实战说明/1660209177038-ecf701a2-1211-4f0c-b1f6-e2e16c1617d5.png" alt="img"></p><p><img src="瑞吉外卖项目实战说明/1660209186576-54493410-47b7-4496-8007-471473cc6fc6.png" alt="img"></p><p><img src="瑞吉外卖项目实战说明/1660209193124-908993c5-90d6-4426-a598-7aeac2a6b791.png" alt="img"></p><h2 id="5-5-总结MyBatis框架的重要实现原理"><a href="#5-5-总结MyBatis框架的重要实现原理" class="headerlink" title="5.5 总结MyBatis框架的重要实现原理"></a>5.5 总结MyBatis框架的重要实现原理</h2><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="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><br><span class="line"><span class="tag">&lt;<span class="name">mapper</span> <span class="attr">namespace</span>=<span class="string">&quot;user&quot;</span>&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;insertUser&quot;</span>&gt;</span></span><br><span class="line">    insert into t_user(id,name,email,address) values(#&#123;id&#125;,#&#123;name&#125;,#&#123;email&#125;,#&#123;address&#125;)</span><br><span class="line">  <span class="tag">&lt;/<span class="name">insert</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;selectUserById&quot;</span> <span class="attr">resultType</span>=<span class="string">&quot;com.powernode.godbatis.pojo.User&quot;</span>&gt;</span></span><br><span class="line">    select id,name,email,address from t_user 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 class="tag">&lt;/<span class="name">mapper</span>&gt;</span></span><br></pre></td></tr></table></figure><p>思考两个问题：</p><ul><li>为什么insert语句中 #{} 里填写的必须是属性名？</li><li>为什么select语句查询结果列名要属性名一致？</li></ul><p><img src="瑞吉外卖项目实战说明/1659578619308-ceb8077a-94a7-4f64-b41d-e54b3c14e7fb.png" alt="img"></p><h1 id="在WEB中应用MyBatis（使用MVC架构模式）"><a href="#在WEB中应用MyBatis（使用MVC架构模式）" class="headerlink" title="在WEB中应用MyBatis（使用MVC架构模式）"></a>在WEB中应用MyBatis（使用MVC架构模式）</h1><div class="note info no-icon flat"><p><strong>目标：</strong></p><ul><li>掌握mybatis在web应用中怎么用</li><li>mybatis三大对象的作用域和生命周期</li><li>ThreadLocal原理及使用</li><li>巩固MVC架构模式</li><li>为学习MyBatis的接口代理机制做准备</li></ul></div><ul><li><p><strong>实现功能：</strong>银行账户转账</p></li><li><p><strong>使用技术：</strong>HTML + Servlet + MyBatis</p></li><li><p><strong>WEB应用的名称：</strong>bank</p></li></ul><h2 id="需求描述"><a href="#需求描述" class="headerlink" title="需求描述"></a>需求描述</h2><p>实现将钱从A账户转到B账户。<br><div class="img-wrap"><div class="img-bg"><img class="img" src="https://raw.githubusercontent.com/silvan2077/markdown_pic/main/Picgo/20251019000348.png"/></div></div></p><h2 id="数据库表的设计和准备数据"><a href="#数据库表的设计和准备数据" class="headerlink" title="数据库表的设计和准备数据"></a>数据库表的设计和准备数据</h2><div class="img-wrap"><div class="img-bg"><img class="img" src="https://raw.githubusercontent.com/silvan2077/markdown_pic/main/Picgo/20251019000506.png"/></div></div><div class="img-wrap"><div class="img-bg"><img class="img" src="https://raw.githubusercontent.com/silvan2077/markdown_pic/main/Picgo/20251019000524.png"/></div></div><h2 id="实现步骤"><a href="#实现步骤" class="headerlink" title="实现步骤"></a>实现步骤</h2><h3 id="第一步：环境搭建"><a href="#第一步：环境搭建" class="headerlink" title="第一步：环境搭建"></a>第一步：环境搭建</h3><ul><li>创建WEB应用</li></ul><div class="img-wrap"><div class="img-bg"><img class="img" src="https://raw.githubusercontent.com/silvan2077/markdown_pic/main/Picgo/20251019000721.png"/></div></div><ul><li>IDEA配置Tomcat，这里Tomcat使用10+版本。并部署应用到tomcat。</li></ul><div class="img-wrap"><div class="img-bg"><img class="img" src="https://raw.githubusercontent.com/silvan2077/markdown_pic/main/Picgo/20251019000813.png"/></div></div><ul><li><p>默认创建的maven web应用没有java和resources目录，自己手动加上即可。</p><div class="img-wrap"><div class="img-bg"><img class="img" src="https://raw.githubusercontent.com/silvan2077/markdown_pic/main/Picgo/20251019000941.png"/></div></div></li><li><p>web.xml文件的版本较低，可以从tomcat10的样例文件中复制，然后修改</p></li></ul><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></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">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_5_0.xsd&quot;</span></span></span><br><span class="line"><span class="tag">         <span class="attr">version</span>=<span class="string">&quot;5.0&quot;</span></span></span><br><span class="line"><span class="tag">         <span class="attr">metadata-complete</span>=<span class="string">&quot;false&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">metadata-complete=&quot;false&quot;&gt; 表示支持配置文件和注解</span></span><br><span class="line"><span class="comment">etadata-complete=&quot;true&quot;&gt; 表示只支持配置文件</span></span><br><span class="line"><span class="comment">&lt;--&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><ul><li>删除index.jsp文件，因为该项目不使用JSP。只使用html。</li><li>确定pom.xml文件中的打包方式是war包。</li><li>引入相关依赖<ul><li>编译器版本修改为17</li><li>引入的依赖包括：mybatis，mysql驱动，junit，logback，servlet。</li></ul></li></ul><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></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><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 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 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.powernode<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-004-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.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">name</span>&gt;</span>mybatis-004-web<span class="tag">&lt;/<span class="name">name</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">url</span>&gt;</span>http://localhost:8080/bank<span class="tag">&lt;/<span class="name">url</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">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">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">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>3.5.10<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="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>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-java<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.30<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="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 class="comment">&lt;!--logback依赖--&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>ch.qos.logback<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>logback-classic<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.11<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="comment">&lt;!--servlet依赖--&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>5.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="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>mybatis-004-web<span class="tag">&lt;/<span class="name">finalName</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">pluginManagement</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">artifactId</span>&gt;</span>maven-clean-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.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">plugin</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">artifactId</span>&gt;</span>maven-resources-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.2<span class="tag">&lt;/<span class="name">version</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">plugin</span>&gt;</span></span><br><span class="line">                    <span class="tag">&lt;<span class="name">artifactId</span>&gt;</span>maven-compiler-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.8.0<span class="tag">&lt;/<span class="name">version</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">plugin</span>&gt;</span></span><br><span class="line">                    <span class="tag">&lt;<span class="name">artifactId</span>&gt;</span>maven-surefire-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>2.22.1<span class="tag">&lt;/<span class="name">version</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">plugin</span>&gt;</span></span><br><span class="line">                    <span class="tag">&lt;<span class="name">artifactId</span>&gt;</span>maven-war-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.2.2<span class="tag">&lt;/<span class="name">version</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">plugin</span>&gt;</span></span><br><span class="line">                    <span class="tag">&lt;<span class="name">artifactId</span>&gt;</span>maven-install-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>2.5.2<span class="tag">&lt;/<span class="name">version</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">plugin</span>&gt;</span></span><br><span class="line">                    <span class="tag">&lt;<span class="name">artifactId</span>&gt;</span>maven-deploy-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>2.8.2<span class="tag">&lt;/<span class="name">version</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">pluginManagement</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><ul><li>引入相关配置文件，放到resources目录下（全部放到类的根路径下）<ul><li>mybatis-config.xml</li><li>AccountMapper.xml</li><li>logback.xml</li><li>jdbc.properties</li></ul></li></ul><div class="tabs" id="web环境搭建"><ul class="nav-tabs"><li class="tab active"><button type="button" data-href="#web环境搭建-1">mybatis-config.xml</button></li><li class="tab"><button type="button" data-href="#web环境搭建-2">AccountMapper.xml</button></li><li class="tab"><button type="button" data-href="#web环境搭建-3">logback.xml</button></li><li class="tab"><button type="button" data-href="#web环境搭建-4">jdbc.properties</button></li></ul><div class="tab-contents"><div class="tab-item-content active" id="web环境搭建-1"><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></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></span><br><span class="line"><span class="meta">        <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="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="tag">&lt;<span class="name">environments</span> <span class="attr">default</span>=<span class="string">&quot;dev&quot;</span>&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;dev&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 class="tag">&lt;<span class="name">mappers</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">mapper</span> <span class="attr">resource</span>=<span class="string">&quot;AccountMapper.xml&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 class="tag">&lt;/<span class="name">configuration</span>&gt;</span></span><br></pre></td></tr></table></figure><button type="button" class="tab-to-top" aria-label="scroll to top"><i class="fas fa-arrow-up"></i></button></div><div class="tab-item-content" id="web环境搭建-2"><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></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></span><br><span class="line"><span class="meta">        <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="tag">&lt;<span class="name">mapper</span> <span class="attr">namespace</span>=<span class="string">&quot;account&quot;</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><button type="button" class="tab-to-top" aria-label="scroll to top"><i class="fas fa-arrow-up"></i></button></div><div class="tab-item-content" id="web环境搭建-3"><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="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><br><span class="line"><span class="tag">&lt;<span class="name">configuration</span> <span class="attr">debug</span>=<span class="string">&quot;false&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">appender</span> <span class="attr">name</span>=<span class="string">&quot;STDOUT&quot;</span> <span class="attr">class</span>=<span class="string">&quot;ch.qos.logback.core.ConsoleAppender&quot;</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">encoder</span> <span class="attr">class</span>=<span class="string">&quot;ch.qos.logback.classic.encoder.PatternLayoutEncoder&quot;</span>&gt;</span></span><br><span class="line">      <span class="comment">&lt;!--格式化输出：%d表示日期，%thread表示线程名，%-5level：级别从左显示5个字符宽度%msg：日志消息，%n是换行符--&gt;</span></span><br><span class="line">      <span class="tag">&lt;<span class="name">pattern</span>&gt;</span>%d&#123;yyyy-MM-dd HH:mm:ss.SSS&#125; [%thread] %-5level %logger&#123;50&#125; - %msg%n<span class="tag">&lt;/<span class="name">pattern</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;/<span class="name">encoder</span>&gt;</span></span><br><span class="line">  <span class="tag">&lt;/<span class="name">appender</span>&gt;</span></span><br></pre></td></tr></table></figure><button type="button" class="tab-to-top" aria-label="scroll to top"><i class="fas fa-arrow-up"></i></button></div><div class="tab-item-content" id="web环境搭建-4"><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></pre></td><td class="code"><pre><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/powernode</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">root</span></span><br></pre></td></tr></table></figure><button type="button" class="tab-to-top" aria-label="scroll to top"><i class="fas fa-arrow-up"></i></button></div></div></div><h3 id="第二步：编写前端页面index-html"><a href="#第二步：编写前端页面index-html" class="headerlink" title="第二步：编写前端页面index.html"></a>第二步：编写前端页面index.html</h3><div class="img-wrap"><div class="img-bg"><img class="img" src="https://raw.githubusercontent.com/silvan2077/markdown_pic/main/Picgo/20251019141945.png"/></div></div><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></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;en&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">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="comment">&lt;!--/bank是应用的根，部署web应用到tomcat的时候一定要注意这个名字--&gt;</span></span><br><span class="line"><span class="tag">&lt;<span class="name">form</span> <span class="attr">action</span>=<span class="string">&quot;/bank/transfer&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">input</span> <span class="attr">type</span>=<span class="string">&quot;text&quot;</span> <span class="attr">name</span>=<span class="string">&quot;fromActno&quot;</span>/&gt;</span><span class="tag">&lt;<span class="name">br</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">name</span>=<span class="string">&quot;toActno&quot;</span>/&gt;</span><span class="tag">&lt;<span class="name">br</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">name</span>=<span class="string">&quot;money&quot;</span>/&gt;</span><span class="tag">&lt;<span class="name">br</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;submit&quot;</span> <span class="attr">value</span>=<span class="string">&quot;转账&quot;</span>/&gt;</span></span><br><span class="line"><span class="tag">&lt;/<span class="name">form</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="第三步：创建项目的包结构"><a href="#第三步：创建项目的包结构" class="headerlink" title="第三步：创建项目的包结构"></a>第三步：创建项目的包结构</h3><p>创建包结构：</p><ul><li>com.powernode.bank.pojo</li><li>com.powernode.bank.service</li><li>com.powernode.bank.service.impl</li><li>com.powernode.bank.dao</li><li>com.powernode.bank.dao.impl</li><li>com.powernode.bank.web.controller</li><li>com.powernode.bank.exception</li><li>com.powernode.bank.utils：<strong>将之前编写的SqlSessionUtil工具类拷贝到该包下。</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></pre></td><td class="code"><pre><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_">Account</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 actno;</span><br><span class="line">    <span class="keyword">private</span> Double balance;</span><br><span class="line">    </span><br><span class="line">    <span class="keyword">public</span> <span class="title function_">Account</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_">Account</span><span class="params">(Long id, String actno, Double balance)</span> &#123;</span><br><span class="line">        <span class="built_in">this</span>.id = id;</span><br><span class="line">        <span class="built_in">this</span>.actno = actno;</span><br><span class="line">        <span class="built_in">this</span>.balance = balance;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h3 id="第五步：编写DAO层接口和实现类"><a href="#第五步：编写DAO层接口和实现类" class="headerlink" title="第五步：编写DAO层接口和实现类"></a>第五步：编写DAO层接口和实现类</h3><p>分析dao中至少要提供几个方法，才能完成转账：</p><ul><li>转账前需要查询余额是否充足：<code>selectByActno</code></li><li>转账时要更新账户：<code>update</code></li></ul><div class="tabs" id="编写dao层接口和实现类"><ul class="nav-tabs"><li class="tab active"><button type="button" data-href="#编写dao层接口和实现类-1">AccountDao</button></li><li class="tab"><button type="button" data-href="#编写dao层接口和实现类-2">AccountDaoImpl</button></li></ul><div class="tab-contents"><div class="tab-item-content active" id="编写dao层接口和实现类-1"><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><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><br><span class="line"><span class="keyword">public</span> <span class="keyword">interface</span> <span class="title class_">AccountDao</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 class="doctag">@param</span> actno 账号</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@return</span> 账户信息</span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    Account <span class="title function_">selectByActno</span><span class="params">(String actno)</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 class="doctag">@param</span> act 账户信息</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@return</span> 1表示更新成功，其他值表示失败</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">(Account act)</span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><button type="button" class="tab-to-top" aria-label="scroll to top"><i class="fas fa-arrow-up"></i></button></div><div class="tab-item-content" id="编写dao层接口和实现类-2"><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><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">public</span> <span class="keyword">class</span> <span class="title class_">AccountDaoImpl</span> <span class="keyword">implements</span> <span class="title class_">AccountDao</span> &#123;</span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="keyword">public</span> Account <span class="title function_">selectByActno</span><span class="params">(String actno)</span> &#123;</span><br><span class="line">        <span class="type">SqlSession</span> <span class="variable">sqlSession</span> <span class="operator">=</span> SqlSessionUtil.openSession();</span><br><span class="line">        <span class="type">Account</span> <span class="variable">act</span> <span class="operator">=</span> (Account)sqlSession.selectOne(<span class="string">&quot;selectByActno&quot;</span>, actno);</span><br><span class="line">        sqlSession.close();</span><br><span class="line">        <span class="keyword">return</span> act;</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_">update</span><span class="params">(Account act)</span> &#123;</span><br><span class="line">        <span class="type">SqlSession</span> <span class="variable">sqlSession</span> <span class="operator">=</span> SqlSessionUtil.openSession();</span><br><span class="line">        <span class="type">int</span> <span class="variable">count</span> <span class="operator">=</span> sqlSession.update(<span class="string">&quot;update&quot;</span>, act);</span><br><span class="line">        sqlSession.commit();</span><br><span class="line">        sqlSession.close();</span><br><span class="line">        <span class="keyword">return</span> count;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><button type="button" class="tab-to-top" aria-label="scroll to top"><i class="fas fa-arrow-up"></i></button></div></div></div><h3 id="第六步：编写SQL映射文件"><a href="#第六步：编写SQL映射文件" class="headerlink" title="第六步：编写SQL映射文件"></a>第六步：编写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></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></span><br><span class="line"><span class="meta">        <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="tag">&lt;<span class="name">mapper</span> <span class="attr">namespace</span>=<span class="string">&quot;account&quot;</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;selectByActno&quot;</span> <span class="attr">resultType</span>=<span class="string">&quot;com.powernode.bank.pojo.Account&quot;</span>&gt;</span></span><br><span class="line">        select * from t_act where actno = #&#123;actno&#125;</span><br><span class="line">    <span class="tag">&lt;/<span class="name">select</span>&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>&gt;</span></span><br><span class="line">        update t_act set balance = #&#123;balance&#125; where actno = #&#123;actno&#125;</span><br><span class="line">    <span class="tag">&lt;/<span class="name">update</span>&gt;</span></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="第七步：编写Service层接口和实现类"><a href="#第七步：编写Service层接口和实现类" class="headerlink" title="第七步：编写Service层接口和实现类"></a>第七步：编写Service层接口和实现类</h3><div class="tabs" id="编写service层接口和实现类"><ul class="nav-tabs"><li class="tab active"><button type="button" data-href="#编写service层接口和实现类-1">AccountService</button></li><li class="tab"><button type="button" data-href="#编写service层接口和实现类-2">AccountServiceImpl</button></li><li class="tab"><button type="button" data-href="#编写service层接口和实现类-3">余额不足异常</button></li><li class="tab"><button type="button" data-href="#编写service层接口和实现类-4">应用异常</button></li></ul><div class="tab-contents"><div class="tab-item-content active" id="编写service层接口和实现类-1"><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><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 class="doctag">@author</span> 老杜</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@version</span> 1.0</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@since</span> 1.0</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_">AccountService</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 class="doctag">@param</span> fromActno 转出账户</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@param</span> toActno 转入账户</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@param</span> money 转账金额</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@throws</span> MoneyNotEnoughException 余额不足异常</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@throws</span> AppException App发生异常</span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    <span class="keyword">void</span> <span class="title function_">transfer</span><span class="params">(String fromActno, String toActno, <span class="type">double</span> money)</span> <span class="keyword">throws</span> MoneyNotEnoughException, AppException;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><button type="button" class="tab-to-top" aria-label="scroll to top"><i class="fas fa-arrow-up"></i></button></div><div class="tab-item-content" id="编写service层接口和实现类-2"><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><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_">AccountServiceImpl</span> <span class="keyword">implements</span> <span class="title class_">AccountService</span> &#123;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">private</span> <span class="type">AccountDao</span> <span class="variable">accountDao</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">AccountDaoImpl</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_">transfer</span><span class="params">(String fromActno, String toActno, <span class="type">double</span> money)</span> <span class="keyword">throws</span> MoneyNotEnoughException, AppException &#123;</span><br><span class="line">        <span class="comment">// 查询转出账户的余额</span></span><br><span class="line">        <span class="type">Account</span> <span class="variable">fromAct</span> <span class="operator">=</span> accountDao.selectByActno(fromActno);</span><br><span class="line">        <span class="keyword">if</span> (fromAct.getBalance() &lt; money) &#123;</span><br><span class="line">            <span class="keyword">throw</span> <span class="keyword">new</span> <span class="title class_">MoneyNotEnoughException</span>(<span class="string">&quot;对不起，您的余额不足。&quot;</span>);</span><br><span class="line">        &#125;</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="comment">// 修改账户余额</span></span><br><span class="line">            <span class="type">Account</span> <span class="variable">toAct</span> <span class="operator">=</span> accountDao.selectByActno(toActno);</span><br><span class="line">            fromAct.setBalance(fromAct.getBalance() - money);</span><br><span class="line">            toAct.setBalance(toAct.getBalance() + money);</span><br><span class="line">            <span class="comment">// 更新数据库</span></span><br><span class="line">            accountDao.update(fromAct);</span><br><span class="line">            accountDao.update(toAct);</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_">AppException</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></pre></td></tr></table></figure><button type="button" class="tab-to-top" aria-label="scroll to top"><i class="fas fa-arrow-up"></i></button></div><div class="tab-item-content" id="编写service层接口和实现类-3"><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><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">package</span> com.powernode.bank.exception;</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 class="doctag">@author</span> 老杜</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@version</span> 1.0</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@since</span> 1.0</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_">MoneyNotEnoughException</span> <span class="keyword">extends</span> <span class="title class_">Exception</span>&#123;</span><br><span class="line">    <span class="keyword">public</span> <span class="title function_">MoneyNotEnoughException</span><span class="params">()</span>&#123;&#125;</span><br><span class="line">    <span class="keyword">public</span> <span class="title function_">MoneyNotEnoughException</span><span class="params">(String msg)</span>&#123; <span class="built_in">super</span>(msg); &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><button type="button" class="tab-to-top" aria-label="scroll to top"><i class="fas fa-arrow-up"></i></button></div><div class="tab-item-content" id="编写service层接口和实现类-4"><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><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 class="doctag">@author</span> 老杜</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@version</span> 1.0</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@since</span> 1.0</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_">AppException</span> <span class="keyword">extends</span> <span class="title class_">Exception</span>&#123;</span><br><span class="line">    <span class="keyword">public</span> <span class="title function_">AppException</span><span class="params">()</span>&#123;&#125;</span><br><span class="line">    <span class="keyword">public</span> <span class="title function_">AppException</span><span class="params">(String msg)</span>&#123; <span class="built_in">super</span>(msg); &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><button type="button" class="tab-to-top" aria-label="scroll to top"><i class="fas fa-arrow-up"></i></button></div></div></div><h3 id="第八步：编写AccountController"><a href="#第八步：编写AccountController" class="headerlink" title="第八步：编写AccountController"></a>第八步：编写AccountController</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 class="doctag">@author</span> 老杜</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@version</span> 1.0</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@since</span> 1.0</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="meta">@WebServlet(&quot;/transfer&quot;)</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">AccountController</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">AccountService</span> <span class="variable">accountService</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">AccountServiceImpl</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_">doPost</span><span class="params">(HttpServletRequest request, HttpServletResponse response)</span></span><br><span class="line">            <span class="keyword">throws</span> ServletException, IOException &#123;</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="type">PrintWriter</span> <span class="variable">out</span> <span class="operator">=</span> response.getWriter();</span><br><span class="line">        <span class="comment">// 获取账户信息</span></span><br><span class="line">        <span class="type">String</span> <span class="variable">fromActno</span> <span class="operator">=</span> request.getParameter(<span class="string">&quot;fromActno&quot;</span>);</span><br><span class="line">        <span class="type">String</span> <span class="variable">toActno</span> <span class="operator">=</span> request.getParameter(<span class="string">&quot;toActno&quot;</span>);</span><br><span class="line">        <span class="type">double</span> <span class="variable">money</span> <span class="operator">=</span> Integer.parseInt(request.getParameter(<span class="string">&quot;money&quot;</span>));</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">            accountService.transfer(fromActno, toActno, money);</span><br><span class="line">            out.print(<span class="string">&quot;&lt;h1&gt;转账成功！！！&lt;/h1&gt;&quot;</span>);</span><br><span class="line">        &#125; <span class="keyword">catch</span> (MoneyNotEnoughException e) &#123;</span><br><span class="line">            out.print(e.getMessage());</span><br><span class="line">        &#125; <span class="keyword">catch</span> (AppException e) &#123;</span><br><span class="line">            out.print(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><p>启动服务器，打开浏览器，输入地址：<a href="http://localhost:8080/bank">http://localhost:8080/bank</a>，测试：</p><div class="img-wrap"><div class="img-bg"><img class="img" src="https://raw.githubusercontent.com/silvan2077/markdown_pic/main/Picgo/20251019145127.png"/></div></div><h2 id="MyBatis对象作用域以及事务问题"><a href="#MyBatis对象作用域以及事务问题" class="headerlink" title="MyBatis对象作用域以及事务问题"></a>MyBatis对象作用域以及事务问题</h2><h3 id="MyBatis核心对象的作用域"><a href="#MyBatis核心对象的作用域" class="headerlink" title="MyBatis核心对象的作用域"></a>MyBatis核心对象的作用域</h3><h4 id="SqlSessionFactoryBuilder"><a href="#SqlSessionFactoryBuilder" class="headerlink" title="SqlSessionFactoryBuilder"></a>SqlSessionFactoryBuilder</h4><p>这个类可以被实例化、使用和丢弃，一旦创建了 SqlSessionFactory，就不再需要它了。 因此 SqlSessionFactoryBuilder 实例的最佳作用域是<code>方法作用域</code>（也就是局部方法变量），该类不建议长期复用，应当在创建完成后立即丢弃</p><h4 id="SqlSessionFactory"><a href="#SqlSessionFactory" class="headerlink" title="SqlSessionFactory"></a>SqlSessionFactory</h4><p>该类是应用程序中唯一的<code>SqlSession</code>生产者，合理的用法是在整个应用程序运行期间只创建一次，因为创建它的过程代价昂贵（需要解析全部配置和映射文件）。因此，<code>SqlSessionFactory</code> 实例的最佳作用域是<code>应用作用域</code>（Application Scope），也就是单例。 它是线程安全的，所有线程都可以安全地共享这同一个实例，并用它来获取各自独立的 SqlSession。</p><h4 id="SqlSession"><a href="#SqlSession" class="headerlink" title="SqlSession"></a>SqlSession</h4><p>这个类代表了与数据库的一次会话，类似于 <code>JDBC</code> 中的 Connection 对象，它负责执行SQL语句、管理事务，并维护着会话级别的一级缓存。 <code>SqlSession</code>是非线程安全的，它绝不能被多个线程共享。 因此，SqlSession 实例的最佳作用域是<code>请求作用域</code>或<code>方法作用域</code>。 你应该在开始一个数据库操作或事务时打开它，并确保在操作结束时立即关闭它（即使发生异常），以释放它所持有的数据库连接资源。 如果忘记关闭，将导致严重的并发问题和数据库连接池耗尽。</p><h3 id="事务问题"><a href="#事务问题" class="headerlink" title="事务问题"></a>事务问题</h3><p>在之前的转账业务中，更新了两个账户，我们需要保证它们的<code>同时成功</code>或<code>同时失败</code>，这个时候就需要使用<code>事务</code>机制，在transfer方法开始执行时开启事务，直到两个更新都成功之后，再提交事务，我们尝试将transfer方法进行如下修改：</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_">AccountServiceImpl</span> <span class="keyword">implements</span> <span class="title class_">AccountService</span> &#123;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">private</span> <span class="type">AccountDao</span> <span class="variable">accountDao</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">AccountDaoImpl</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_">transfer</span><span class="params">(String fromActno, String toActno, <span class="type">double</span> money)</span> <span class="keyword">throws</span> MoneyNotEnoughException, AppException &#123;</span><br><span class="line">        <span class="comment">// 查询转出账户的余额</span></span><br><span class="line">        <span class="type">Account</span> <span class="variable">fromAct</span> <span class="operator">=</span> accountDao.selectByActno(fromActno);</span><br><span class="line">        <span class="keyword">if</span> (fromAct.getBalance() &lt; money) &#123;</span><br><span class="line">            <span class="keyword">throw</span> <span class="keyword">new</span> <span class="title class_">MoneyNotEnoughException</span>(<span class="string">&quot;对不起，您的余额不足。&quot;</span>);</span><br><span class="line">        &#125;</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="comment">// 修改账户余额</span></span><br><span class="line">            <span class="type">Account</span> <span class="variable">toAct</span> <span class="operator">=</span> accountDao.selectByActno(toActno);</span><br><span class="line">            fromAct.setBalance(fromAct.getBalance() - money);</span><br><span class="line">            toAct.setBalance(toAct.getBalance() + money);</span><br><span class="line">            <span class="comment">// 更新数据库（添加事务）</span></span><br><span class="line">            <span class="type">SqlSession</span> <span class="variable">sqlSession</span> <span class="operator">=</span> SqlSessionUtil.openSession();</span><br><span class="line">            accountDao.update(fromAct);</span><br><span class="line">            <span class="comment">// 模拟异常</span></span><br><span class="line">            <span class="type">String</span> <span class="variable">s</span> <span class="operator">=</span> <span class="literal">null</span>;</span><br><span class="line">            s.toString();</span><br><span class="line">            accountDao.update(toAct);</span><br><span class="line">            sqlSession.commit();</span><br><span class="line">            sqlSession.close();</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_">AppException</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><div class="tabs" id="实物问题"><ul class="nav-tabs"><li class="tab active"><button type="button" data-href="#实物问题-1">运行前数据库中的数据</button></li><li class="tab"><button type="button" data-href="#实物问题-2">执行程序</button></li><li class="tab"><button type="button" data-href="#实物问题-3">再次查看数据库表</button></li></ul><div class="tab-contents"><div class="tab-item-content active" id="实物问题-1"><div class="img-wrap"><div class="img-bg"><img class="img" src="https://raw.githubusercontent.com/silvan2077/markdown_pic/main/Picgo/20251020191250.png"/></div></div><button type="button" class="tab-to-top" aria-label="scroll to top"><i class="fas fa-arrow-up"></i></button></div><div class="tab-item-content" id="实物问题-2"><div class="img-wrap"><div class="img-bg"><img class="img" src="https://raw.githubusercontent.com/silvan2077/markdown_pic/main/Picgo/20251020191250.png"/></div></div><div class="img-wrap"><div class="img-bg"><img class="img" src="https://raw.githubusercontent.com/silvan2077/markdown_pic/main/Picgo/20251020191416.png"/></div></div><button type="button" class="tab-to-top" aria-label="scroll to top"><i class="fas fa-arrow-up"></i></button></div><div class="tab-item-content" id="实物问题-3"><div class="img-wrap"><div class="img-bg"><img class="img" src="https://raw.githubusercontent.com/silvan2077/markdown_pic/main/Picgo/20251020191526.png"/></div></div><button type="button" class="tab-to-top" aria-label="scroll to top"><i class="fas fa-arrow-up"></i></button></div></div></div><div class="note danger no-icon flat"><p>明明转账失败了，但钱却少了，而对面的钱没有增加，这是为什么？主要是因为service和dao中使用的SqlSession对象不是同一个</p></div><p>解决方法：为了保证<code>service</code>和<code>dao</code>中使用的SqlSession对象是同一个，可以将SqlSession对象存放到ThreadLocal当中。修改SqlSessionUtil工具类</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="keyword">public</span> <span class="keyword">class</span> <span class="title class_">SqlSessionUtil</span> &#123;</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="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">static</span> &#123;</span><br><span class="line">        <span class="keyword">try</span> &#123;</span><br><span class="line">            <span class="type">SqlSessionFactoryBuilder</span> <span class="variable">sqlSessionFactoryBuilder</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">SqlSessionFactoryBuilder</span>();</span><br><span class="line">            sqlSessionFactory = sqlSessionFactoryBuilder.build(Resources.getResourceAsStream(<span class="string">&quot;mybatis-config.xml&quot;</span>));</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"></span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">static</span> ThreadLocal&lt;SqlSession&gt; local = <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="comment">/**</span></span><br><span class="line"><span class="comment">     * 每调用一次openSession()可获取一个新的会话，该会话支持自动提交。</span></span><br><span class="line"><span class="comment">     *</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@return</span> 新的会话对象</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="type">SqlSession</span> <span class="variable">sqlSession</span> <span class="operator">=</span> local.get();</span><br><span class="line">        <span class="keyword">if</span> (sqlSession == <span class="literal">null</span>) &#123;</span><br><span class="line">            sqlSession = sqlSessionFactory.openSession();</span><br><span class="line">            local.set(sqlSession);</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">return</span> sqlSession;</span><br><span 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 class="doctag">@param</span> 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> <span class="keyword">void</span> <span class="title function_">close</span><span class="params">(SqlSession sqlSession)</span>&#123;</span><br><span class="line">        <span class="keyword">if</span> (sqlSession != <span class="literal">null</span>) &#123;</span><br><span class="line">            sqlSession.close();</span><br><span class="line">        &#125;</span><br><span class="line">        local.remove();</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>修改dao中的方法：将AccountDaoImpl中所有方法中的提交commit和关闭close代码全部删除。</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="keyword">public</span> <span class="keyword">class</span> <span class="title class_">AccountDaoImpl</span> <span class="keyword">implements</span> <span class="title class_">AccountDao</span> &#123;</span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="keyword">public</span> Account <span class="title function_">selectByActno</span><span class="params">(String actno)</span> &#123;</span><br><span class="line">        <span class="type">SqlSession</span> <span class="variable">sqlSession</span> <span class="operator">=</span> SqlSessionUtil.openSession();</span><br><span class="line">        <span class="type">Account</span> <span class="variable">act</span> <span class="operator">=</span> (Account)sqlSession.selectOne(<span class="string">&quot;account.selectByActno&quot;</span>, actno);</span><br><span class="line">        <span class="keyword">return</span> act;</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_">update</span><span class="params">(Account act)</span> &#123;</span><br><span class="line">        <span class="type">SqlSession</span> <span class="variable">sqlSession</span> <span class="operator">=</span> SqlSessionUtil.openSession();</span><br><span class="line">        <span class="type">int</span> <span class="variable">count</span> <span class="operator">=</span> sqlSession.update(<span class="string">&quot;account.update&quot;</span>, act);</span><br><span class="line">        <span class="keyword">return</span> count;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>修改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></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">AccountServiceImpl</span> <span class="keyword">implements</span> <span class="title class_">AccountService</span> &#123;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">private</span> <span class="type">AccountDao</span> <span class="variable">accountDao</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">AccountDaoImpl</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_">transfer</span><span class="params">(String fromActno, String toActno, <span class="type">double</span> money)</span> <span class="keyword">throws</span> MoneyNotEnoughException, AppException &#123;</span><br><span class="line">        <span class="comment">// 查询转出账户的余额</span></span><br><span class="line">        <span class="type">Account</span> <span class="variable">fromAct</span> <span class="operator">=</span> accountDao.selectByActno(fromActno);</span><br><span class="line">        <span class="keyword">if</span> (fromAct.getBalance() &lt; money) &#123;</span><br><span class="line">            <span class="keyword">throw</span> <span class="keyword">new</span> <span class="title class_">MoneyNotEnoughException</span>(<span class="string">&quot;对不起，您的余额不足。&quot;</span>);</span><br><span class="line">        &#125;</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="comment">// 修改账户余额</span></span><br><span class="line">            <span class="type">Account</span> <span class="variable">toAct</span> <span class="operator">=</span> accountDao.selectByActno(toActno);</span><br><span class="line">            fromAct.setBalance(fromAct.getBalance() - money);</span><br><span class="line">            toAct.setBalance(toAct.getBalance() + money);</span><br><span class="line">            <span class="comment">// 更新数据库（添加事务）</span></span><br><span class="line">            <span class="type">SqlSession</span> <span class="variable">sqlSession</span> <span class="operator">=</span> SqlSessionUtil.openSession();</span><br><span class="line">            accountDao.update(fromAct);</span><br><span class="line">            <span class="comment">// 模拟异常</span></span><br><span class="line">            <span class="type">String</span> <span class="variable">s</span> <span class="operator">=</span> <span class="literal">null</span>;</span><br><span class="line">            s.toString();</span><br><span class="line">            accountDao.update(toAct);</span><br><span class="line">            sqlSession.commit();</span><br><span class="line">            SqlSessionUtil.close(sqlSession);  <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">throw</span> <span class="keyword">new</span> <span class="title class_">AppException</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><p>再次测试：<br><div class="img-wrap"><div class="img-bg"><img class="img" src="https://raw.githubusercontent.com/silvan2077/markdown_pic/main/Picgo/20251020192322.png"/></div></div><br><div class="img-wrap"><div class="img-bg"><img class="img" src="https://raw.githubusercontent.com/silvan2077/markdown_pic/main/Picgo/20251020192238.png"/></div></div><br><div class="img-wrap"><div class="img-bg"><img class="img" src="https://raw.githubusercontent.com/silvan2077/markdown_pic/main/Picgo/20251020192349.png"/></div></div><br>此时转账失败,账户里的余额没有被修改<br><div class="img-wrap"><div class="img-bg"><img class="img" src="https://raw.githubusercontent.com/silvan2077/markdown_pic/main/Picgo/20251020192405.png"/></div></div></p><h2 id="分析当前程序存在的问题"><a href="#分析当前程序存在的问题" class="headerlink" title="分析当前程序存在的问题"></a>分析当前程序存在的问题</h2><p>我们来看一下DaoImpl的代码</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="keyword">public</span> <span class="keyword">class</span> <span class="title class_">AccountDaoImpl</span> <span class="keyword">implements</span> <span class="title class_">AccountDao</span> &#123;</span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="keyword">public</span> Account <span class="title function_">selectByActno</span><span class="params">(String actno)</span> &#123;</span><br><span class="line">        <span class="type">SqlSession</span> <span class="variable">sqlSession</span> <span class="operator">=</span> SqlSessionUtil.openSession();</span><br><span class="line">        <span class="type">Account</span> <span class="variable">act</span> <span class="operator">=</span> (Account)sqlSession.selectOne(<span class="string">&quot;account.selectByActno&quot;</span>, actno);</span><br><span class="line">        <span class="keyword">return</span> act;</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_">update</span><span class="params">(Account act)</span> &#123;</span><br><span class="line">        <span class="type">SqlSession</span> <span class="variable">sqlSession</span> <span class="operator">=</span> SqlSessionUtil.openSession();</span><br><span class="line">        <span class="type">int</span> <span class="variable">count</span> <span class="operator">=</span> sqlSession.update(<span class="string">&quot;account.update&quot;</span>, act);</span><br><span class="line">        <span class="keyword">return</span> count;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>不难发现，这个dao实现类中的方法代码很固定，基本上就是一行代码，通过SqlSession对象调用insert、delete、update、select等方法，这个类中的方法没有任何业务逻辑，既然是这样，<strong>这个类我们能不能动态的生成</strong>，以后可以不写这个类吗？答案：可以。</p><h1 id="使用javassist生成类"><a href="#使用javassist生成类" class="headerlink" title="使用javassist生成类"></a>使用javassist生成类</h1><p>来自百度百科：</p><blockquote><p>Javassist是一个开源的分析、编辑和创建Java字节码的类库。是由东京工业大学的数学和计算机科学系的 Shigeru Chiba （千叶 滋）所创建的。它已加入了开放源代码JBoss 应用服务器项目，通过使用Javassist对字节码操作为JBoss实现动态”AOP”框架。</p></blockquote><h2 id="Javassist的使用"><a href="#Javassist的使用" class="headerlink" title="Javassist的使用"></a>Javassist的使用</h2><p>要使用javassist，首先要引入它的依赖</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>org.javassist<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>javassist<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.29.1-GA<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>样例代码：</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_">JavassistTest</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">ClassPool</span> <span class="variable">pool</span> <span class="operator">=</span> ClassPool.getDefault();</span><br><span class="line">        <span class="comment">// 创建类</span></span><br><span class="line">        <span class="type">CtClass</span> <span class="variable">ctClass</span> <span class="operator">=</span> pool.makeClass(<span class="string">&quot;com.powernode.javassist.Test&quot;</span>);</span><br><span class="line">        <span class="comment">// 创建方法</span></span><br><span class="line">        <span class="comment">// 1.返回值类型 2.方法名 3.形式参数列表 4.所属类</span></span><br><span class="line">        <span class="type">CtMethod</span> <span class="variable">ctMethod</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">CtMethod</span>(CtClass.voidType, <span class="string">&quot;execute&quot;</span>, <span class="keyword">new</span> <span class="title class_">CtClass</span>[]&#123;&#125;, ctClass);</span><br><span class="line">        <span class="comment">// 设置方法的修饰符列表</span></span><br><span class="line">        ctMethod.setModifiers(Modifier.PUBLIC);</span><br><span class="line">        <span class="comment">// 设置方法体</span></span><br><span class="line">        ctMethod.setBody(<span class="string">&quot;&#123;System.out.println(\&quot;hello world\&quot;);&#125;&quot;</span>);</span><br><span class="line">        <span class="comment">// 给类添加方法</span></span><br><span class="line">        ctClass.addMethod(ctMethod);</span><br><span class="line">        <span class="comment">// 调用方法</span></span><br><span class="line">        Class&lt;?&gt; aClass = ctClass.toClass();</span><br><span class="line">        <span class="type">Object</span> <span class="variable">o</span> <span class="operator">=</span> aClass.newInstance();</span><br><span class="line">        <span class="type">Method</span> <span class="variable">method</span> <span class="operator">=</span> aClass.getDeclaredMethod(<span class="string">&quot;execute&quot;</span>);</span><br><span class="line">        method.invoke(o);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>运行要注意：加两个参数，要不然会有异常。</p><ul><li>—add-opens java.base/java.lang=ALL-UNNAMED</li><li>—add-opens java.base/sun.net.util=ALL-UNNAMED</li></ul><div class="tabs" id="javassist的使用"><ul class="nav-tabs"><li class="tab active"><button type="button" data-href="#javassist的使用-1">IDEA 2023新版及以后</button></li><li class="tab"><button type="button" data-href="#javassist的使用-2">IDEA 旧版</button></li><li class="tab"><button type="button" data-href="#javassist的使用-3">运行结果</button></li></ul><div class="tab-contents"><div class="tab-item-content active" id="javassist的使用-1"><div class="img-wrap"><div class="img-bg"><img class="img" src="https://raw.githubusercontent.com/silvan2077/markdown_pic/main/Picgo/20251020212505.png"/></div></div><button type="button" class="tab-to-top" aria-label="scroll to top"><i class="fas fa-arrow-up"></i></button></div><div class="tab-item-content" id="javassist的使用-2"><div class="img-wrap"><div class="img-bg"><img class="img" src="https://raw.githubusercontent.com/silvan2077/markdown_pic/main/Picgo/20251020212529.png"/></div></div><button type="button" class="tab-to-top" aria-label="scroll to top"><i class="fas fa-arrow-up"></i></button></div><div class="tab-item-content" id="javassist的使用-3"><div class="img-wrap"><div class="img-bg"><img class="img" src="https://raw.githubusercontent.com/silvan2077/markdown_pic/main/Picgo/20251020212548.png"/></div></div><button type="button" class="tab-to-top" aria-label="scroll to top"><i class="fas fa-arrow-up"></i></button></div></div></div><h2 id="使用Javassist生成DaoImpl类"><a href="#使用Javassist生成DaoImpl类" class="headerlink" title="使用Javassist生成DaoImpl类"></a>使用Javassist生成DaoImpl类</h2><p>使用Javassist动态生成DaoImpl类</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></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">GenerateDaoByJavassist</span> &#123;</span><br><span class="line"></span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     * 根据dao接口生成dao接口的代理对象</span></span><br><span class="line"><span class="comment">     *</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@param</span> sqlSession   sql会话</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@param</span> daoInterface dao接口</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@return</span> dao接口代理对象</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> Object <span class="title function_">getMapper</span><span class="params">(SqlSession sqlSession, Class daoInterface)</span> &#123;</span><br><span class="line">        <span class="type">ClassPool</span> <span class="variable">pool</span> <span class="operator">=</span> ClassPool.getDefault();</span><br><span class="line">        <span class="comment">// 生成代理类</span></span><br><span class="line">        <span class="type">CtClass</span> <span class="variable">ctClass</span> <span class="operator">=</span> pool.makeClass(daoInterface.getPackageName() + <span class="string">&quot;.impl.&quot;</span> + daoInterface.getSimpleName() + <span class="string">&quot;Impl&quot;</span>);</span><br><span class="line">        <span class="comment">// 接口</span></span><br><span class="line">        <span class="type">CtClass</span> <span class="variable">ctInterface</span> <span class="operator">=</span> pool.makeClass(daoInterface.getName());</span><br><span class="line">        <span class="comment">// 代理类实现接口</span></span><br><span class="line">        ctClass.addInterface(ctInterface);</span><br><span class="line">        <span class="comment">// 获取所有的方法</span></span><br><span class="line">        Method[] methods = daoInterface.getDeclaredMethods();</span><br><span class="line">        Arrays.stream(methods).forEach(method -&gt; &#123;</span><br><span class="line">            <span class="comment">// 拼接方法的签名</span></span><br><span class="line">            <span class="type">StringBuilder</span> <span class="variable">methodStr</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">StringBuilder</span>();</span><br><span class="line">            <span class="type">String</span> <span class="variable">returnTypeName</span> <span class="operator">=</span> method.getReturnType().getName();</span><br><span class="line">            methodStr.append(returnTypeName);</span><br><span class="line">            methodStr.append(<span class="string">&quot; &quot;</span>);</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">            methodStr.append(methodName);</span><br><span class="line">            methodStr.append(<span class="string">&quot;(&quot;</span>);</span><br><span class="line">            Class&lt;?&gt;[] parameterTypes = method.getParameterTypes();</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; parameterTypes.length; i++) &#123;</span><br><span class="line">                methodStr.append(parameterTypes[i].getName());</span><br><span class="line">                methodStr.append(<span class="string">&quot; arg&quot;</span>);</span><br><span class="line">                methodStr.append(i);</span><br><span class="line">                <span class="keyword">if</span> (i != parameterTypes.length - <span class="number">1</span>) &#123;</span><br><span class="line">                    methodStr.append(<span class="string">&quot;,&quot;</span>);</span><br><span class="line">                &#125;</span><br><span class="line">            &#125;</span><br><span class="line">            methodStr.append(<span class="string">&quot;)&#123;&quot;</span>);</span><br><span class="line">            <span class="comment">// 方法体当中的代码怎么写？</span></span><br><span class="line">            <span class="comment">// 获取sqlId（这里非常重要：因为这行代码导致以后namespace必须是接口的全限定接口名，sqlId必须是接口中方法的方法名。）</span></span><br><span class="line">            <span class="type">String</span> <span class="variable">sqlId</span> <span class="operator">=</span> daoInterface.getName() + <span class="string">&quot;.&quot;</span> + methodName;</span><br><span class="line">            <span class="comment">// 获取SqlCommondType</span></span><br><span class="line">            <span class="type">String</span> <span class="variable">sqlCommondTypeName</span> <span class="operator">=</span> sqlSession.getConfiguration().getMappedStatement(sqlId).getSqlCommandType().name();</span><br><span class="line">            <span class="keyword">if</span> (<span class="string">&quot;SELECT&quot;</span>.equals(sqlCommondTypeName)) &#123;</span><br><span class="line">                methodStr.append(<span class="string">&quot;org.apache.ibatis.session.SqlSession sqlSession = com.powernode.bank.utils.SqlSessionUtil.openSession();&quot;</span>);</span><br><span class="line">                methodStr.append(<span class="string">&quot;Object obj = sqlSession.selectOne(\&quot;&quot;</span> + sqlId + <span class="string">&quot;\&quot;, arg0);&quot;</span>);</span><br><span class="line">                methodStr.append(<span class="string">&quot;return (&quot;</span> + returnTypeName + <span class="string">&quot;)obj;&quot;</span>);</span><br><span class="line">            &#125; <span class="keyword">else</span> <span class="keyword">if</span> (<span class="string">&quot;UPDATE&quot;</span>.equals(sqlCommondTypeName)) &#123;</span><br><span class="line">                methodStr.append(<span class="string">&quot;org.apache.ibatis.session.SqlSession sqlSession = com.powernode.bank.utils.SqlSessionUtil.openSession();&quot;</span>);</span><br><span class="line">                methodStr.append(<span class="string">&quot;int count = sqlSession.update(\&quot;&quot;</span> + sqlId + <span class="string">&quot;\&quot;, arg0);&quot;</span>);</span><br><span class="line">                methodStr.append(<span class="string">&quot;return count;&quot;</span>);</span><br><span class="line">            &#125;</span><br><span class="line">            methodStr.append(<span class="string">&quot;&#125;&quot;</span>);</span><br><span class="line">            System.out.println(methodStr);</span><br><span class="line">            <span class="keyword">try</span> &#123;</span><br><span class="line">                <span class="comment">// 创建CtMethod对象</span></span><br><span class="line">                <span class="type">CtMethod</span> <span class="variable">ctMethod</span> <span class="operator">=</span> CtMethod.make(methodStr.toString(), ctClass);</span><br><span class="line">                ctMethod.setModifiers(Modifier.PUBLIC);</span><br><span class="line">                <span class="comment">// 将方法添加到类</span></span><br><span class="line">                ctClass.addMethod(ctMethod);</span><br><span class="line">            &#125; <span class="keyword">catch</span> (CannotCompileException e) &#123;</span><br><span class="line">                <span class="keyword">throw</span> <span class="keyword">new</span> <span class="title class_">RuntimeException</span>(e);</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;);</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">            Class&lt;?&gt; aClass = ctClass.toClass();</span><br><span class="line">            Constructor&lt;?&gt; defaultCon = aClass.getDeclaredConstructor();</span><br><span class="line">            <span class="type">Object</span> <span class="variable">o</span> <span class="operator">=</span> defaultCon.newInstance();</span><br><span class="line">            <span class="keyword">return</span> o;</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>(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>修改AccountMapper.xml文件：namespace必须是dao接口的全限定名称，id必须是dao接口中的方法名：</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><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="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></span><br><span class="line"><span class="meta">        <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="tag">&lt;<span class="name">mapper</span> <span class="attr">namespace</span>=<span class="string">&quot;com.powernode.bank.dao.AccountDao&quot;</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;selectByActno&quot;</span> <span class="attr">resultType</span>=<span class="string">&quot;com.powernode.bank.pojo.Account&quot;</span>&gt;</span></span><br><span class="line">        select * from t_act where actno = #&#123;actno&#125;</span><br><span class="line">    <span class="tag">&lt;/<span class="name">select</span>&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>&gt;</span></span><br><span class="line">        update t_act set balance = #&#123;balance&#125; where actno = #&#123;actno&#125;</span><br><span class="line">    <span class="tag">&lt;/<span class="name">update</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;/<span class="name">mapper</span>&gt;</span></span><br></pre></td></tr></table></figure><p>修改service类中获取dao对象的代码：</p><div class="img-wrap"><div class="img-bg"><img class="img" src="https://raw.githubusercontent.com/silvan2077/markdown_pic/main/Picgo/20251020213123.png"/></div></div><p>启动服务器：<strong>启动过程中显示，tomcat服务器自动添加了以下的两个运行参数。所以不需要再单独配置。</strong></p><div class="img-wrap"><div class="img-bg"><img class="img" src="https://raw.githubusercontent.com/silvan2077/markdown_pic/main/Picgo/20251020213147.png"/></div></div><h1 id="MyBatis中接口代理机制及使用"><a href="#MyBatis中接口代理机制及使用" class="headerlink" title="MyBatis中接口代理机制及使用"></a>MyBatis中接口代理机制及使用</h1><div class="note info no-icon flat"><p>其实以上所讲内容mybatis内部已经实现了。直接调用以下代码即可获取dao接口的代理类</p></div><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="type">AccountDao</span> <span class="variable">accountDao</span> <span class="operator">=</span> (AccountDao)sqlSession.getMapper(AccountDao.class);</span><br></pre></td></tr></table></figure><p>使用以上代码的前提是：<strong>AccountMapper.xml文件中的namespace必须和dao接口的全限定名称一致，id必须和dao接口中方法名一致。</strong></p><p>将service中获取dao对象的代码再次修改，如下：</p><p><img src="瑞吉外卖项目实战说明/1660611871663-27d92f0e-e0f5-4ab5-9a6e-be949a5b5469.png" alt="img"></p><h1 id="MyBatis小技巧"><a href="#MyBatis小技巧" class="headerlink" title="MyBatis小技巧"></a>MyBatis小技巧</h1><h2 id="和"><a href="#和" class="headerlink" title="#{}和${}"></a>#{}和${}</h2><ul><li><p><code>#&#123;&#125;</code>：先编译sql语句，再给占位符传值，底层是PreparedStatement实现。可以防止sql注入，比较常用。</p></li><li><p><code>$&#123;&#125;</code>：先进行sql语句拼接，然后再编译sql语句，底层是Statement实现。存在sql注入现象。只有在需要进行sql语句关键字拼接的情况下才会用到。</p></li></ul><div class="note info no-icon flat"><p>需求：根据car_type查询汽车</p></div><h3 id=""><a href="#" class="headerlink" title="#{}"></a>#{}</h3><p>环境准备：<br><div class="tabs" id="111"><ul class="nav-tabs"><li class="tab active"><button type="button" data-href="#111-1">pom.xml</button></li><li class="tab"><button type="button" data-href="#111-2">jdbc.properties</button></li><li class="tab"><button type="button" data-href="#111-3">logback.xml</button></li><li class="tab"><button type="button" data-href="#111-4">utils工具类</button></li></ul><div class="tab-contents"><div class="tab-item-content active" id="111-1"><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></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 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.powernode<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-005-antic<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">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>3.5.10<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="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>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-java<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.30<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="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 class="comment">&lt;!--logback依赖--&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>ch.qos.logback<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>logback-classic<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.11<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">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">properties</span>&gt;</span></span><br><span class="line"></span><br><span class="line"><span class="tag">&lt;/<span class="name">project</span>&gt;</span></span><br></pre></td></tr></table></figure><button type="button" class="tab-to-top" aria-label="scroll to top"><i class="fas fa-arrow-up"></i></button></div><div class="tab-item-content" id="111-2"><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></pre></td><td class="code"><pre><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/powernode</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">root</span></span><br></pre></td></tr></table></figure><button type="button" class="tab-to-top" aria-label="scroll to top"><i class="fas fa-arrow-up"></i></button></div><div class="tab-item-content" id="111-3"><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><br><span class="line"><span class="tag">&lt;<span class="name">configuration</span> <span class="attr">debug</span>=<span class="string">&quot;false&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">appender</span> <span class="attr">name</span>=<span class="string">&quot;STDOUT&quot;</span> <span class="attr">class</span>=<span class="string">&quot;ch.qos.logback.core.ConsoleAppender&quot;</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">encoder</span> <span class="attr">class</span>=<span class="string">&quot;ch.qos.logback.classic.encoder.PatternLayoutEncoder&quot;</span>&gt;</span></span><br><span class="line">      <span class="comment">&lt;!--格式化输出：%d表示日期，%thread表示线程名，%-5level：级别从左显示5个字符宽度%msg：日志消息，%n是换行符--&gt;</span></span><br><span class="line">      <span class="tag">&lt;<span class="name">pattern</span>&gt;</span>%d&#123;yyyy-MM-dd HH:mm:ss.SSS&#125; [%thread] %-5level %logger&#123;50&#125; - %msg%n<span class="tag">&lt;/<span class="name">pattern</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;/<span class="name">encoder</span>&gt;</span></span><br><span class="line">  <span class="tag">&lt;/<span class="name">appender</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">appender</span> <span class="attr">name</span>=<span class="string">&quot;FILE&quot;</span> <span class="attr">class</span>=<span class="string">&quot;ch.qos.logback.core.rolling.RollingFileAppender&quot;</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">rollingPolicy</span> <span class="attr">class</span>=<span class="string">&quot;ch.qos.logback.core.rolling.TimeBasedRollingPolicy&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">FileNamePattern</span>&gt;</span>$&#123;LOG_HOME&#125;/TestWeb.log.%d&#123;yyyy-MM-dd&#125;.log<span class="tag">&lt;/<span class="name">FileNamePattern</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">MaxHistory</span>&gt;</span>30<span class="tag">&lt;/<span class="name">MaxHistory</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;/<span class="name">rollingPolicy</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">encoder</span> <span class="attr">class</span>=<span class="string">&quot;ch.qos.logback.classic.encoder.PatternLayoutEncoder&quot;</span>&gt;</span></span><br><span class="line">      <span class="comment">&lt;!--格式化输出：%d表示日期，%thread表示线程名，%-5level：级别从左显示5个字符宽度%msg：日志消息，%n是换行符--&gt;</span></span><br><span class="line">      <span class="tag">&lt;<span class="name">pattern</span>&gt;</span>%d&#123;yyyy-MM-dd HH:mm:ss.SSS&#125; [%thread] %-5level %logger&#123;50&#125; - %msg%n<span class="tag">&lt;/<span class="name">pattern</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;/<span class="name">encoder</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">triggeringPolicy</span> <span class="attr">class</span>=<span class="string">&quot;ch.qos.logback.core.rolling.SizeBasedTriggeringPolicy&quot;</span>&gt;</span></span><br><span class="line">      <span class="tag">&lt;<span class="name">MaxFileSize</span>&gt;</span>100MB<span class="tag">&lt;/<span class="name">MaxFileSize</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;/<span class="name">triggeringPolicy</span>&gt;</span></span><br><span class="line">  <span class="tag">&lt;/<span class="name">appender</span>&gt;</span></span><br><span class="line"></span><br><span class="line">  <span class="comment">&lt;!--mybatis log configure--&gt;</span></span><br><span class="line">  <span class="tag">&lt;<span class="name">logger</span> <span class="attr">name</span>=<span class="string">&quot;com.apache.ibatis&quot;</span> <span class="attr">level</span>=<span class="string">&quot;TRACE&quot;</span>/&gt;</span></span><br><span class="line">  <span class="tag">&lt;<span class="name">logger</span> <span class="attr">name</span>=<span class="string">&quot;java.sql.Connection&quot;</span> <span class="attr">level</span>=<span class="string">&quot;DEBUG&quot;</span>/&gt;</span></span><br><span class="line">  <span class="tag">&lt;<span class="name">logger</span> <span class="attr">name</span>=<span class="string">&quot;java.sql.Statement&quot;</span> <span class="attr">level</span>=<span class="string">&quot;DEBUG&quot;</span>/&gt;</span></span><br><span class="line">  <span class="tag">&lt;<span class="name">logger</span> <span class="attr">name</span>=<span class="string">&quot;java.sql.PreparedStatement&quot;</span> <span class="attr">level</span>=<span class="string">&quot;DEBUG&quot;</span>/&gt;</span></span><br><span class="line"></span><br><span class="line">  <span class="comment">&lt;!-- 日志输出级别,logback日志级别包括五个：TRACE &lt; DEBUG &lt; INFO &lt; WARN &lt; ERROR --&gt;</span></span><br><span class="line">  <span class="tag">&lt;<span class="name">root</span> <span class="attr">level</span>=<span class="string">&quot;DEBUG&quot;</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">appender-ref</span> <span class="attr">ref</span>=<span class="string">&quot;STDOUT&quot;</span>/&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">appender-ref</span> <span class="attr">ref</span>=<span class="string">&quot;FILE&quot;</span>/&gt;</span></span><br><span class="line">  <span class="tag">&lt;/<span class="name">root</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><button type="button" class="tab-to-top" aria-label="scroll to top"><i class="fas fa-arrow-up"></i></button></div><div class="tab-item-content" id="111-4"><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><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_">SqlSessionUtil</span> &#123;</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="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">static</span> &#123;</span><br><span class="line">        <span class="keyword">try</span> &#123;</span><br><span class="line">            <span class="type">SqlSessionFactoryBuilder</span> <span class="variable">sqlSessionFactoryBuilder</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">SqlSessionFactoryBuilder</span>();</span><br><span class="line">            sqlSessionFactory = sqlSessionFactoryBuilder.build(Resources.getResourceAsStream(<span class="string">&quot;mybatis-config.xml&quot;</span>));</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"></span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">static</span> ThreadLocal&lt;SqlSession&gt; local = <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="comment">/**</span></span><br><span class="line"><span class="comment">     * 每调用一次openSession()可获取一个新的会话，该会话支持自动提交。</span></span><br><span class="line"><span class="comment">     *</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@return</span> 新的会话对象</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="type">SqlSession</span> <span class="variable">sqlSession</span> <span class="operator">=</span> local.get();</span><br><span class="line">        <span class="keyword">if</span> (sqlSession == <span class="literal">null</span>) &#123;</span><br><span class="line">            sqlSession = sqlSessionFactory.openSession();</span><br><span class="line">            local.set(sqlSession);</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">return</span> sqlSession;</span><br><span 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 class="doctag">@param</span> 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> <span class="keyword">void</span> <span class="title function_">close</span><span class="params">(SqlSession sqlSession)</span>&#123;</span><br><span class="line">        <span class="keyword">if</span> (sqlSession != <span class="literal">null</span>) &#123;</span><br><span class="line">            sqlSession.close();</span><br><span class="line">        &#125;</span><br><span class="line">        local.remove();</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><button type="button" class="tab-to-top" aria-label="scroll to top"><i class="fas fa-arrow-up"></i></button></div></div></div></p><div class="tabs" id="222"><ul class="nav-tabs"><li class="tab active"><button type="button" data-href="#222-1">Car</button></li><li class="tab"><button type="button" data-href="#222-2">mapper接口</button></li><li class="tab"><button type="button" data-href="#222-3">mybatis-config.xml</button></li></ul><div class="tab-contents"><div class="tab-item-content active" id="222-1"><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><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><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">Car</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 carNum;</span><br><span class="line">    <span class="keyword">private</span> String brand;</span><br><span class="line">    <span class="keyword">private</span> Double guidePrice;</span><br><span class="line">    <span class="keyword">private</span> String produceTime;</span><br><span class="line">    <span class="keyword">private</span> String carType;</span><br><span class="line">    <span class="comment">// 构造方法</span></span><br><span class="line">    <span class="comment">// set get方法</span></span><br><span class="line">    <span class="comment">// toString方法</span></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><button type="button" class="tab-to-top" aria-label="scroll to top"><i class="fas fa-arrow-up"></i></button></div><div class="tab-item-content" id="222-2"><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><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">interface</span> <span class="title class_">CarMapper</span> &#123;</span><br><span class="line"></span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     * 根据car_num获取Car</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@param</span> carType</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@return</span></span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    List&lt;Car&gt; <span class="title function_">selectByCarType</span><span class="params">(String carType)</span>;</span><br><span class="line"></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><button type="button" class="tab-to-top" aria-label="scroll to top"><i class="fas fa-arrow-up"></i></button></div><div class="tab-item-content" id="222-3"><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></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></span><br><span class="line"><span class="meta">        <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 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 class="tag">&lt;<span class="name">environments</span> <span class="attr">default</span>=<span class="string">&quot;dev&quot;</span>&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;dev&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 class="tag">&lt;<span class="name">mappers</span>&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;CarMapper.xml&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 class="tag">&lt;/<span class="name">configuration</span>&gt;</span></span><br></pre></td></tr></table></figure><button type="button" class="tab-to-top" aria-label="scroll to top"><i class="fas fa-arrow-up"></i></button></div></div></div><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></pre></td><td class="code"><pre><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * CarMapper测试类</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_">CarMapperTest</span> &#123;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Test</span></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">testSelectByCarType</span><span class="params">()</span>&#123;</span><br><span class="line">        <span class="type">CarMapper</span> <span class="variable">mapper</span> <span class="operator">=</span> (CarMapper) SqlSessionUtil.openSession().getMapper(CarMapper.class);</span><br><span class="line">        List&lt;Car&gt; cars = mapper.selectByCarType(<span class="string">&quot;燃油车&quot;</span>);</span><br><span class="line">        cars.forEach(car -&gt; System.out.println(car));</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>执行结果：<br><div class="img-wrap"><div class="img-bg"><img class="img" src="https://raw.githubusercontent.com/silvan2077/markdown_pic/main/Picgo/20251020215435.png"/></div></div></p><p>通过执行可以清楚的看到，sql语句中是带有 <code>?</code> 的，这个 <code>?</code> 就是之前在JDBC中所学的占位符，专门用来接收值的。</p><p>把“燃油车”以String类型的值，传递给 ? </p><p><strong><code>#&#123;&#125;</code>它会先进行sql语句的预编译，然后再给占位符传值</strong></p><h3 id="-1"><a href="#-1" class="headerlink" title="${}"></a>${}</h3><p>同样的需求，我们使用<code>$&#123;&#125;</code>来完成</p><p>CarMapper.xml文件修改如下：</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></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></span><br><span class="line"><span class="meta">        <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="tag">&lt;<span class="name">mapper</span> <span class="attr">namespace</span>=<span class="string">&quot;com.powernode.mybatis.mapper.CarMapper&quot;</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;selectByCarType&quot;</span> <span class="attr">resultType</span>=<span class="string">&quot;com.powernode.mybatis.pojo.Car&quot;</span>&gt;</span></span><br><span class="line">        select</span><br><span class="line">            id,car_num as carNum,brand,guide_price as guidePrice,produce_time as produceTime,car_type as carType</span><br><span class="line">        from</span><br><span class="line">            t_car</span><br><span class="line">        where</span><br><span class="line">            <span class="comment">&lt;!--car_type = #&#123;carType&#125;--&gt;</span></span><br><span class="line">            car_type = $&#123;carType&#125;</span><br><span class="line">    <span class="tag">&lt;/<span class="name">select</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;/<span class="name">mapper</span>&gt;</span></span><br></pre></td></tr></table></figure><p>再次运行测试程序：</p><div class="img-wrap"><div class="img-bg"><img class="img" src="https://raw.githubusercontent.com/silvan2077/markdown_pic/main/Picgo/20251020215638.png"/></div></div><p>这时候会抛出异常，观察生成的sql语句：<br><div class="img-wrap"><div class="img-bg"><img class="img" src="https://raw.githubusercontent.com/silvan2077/markdown_pic/main/Picgo/20251020215747.png"/></div></div></p><p>显然<code>$&#123;&#125;</code>是先进行sql语句的<code>拼接</code>，然后再编译，燃油车是一个字符串，在Sql语句中应当添加单引号</p><p>修改：</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></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></span><br><span class="line"><span class="meta">        <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="tag">&lt;<span class="name">mapper</span> <span class="attr">namespace</span>=<span class="string">&quot;com.powernode.mybatis.mapper.CarMapper&quot;</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;selectByCarType&quot;</span> <span class="attr">resultType</span>=<span class="string">&quot;com.powernode.mybatis.pojo.Car&quot;</span>&gt;</span></span><br><span class="line">        select</span><br><span class="line">            id,car_num as carNum,brand,guide_price as guidePrice,produce_time as produceTime,car_type as carType</span><br><span class="line">        from</span><br><span class="line">            t_car</span><br><span class="line">        where</span><br><span class="line">            <span class="comment">&lt;!--car_type = #&#123;carType&#125;--&gt;</span></span><br><span class="line">            <span class="comment">&lt;!--car_type = $&#123;carType&#125;--&gt;</span></span><br><span class="line">            car_type = &#x27;$&#123;carType&#125;&#x27;</span><br><span class="line">    <span class="tag">&lt;/<span class="name">select</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;/<span class="name">mapper</span>&gt;</span></span><br></pre></td></tr></table></figure><div class="note warning no-icon flat"><p>在日常的普通需求中，还是建议使用<code>#{}</code>的方式。<br><strong>原则：能用 #{} 就不用 ${}</strong></p></div><h3 id="什么情况下必须使用"><a href="#什么情况下必须使用" class="headerlink" title="什么情况下必须使用${}"></a>什么情况下必须使用${}</h3><h4 id="关键字拼接"><a href="#关键字拼接" class="headerlink" title="关键字拼接"></a>关键字拼接</h4><p>当需要进行sql语句关键字拼接的时候。必须使用<code>$&#123;&#125;</code></p><p>需求：通过向sql语句中注入<code>asc</code>或<code>desc</code>关键字，来完成数据的升序或降序排列。</p><ul><li><strong>尝试使用#{}：</strong></li></ul><div class="tabs" id="尝试使用"><ul class="nav-tabs"><li class="tab active"><button type="button" data-href="#尝试使用-1">CarMapper.java</button></li><li class="tab"><button type="button" data-href="#尝试使用-2">CarMapper.xml</button></li></ul><div class="tab-contents"><div class="tab-item-content active" id="尝试使用-1"><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><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="comment"> * 查询所有的Car</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@param</span> ascOrDesc asc或desc</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@return</span></span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line">List&lt;Car&gt; <span class="title function_">selectAll</span><span class="params">(String ascOrDesc)</span>;</span><br></pre></td></tr></table></figure><button type="button" class="tab-to-top" aria-label="scroll to top"><i class="fas fa-arrow-up"></i></button></div><div class="tab-item-content" id="尝试使用-2"><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="tag">&lt;<span class="name">select</span> <span class="attr">id</span>=<span class="string">&quot;selectAll&quot;</span> <span class="attr">resultType</span>=<span class="string">&quot;com.powernode.mybatis.pojo.Car&quot;</span>&gt;</span></span><br><span class="line">  select</span><br><span class="line">  id,car_num as carNum,brand,guide_price as guidePrice,produce_time as produceTime,car_type as carType</span><br><span class="line">  from</span><br><span class="line">  t_car</span><br><span class="line">  order by carNum #&#123;key&#125;</span><br><span class="line"><span class="tag">&lt;/<span class="name">select</span>&gt;</span></span><br></pre></td></tr></table></figure><button type="button" class="tab-to-top" aria-label="scroll to top"><i class="fas fa-arrow-up"></i></button></div></div></div><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></pre></td><td class="code"><pre><span class="line"><span class="meta">@Test</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">testSelectAll</span><span class="params">()</span>&#123;</span><br><span class="line">    <span class="type">CarMapper</span> <span class="variable">mapper</span> <span class="operator">=</span> (CarMapper) SqlSessionUtil.openSession().getMapper(CarMapper.class);</span><br><span class="line">    List&lt;Car&gt; cars = mapper.selectAll(<span class="string">&quot;desc&quot;</span>);</span><br><span class="line">    cars.forEach(car -&gt; System.out.println(car));</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>运行：</p><div class="img-wrap"><div class="img-bg"><img class="img" src="https://raw.githubusercontent.com/silvan2077/markdown_pic/main/Picgo/20251020220310.png"/></div></div><p>报错的原因是sql语句不合法，因为采用这种方式传值，程序会认为desc是一个字段名，而不是一个关键字，最终SQL语句会是这样：</p><blockquote><p>select id,car_num as carNum,brand,guide_price as guidePrice,produce_time as produceTime,car_type as carType from t_car order by carNum ‘desc’</p></blockquote><p>desc是一个关键字，不能带单引号的，所以在进行sql语句关键字拼接的时候，必须使用<code>$&#123;&#125;</code></p><ul><li><strong>使用${}改造</strong></li></ul><p>修改xml文件中的<code>#&#123;&#125;</code>为<code>$&#123;&#125;</code><br><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></pre></td><td class="code"><pre><span class="line"><span class="tag">&lt;<span class="name">select</span> <span class="attr">id</span>=<span class="string">&quot;selectAll&quot;</span> <span class="attr">resultType</span>=<span class="string">&quot;com.powernode.mybatis.pojo.Car&quot;</span>&gt;</span></span><br><span class="line">  select</span><br><span class="line">  id,car_num as carNum,brand,guide_price as guidePrice,produce_time as produceTime,car_type as carType</span><br><span class="line">  from</span><br><span class="line">  t_car</span><br><span class="line">  <span class="comment">&lt;!--order by carNum #&#123;key&#125;--&gt;</span></span><br><span class="line">  order by carNum $&#123;key&#125;</span><br><span class="line"><span class="tag">&lt;/<span class="name">select</span>&gt;</span></span><br></pre></td></tr></table></figure></p><p>再次执行测试程序：</p><div class="img-wrap"><div class="img-bg"><img class="img" src="https://raw.githubusercontent.com/silvan2077/markdown_pic/main/Picgo/20251020220310.png"/></div></div><h4 id="拼接表名"><a href="#拼接表名" class="headerlink" title="拼接表名"></a>拼接表名</h4><div class="note info no-icon flat"><p>业务背景：实际开发中，若数据量巨大可能采用分表的方式进行存储，比如每天生成一张表，表的名字与日期挂钩，例如：2022年8月1日生成的表：t_user20220108，2000年1月1日生成的表：t_user20000101。此时前端查询时会提交一个具体的日期，而后端则根据日期动态拼接表名。</p></div><p>使用<code>#&#123;&#125;</code>拼接表名会是这样：select <em> from ‘t_car’<br>使用<code>$&#123;&#125;</code>拼接表名会是这样：select </em> from t_car</p><div class="tabs" id="拼接表名"><ul class="nav-tabs"><li class="tab active"><button type="button" data-href="#拼接表名-1">CarMapper接口</button></li><li class="tab"><button type="button" data-href="#拼接表名-2">CarMapper.xml</button></li></ul><div class="tab-contents"><div class="tab-item-content active" id="拼接表名-1"><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><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="comment"> * 根据表名查询所有的Car</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@param</span> tableName</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@return</span></span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line">List&lt;Car&gt; <span class="title function_">selectAllByTableName</span><span class="params">(String tableName)</span>;</span><br></pre></td></tr></table></figure><button type="button" class="tab-to-top" aria-label="scroll to top"><i class="fas fa-arrow-up"></i></button></div><div class="tab-item-content" id="拼接表名-2"><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="tag">&lt;<span class="name">select</span> <span class="attr">id</span>=<span class="string">&quot;selectAllByTableName&quot;</span> <span class="attr">resultType</span>=<span class="string">&quot;car&quot;</span>&gt;</span></span><br><span class="line">  select</span><br><span class="line">  id,car_num as carNum,brand,guide_price as guidePrice,produce_time as produceTime,car_type as carType</span><br><span class="line">  from</span><br><span class="line">  $&#123;tableName&#125;</span><br><span class="line"><span class="tag">&lt;/<span class="name">select</span>&gt;</span></span><br></pre></td></tr></table></figure><button type="button" class="tab-to-top" aria-label="scroll to top"><i class="fas fa-arrow-up"></i></button></div></div></div><p>测试程序：<br><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><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="meta">@Test</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">testSelectAllByTableName</span><span class="params">()</span>&#123;</span><br><span class="line">    <span class="type">CarMapper</span> <span class="variable">mapper</span> <span class="operator">=</span> SqlSessionUtil.openSession().getMapper(CarMapper.class);</span><br><span class="line">    List&lt;Car&gt; cars = mapper.selectAllByTableName(<span class="string">&quot;t_car&quot;</span>);</span><br><span class="line">    cars.forEach(car -&gt; System.out.println(car));</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><br>执行结果：</p><div class="img-wrap"><div class="img-bg"><img class="img" src="https://raw.githubusercontent.com/silvan2077/markdown_pic/main/Picgo/20251020224121.png"/></div></div><h4 id="批量删除"><a href="#批量删除" class="headerlink" title="批量删除"></a>批量删除</h4><div class="note info no-icon flat"><p>业务背景：一次删除多条记录。</p></div><div class="img-wrap"><div class="img-bg"><img class="img" src="https://raw.githubusercontent.com/silvan2077/markdown_pic/main/Picgo/20251020224248.png"/></div></div><p>对应的sql语句：</p><blockquote><ul><li>delete from t_user where id = 1 or id = 2 or id = 3;</li><li>delete from t_user where id in(1, 2, 3);</li></ul></blockquote><p>假设现在使用in的方式处理，前端传过来的字符串：1, 2, 3</p><ul><li><p>使用<code>#&#123;&#125;</code> ：delete from t_user where id in(‘1,2,3’)<br><strong>执行错误：1292 - Truncated incorrect DOUBLE value: ‘1,2,3’</strong></p></li><li><p>使用<code>$&#123;&#125;</code> ：delete from t_user where id in(1, 2, 3)</p></li></ul><div class="tabs" id="批量删除"><ul class="nav-tabs"><li class="tab active"><button type="button" data-href="#批量删除-1">CarMapper接口</button></li><li class="tab"><button type="button" data-href="#批量删除-2">CarMapper.xml</button></li></ul><div class="tab-contents"><div class="tab-item-content active" id="批量删除-1"><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><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="comment">     * 根据id批量删除</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@param</span> ids</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@return</span></span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line"><span class="type">int</span> <span class="title function_">deleteBatch</span><span class="params">(String ids)</span>;</span><br></pre></td></tr></table></figure><button type="button" class="tab-to-top" aria-label="scroll to top"><i class="fas fa-arrow-up"></i></button></div><div class="tab-item-content" id="批量删除-2"><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="tag">&lt;<span class="name">delete</span> <span class="attr">id</span>=<span class="string">&quot;deleteBatch&quot;</span>&gt;</span></span><br><span class="line">  delete from t_car where id in($&#123;ids&#125;)</span><br><span class="line"><span class="tag">&lt;/<span class="name">delete</span>&gt;</span></span><br></pre></td></tr></table></figure><button type="button" class="tab-to-top" aria-label="scroll to top"><i class="fas fa-arrow-up"></i></button></div></div></div><p>测试程序：<br><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><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="meta">@Test</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">testDeleteBatch</span><span class="params">()</span>&#123;</span><br><span class="line">    <span class="type">CarMapper</span> <span class="variable">mapper</span> <span class="operator">=</span> SqlSessionUtil.openSession().getMapper(CarMapper.class);</span><br><span class="line">    <span class="type">int</span> <span class="variable">count</span> <span class="operator">=</span> mapper.deleteBatch(<span class="string">&quot;1,2,3&quot;</span>);</span><br><span class="line">    System.out.println(<span class="string">&quot;删除了几条记录：&quot;</span> + count);</span><br><span class="line">    SqlSessionUtil.openSession().commit();</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><br>执行结果：</p><div class="img-wrap"><div class="img-bg"><img class="img" src="https://raw.githubusercontent.com/silvan2077/markdown_pic/main/Picgo/20251020224616.png"/></div></div><h3 id="模糊查询"><a href="#模糊查询" class="headerlink" title="模糊查询"></a>模糊查询</h3><div class="note info no-icon flat"><p>需求：查询奔驰系列的汽车。【只要品牌brand中含有奔驰两个字的都查询出来。】</p></div><h4 id="使用"><a href="#使用" class="headerlink" title="使用${}"></a>使用${}</h4><div class="tabs" id="模糊查询1"><ul class="nav-tabs"><li class="tab active"><button type="button" data-href="#模糊查询1-1">CarMapper接口</button></li><li class="tab"><button type="button" data-href="#模糊查询1-2">CarMapper.xml</button></li></ul><div class="tab-contents"><div class="tab-item-content active" id="模糊查询1-1"><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><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="comment">     * 根据品牌进行模糊查询</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@param</span> likeBrank</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@return</span></span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">List&lt;Car&gt; <span class="title function_">selectLikeByBrand</span><span class="params">(String likeBrank)</span>;</span><br></pre></td></tr></table></figure><button type="button" class="tab-to-top" aria-label="scroll to top"><i class="fas fa-arrow-up"></i></button></div><div class="tab-item-content" id="模糊查询1-2"><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></pre></td><td class="code"><pre><span class="line"><span class="tag">&lt;<span class="name">select</span> <span class="attr">id</span>=<span class="string">&quot;selectLikeByBrand&quot;</span> <span class="attr">resultType</span>=<span class="string">&quot;Car&quot;</span>&gt;</span></span><br><span class="line">  select</span><br><span class="line">  id,car_num as carNum,brand,guide_price as guidePrice,produce_time as produceTime,car_type as carType</span><br><span class="line">  from</span><br><span class="line">  t_car</span><br><span class="line">  where</span><br><span class="line">  brand like &#x27;%$&#123;brand&#125;%&#x27;</span><br><span class="line"><span class="tag">&lt;/<span class="name">select</span>&gt;</span></span><br></pre></td></tr></table></figure><button type="button" class="tab-to-top" aria-label="scroll to top"><i class="fas fa-arrow-up"></i></button></div></div></div><p>测试程序：<br><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><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="meta">@Test</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">testSelectLikeByBrand</span><span class="params">()</span>&#123;</span><br><span class="line">    <span class="type">CarMapper</span> <span class="variable">mapper</span> <span class="operator">=</span> SqlSessionUtil.openSession().getMapper(CarMapper.class);</span><br><span class="line">    List&lt;Car&gt; cars = mapper.selectLikeByBrand(<span class="string">&quot;奔驰&quot;</span>);</span><br><span class="line">    cars.forEach(car -&gt; System.out.println(car));</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><br>执行结果：<br><div class="img-wrap"><div class="img-bg"><img class="img" src="https://raw.githubusercontent.com/silvan2077/markdown_pic/main/Picgo/20251020225010.png"/></div></div></p><h4 id="使用-1"><a href="#使用-1" class="headerlink" title="使用#{}"></a>使用#{}</h4><p><strong>第一种：concat函数</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></pre></td><td class="code"><pre><span class="line"><span class="tag">&lt;<span class="name">select</span> <span class="attr">id</span>=<span class="string">&quot;selectLikeByBrand&quot;</span> <span class="attr">resultType</span>=<span class="string">&quot;Car&quot;</span>&gt;</span></span><br><span class="line">  select</span><br><span class="line">  id,car_num as carNum,brand,guide_price as guidePrice,produce_time as produceTime,car_type as carType</span><br><span class="line">  from</span><br><span class="line">  t_car</span><br><span class="line">  where</span><br><span class="line">  brand like concat(&#x27;%&#x27;,#&#123;brand&#125;,&#x27;%&#x27;)</span><br><span class="line"><span class="tag">&lt;/<span class="name">select</span>&gt;</span></span><br></pre></td></tr></table></figure><p>执行结果：</p><div class="img-wrap"><div class="img-bg"><img class="img" src="https://raw.githubusercontent.com/silvan2077/markdown_pic/main/Picgo/20251020225641.png"/></div></div><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></pre></td><td class="code"><pre><span class="line"><span class="tag">&lt;<span class="name">select</span> <span class="attr">id</span>=<span class="string">&quot;selectLikeByBrand&quot;</span> <span class="attr">resultType</span>=<span class="string">&quot;Car&quot;</span>&gt;</span></span><br><span class="line">  select</span><br><span class="line">  id,car_num as carNum,brand,guide_price as guidePrice,produce_time as produceTime,car_type as carType</span><br><span class="line">  from</span><br><span class="line">  t_car</span><br><span class="line">  where</span><br><span class="line">  brand like &quot;%&quot;#&#123;brand&#125;&quot;%&quot;</span><br><span class="line"><span class="tag">&lt;/<span class="name">select</span>&gt;</span></span><br></pre></td></tr></table></figure><p>执行结果：<br><div class="img-wrap"><div class="img-bg"><img class="img" src="https://raw.githubusercontent.com/silvan2077/markdown_pic/main/Picgo/20251020225829.png"/></div></div></p><h2 id="typeAliases"><a href="#typeAliases" class="headerlink" title="typeAliases"></a>typeAliases</h2><p>在CarMapper.xml配置信息：</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></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></span><br><span class="line"><span class="meta">        <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="tag">&lt;<span class="name">mapper</span> <span class="attr">namespace</span>=<span class="string">&quot;com.powernode.mybatis.mapper.CarMapper&quot;</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;selectAll&quot;</span> <span class="attr">resultType</span>=<span class="string">&quot;com.powernode.mybatis.pojo.Car&quot;</span>&gt;</span></span><br><span class="line">        select</span><br><span class="line">            id,car_num as carNum,brand,guide_price as guidePrice,produce_time as produceTime,car_type as carType</span><br><span class="line">        from</span><br><span class="line">            t_car</span><br><span class="line">        order by carNum $&#123;key&#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="tag">&lt;<span class="name">select</span> <span class="attr">id</span>=<span class="string">&quot;selectByCarType&quot;</span> <span class="attr">resultType</span>=<span class="string">&quot;com.powernode.mybatis.pojo.Car&quot;</span>&gt;</span></span><br><span class="line">        select</span><br><span class="line">            id,car_num as carNum,brand,guide_price as guidePrice,produce_time as produceTime,car_type as carType</span><br><span class="line">        from</span><br><span class="line">            t_car</span><br><span class="line">        where</span><br><span class="line">            car_type = &#x27;$&#123;carType&#125;&#x27;</span><br><span class="line">    <span class="tag">&lt;/<span class="name">select</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;/<span class="name">mapper</span>&gt;</span></span><br></pre></td></tr></table></figure><p>resultType属性用来指定查询结果集的封装类型，一般情况下需要写包的全类名，但我们可以通过typeAliases标签来起别名，这样，以后使用别名来代替包名+类名的方式来获取对象。</p><h3 id="第一种方式：typeAlias"><a href="#第一种方式：typeAlias" class="headerlink" title="第一种方式：typeAlias"></a>第一种方式：typeAlias</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></pre></td><td class="code"><pre><span class="line"><span class="tag">&lt;<span class="name">typeAliases</span>&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.powernode.mybatis.pojo.Car&quot;</span> <span class="attr">alias</span>=<span class="string">&quot;Car&quot;</span>/&gt;</span></span><br><span class="line"><span class="tag">&lt;/<span class="name">typeAliases</span>&gt;</span></span><br></pre></td></tr></table></figure><ul><li>首先要注意typeAliases标签的放置位置，如果不清楚的话，可以看看错误提示信息。</li><li>typeAliases标签中的typeAlias可以写多个。</li><li>typeAlias：<ul><li>type属性：指定给哪个类起别名</li><li>alias属性：别名。<ul><li><strong>alias属性不是必须的，</strong>如果缺省的话，type属性指定的类型名的简类名作为别名。</li><li><strong>alias是大小写不敏感的。</strong>也就是说假设alias=”Car”，再用的时候，可以CAR，也可以car，也可以Car，都行。</li></ul></li></ul></li></ul><h3 id="第二种方式：package"><a href="#第二种方式：package" class="headerlink" title="第二种方式：package"></a>第二种方式：package</h3><p>如果一个包下的类太多，每个类都要起别名，会导致typeAlias标签配置较多，所以mybatis也提供package的配置方式，只需要指定包名，该包下的所有类都自动起别名，别名就是简类名。并且别名不区分大小写。</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></pre></td><td class="code"><pre><span class="line"><span class="tag">&lt;<span class="name">typeAliases</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.powernode.mybatis.pojo&quot;</span>/&gt;</span></span><br><span class="line"><span class="tag">&lt;/<span class="name">typeAliases</span>&gt;</span></span><br></pre></td></tr></table></figure><p>package也可以配置多个，通常使用逗号分隔。<br><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="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">package</span> <span class="attr">name</span>=<span class="string">&quot;com.example.model,com.example.dto&quot;</span>/&gt;</span></span><br><span class="line">    </span><br><span class="line">    <span class="comment">&lt;!-- 也可以配置多个 &lt;package&gt; 标签，但更常见和简洁的是使用逗号分隔 --&gt;</span></span><br><span class="line">    <span class="comment">&lt;!-- 另一种方式：</span></span><br><span class="line"><span class="comment">    &lt;package name=&quot;com.example.model&quot;/&gt;</span></span><br><span class="line"><span class="comment">    &lt;package name=&quot;com.example.dto&quot;/&gt; </span></span><br><span class="line"><span class="comment">    --&gt;</span></span><br><span class="line">  <span class="tag">&lt;/<span class="name">typeAliases</span>&gt;</span></span><br></pre></td></tr></table></figure><br><div class="note warning no-icon flat"><p>实际上，MyBatis 在解析包名列表时，支持的默认分隔符包括逗号（,）、分号（;）、空格（）、制表符（\t）和换行符（\n）。但逗号是最常用和推荐的方式。</p></div></p><h3 id="在SQL映射文件中用一下"><a href="#在SQL映射文件中用一下" class="headerlink" title="在SQL映射文件中用一下"></a>在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></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></span><br><span class="line"><span class="meta">        <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="tag">&lt;<span class="name">mapper</span> <span class="attr">namespace</span>=<span class="string">&quot;com.powernode.mybatis.mapper.CarMapper&quot;</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;selectAll&quot;</span> <span class="attr">resultType</span>=<span class="string">&quot;CAR&quot;</span>&gt;</span></span><br><span class="line">        select</span><br><span class="line">            id,car_num as carNum,brand,guide_price as guidePrice,produce_time as produceTime,car_type as carType</span><br><span class="line">        from</span><br><span class="line">            t_car</span><br><span class="line">        order by carNum $&#123;key&#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="tag">&lt;<span class="name">select</span> <span class="attr">id</span>=<span class="string">&quot;selectByCarType&quot;</span> <span class="attr">resultType</span>=<span class="string">&quot;car&quot;</span>&gt;</span></span><br><span class="line">        select</span><br><span class="line">            id,car_num as carNum,brand,guide_price as guidePrice,produce_time as produceTime,car_type as carType</span><br><span class="line">        from</span><br><span class="line">            t_car</span><br><span class="line">        where</span><br><span class="line">            car_type = &#x27;$&#123;carType&#125;&#x27;</span><br><span class="line">    <span class="tag">&lt;/<span class="name">select</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;/<span class="name">mapper</span>&gt;</span></span><br></pre></td></tr></table></figure><h2 id="mappers"><a href="#mappers" class="headerlink" title="mappers"></a>mappers</h2><p>SQL映射文件的配置方式包括四种：</p><ul><li>resource：从类路径中加载</li><li>url：从指定的全限定资源路径中加载</li><li>class：使用映射器接口实现类的完全限定类名</li><li>package：将包内的映射器接口实现全部注册为映射器</li></ul><h3 id="resource"><a href="#resource" class="headerlink" title="resource"></a>resource</h3><p>这种方式是从类路径中加载配置文件，所以这种方式要求SQL映射文件必须放在resources目录下或其子目录下。</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">mappers</span>&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;org/mybatis/builder/AuthorMapper.xml&quot;</span>/&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;org/mybatis/builder/BlogMapper.xml&quot;</span>/&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;org/mybatis/builder/PostMapper.xml&quot;</span>/&gt;</span></span><br><span class="line"><span class="tag">&lt;/<span class="name">mappers</span>&gt;</span></span><br></pre></td></tr></table></figure><h3 id="url"><a href="#url" class="headerlink" title="url"></a>url</h3><p>这种方式显然使用了绝对路径的方式，这种配置对SQL映射文件存放的位置没有要求，随意。</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">mappers</span>&gt;</span></span><br><span class="line">  <span class="tag">&lt;<span class="name">mapper</span> <span class="attr">url</span>=<span class="string">&quot;file:///var/mappers/AuthorMapper.xml&quot;</span>/&gt;</span></span><br><span class="line">  <span class="tag">&lt;<span class="name">mapper</span> <span class="attr">url</span>=<span class="string">&quot;file:///var/mappers/BlogMapper.xml&quot;</span>/&gt;</span></span><br><span class="line">  <span class="tag">&lt;<span class="name">mapper</span> <span class="attr">url</span>=<span class="string">&quot;file:///var/mappers/PostMapper.xml&quot;</span>/&gt;</span></span><br><span class="line"><span class="tag">&lt;/<span class="name">mappers</span>&gt;</span></span><br></pre></td></tr></table></figure><h3 id="class"><a href="#class" class="headerlink" title="class"></a>class</h3><div class="img-wrap"><div class="img-bg"><img class="img" src="https://raw.githubusercontent.com/silvan2077/markdown_pic/main/Picgo/20251020233631.png"/></div></div><p>如果使用这种方式必须满足以下条件：</p><ul><li>SQL映射文件和mapper接口放在同一个目录下。</li><li>SQL映射文件的名字也必须和mapper接口名一致。</li></ul><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;!-- 使用映射器接口实现类的完全限定类名 --&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="tag">&lt;<span class="name">mapper</span> <span class="attr">class</span>=<span class="string">&quot;org.mybatis.builder.AuthorMapper&quot;</span>/&gt;</span></span><br><span class="line">  <span class="tag">&lt;<span class="name">mapper</span> <span class="attr">class</span>=<span class="string">&quot;org.mybatis.builder.BlogMapper&quot;</span>/&gt;</span></span><br><span class="line">  <span class="tag">&lt;<span class="name">mapper</span> <span class="attr">class</span>=<span class="string">&quot;org.mybatis.builder.PostMapper&quot;</span>/&gt;</span></span><br><span class="line"><span class="tag">&lt;/<span class="name">mappers</span>&gt;</span></span><br></pre></td></tr></table></figure><p>将CarMapper.xml文件移动到和mapper接口同一个目录下：</p><ul><li>在resources目录下新建：com/powernode/mybatis/mapper【这里千万要注意：<strong>不能这样新建 com.powernode.mybatis.dao</strong>】</li><li>将CarMapper.xml文件移动到mapper目录下</li><li>修改mybatis-config.xml文件</li></ul><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="tag">&lt;<span class="name">mappers</span>&gt;</span></span><br><span class="line">  <span class="tag">&lt;<span class="name">mapper</span> <span class="attr">class</span>=<span class="string">&quot;com.powernode.mybatis.mapper.CarMapper&quot;</span>/&gt;</span></span><br><span class="line"><span class="tag">&lt;/<span class="name">mappers</span>&gt;</span></span><br></pre></td></tr></table></figure><h3 id="package"><a href="#package" class="headerlink" title="package"></a>package</h3><p>如果class较多，可以使用这种package的方式，但前提条件和上一种方式一样。</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="comment">&lt;!-- 将包内的映射器接口实现全部注册为映射器 --&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="tag">&lt;<span class="name">package</span> <span class="attr">name</span>=<span class="string">&quot;com.powernode.mybatis.mapper&quot;</span>/&gt;</span></span><br><span class="line"><span class="tag">&lt;/<span class="name">mappers</span>&gt;</span></span><br></pre></td></tr></table></figure><h2 id="idea配置文件模板"><a href="#idea配置文件模板" class="headerlink" title="idea配置文件模板"></a>idea配置文件模板</h2><p>mybatis-config.xml和SqlMapper.xml文件可以在IDEA中提前创建好模板，以后通过模板创建配置文件。</p><div class="img-wrap"><div class="img-bg"><img class="img" src="https://raw.githubusercontent.com/silvan2077/markdown_pic/main/Picgo/20251020233939.png"/></div></div><h2 id="插入数据时获取自动生成的主键"><a href="#插入数据时获取自动生成的主键" class="headerlink" title="插入数据时获取自动生成的主键"></a>插入数据时获取自动生成的主键</h2><p><strong>前提：主键是自动生成的。</strong><br>业务背景：一个用户有多个角色。</p><div class="img-wrap"><div class="img-bg"><img class="img" src="https://raw.githubusercontent.com/silvan2077/markdown_pic/main/Picgo/20251020234134.png"/></div></div><div class="note info no-icon flat"><p>需求：插入一个用户数据的同时需要给该用户分配角色：需要将生成的用户的id插入到角色表的user_id字段上。如何获得该主键？</p><ul><li><p>第一种方式：可以先插入用户数据，再写一条查询语句获取id，然后再插入user_id字段。【比较麻烦】</p></li><li><p>第二种方式：mybatis提供了一种方式更加便捷。</p></li></ul></div><p>Mybatis提供的方法：<br><div class="tabs" id="获取自动生成的主键"><ul class="nav-tabs"><li class="tab active"><button type="button" data-href="#获取自动生成的主键-1">CarMapper接口</button></li><li class="tab"><button type="button" data-href="#获取自动生成的主键-2">CarMapper.xml</button></li></ul><div class="tab-contents"><div class="tab-item-content active" id="获取自动生成的主键-1"><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><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="comment">     * 获取自动生成的主键</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@param</span> car</span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line"><span class="keyword">void</span> <span class="title function_">insertUseGeneratedKeys</span><span class="params">(Car car)</span>;</span><br></pre></td></tr></table></figure><button type="button" class="tab-to-top" aria-label="scroll to top"><i class="fas fa-arrow-up"></i></button></div><div class="tab-item-content" id="获取自动生成的主键-2"><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;!--</span></span><br><span class="line"><span class="comment">useGeneratedKeys=&quot;true&quot; 使用自动生成的主键值。</span></span><br><span class="line"><span class="comment">keyProperty=&quot;id” 指定主键值赋值给对象的哪个属性。这个就表示将主键值财值给Car对象的id届性。</span></span><br><span class="line"><span class="comment">--&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;insertUseGeneratedKeys&quot;</span> <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 t_car(id,car_num,brand,guide_price,produce_time,car_type) values(null,#&#123;carNum&#125;,#&#123;brand&#125;,#&#123;guidePrice&#125;,#&#123;produceTime&#125;,#&#123;carType&#125;)</span><br><span class="line"><span class="tag">&lt;/<span class="name">insert</span>&gt;</span></span><br></pre></td></tr></table></figure><button type="button" class="tab-to-top" aria-label="scroll to top"><i class="fas fa-arrow-up"></i></button></div></div></div></p><h1 id="MyBatis参数处理"><a href="#MyBatis参数处理" class="headerlink" title="MyBatis参数处理"></a>MyBatis参数处理</h1><p>表：t_student<br><div class="img-wrap"><div class="img-bg"><img class="img" src="https://raw.githubusercontent.com/silvan2077/markdown_pic/main/Picgo/20251021115750.png"/></div></div></p><p>表中现有数据：<br><div class="img-wrap"><div class="img-bg"><img class="img" src="https://raw.githubusercontent.com/silvan2077/markdown_pic/main/Picgo/20251021115813.png"/></div></div></p><p>pojo类：</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="keyword">public</span> <span class="keyword">class</span> <span class="title class_">Student</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">    <span class="keyword">private</span> Double height;</span><br><span class="line">    <span class="keyword">private</span> Character sex;</span><br><span class="line">    <span class="keyword">private</span> Date birth;</span><br><span class="line">    <span class="comment">// constructor</span></span><br><span class="line">    <span class="comment">// setter and getter</span></span><br><span class="line">    <span class="comment">// toString</span></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h2 id="单个简单类型参数"><a href="#单个简单类型参数" class="headerlink" title="单个简单类型参数"></a>单个简单类型参数</h2><p>简单类型包括：</p><ul><li>byte short int long float double char</li><li>Byte Short Integer Long Float Double Character</li><li>String</li><li>java.util.Date</li><li>java.sql.Date</li></ul><div class="note info no-icon flat"><p>需求：根据name查、根据id查、根据birth查、根据sex查</p></div><div class="tabs" id="单个简单参数"><ul class="nav-tabs"><li class="tab active"><button type="button" data-href="#单个简单参数-1">StudentMapper接口</button></li><li class="tab"><button type="button" data-href="#单个简单参数-2">StudentMapper.xml</button></li><li class="tab"><button type="button" data-href="#单个简单参数-3">测试方法</button></li></ul><div class="tab-contents"><div class="tab-item-content active" id="单个简单参数-1"><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><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">/**</span></span><br><span class="line"><span class="comment"> * 学生数据Sql映射器</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@author</span> 老杜</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@version</span> 1.0</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@since</span> 1.0</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_">StudentMapper</span> &#123;</span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     * 根据name查询</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@param</span> name</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@return</span></span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    List&lt;Student&gt; <span class="title function_">selectByName</span><span class="params">(String name)</span>;</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 class="doctag">@param</span> id</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@return</span></span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    Student <span class="title function_">selectById</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">     * 根据birth查询</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@param</span> birth</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@return</span></span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    List&lt;Student&gt; <span class="title function_">selectByBirth</span><span class="params">(Date birth)</span>;</span><br><span class="line"></span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     * 根据sex查询</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@param</span> sex</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@return</span></span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    List&lt;Student&gt; <span class="title function_">selectBySex</span><span class="params">(Character sex)</span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><button type="button" class="tab-to-top" aria-label="scroll to top"><i class="fas fa-arrow-up"></i></button></div><div class="tab-item-content" id="单个简单参数-2"><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="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></span><br><span class="line"><span class="meta">        <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="tag">&lt;<span class="name">mapper</span> <span class="attr">namespace</span>=<span class="string">&quot;com.powernode.mybatis.mapper.StudentMapper&quot;</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;selectByName&quot;</span> <span class="attr">resultType</span>=<span class="string">&quot;student&quot;</span>&gt;</span></span><br><span class="line">        select * from t_student where name = #&#123;name&#125;</span><br><span class="line">    <span class="tag">&lt;/<span class="name">select</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;selectById&quot;</span> <span class="attr">resultType</span>=<span class="string">&quot;student&quot;</span>&gt;</span></span><br><span class="line">        select * from t_student 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 class="tag">&lt;<span class="name">select</span> <span class="attr">id</span>=<span class="string">&quot;selectByBirth&quot;</span> <span class="attr">resultType</span>=<span class="string">&quot;student&quot;</span>&gt;</span></span><br><span class="line">        select * from t_student where birth = #&#123;birth&#125;</span><br><span class="line">    <span class="tag">&lt;/<span class="name">select</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;selectBySex&quot;</span> <span class="attr">resultType</span>=<span class="string">&quot;student&quot;</span>&gt;</span></span><br><span class="line">        select * from t_student where sex = #&#123;sex&#125;</span><br><span class="line">    <span class="tag">&lt;/<span class="name">select</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;/<span class="name">mapper</span>&gt;</span></span><br></pre></td></tr></table></figure><button type="button" class="tab-to-top" aria-label="scroll to top"><i class="fas fa-arrow-up"></i></button></div><div class="tab-item-content" id="单个简单参数-3"><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><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_">StudentMapperTest</span> &#123;</span><br><span class="line"></span><br><span class="line">    <span class="type">StudentMapper</span> <span class="variable">mapper</span> <span class="operator">=</span> SqlSessionUtil.openSession().getMapper(StudentMapper.class);</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Test</span></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">testSelectByName</span><span class="params">()</span>&#123;</span><br><span class="line">        List&lt;Student&gt; students = mapper.selectByName(<span class="string">&quot;张三&quot;</span>);</span><br><span class="line">        students.forEach(student -&gt; System.out.println(student));</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="meta">@Test</span></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">testSelectById</span><span class="params">()</span>&#123;</span><br><span class="line">        <span class="type">Student</span> <span class="variable">student</span> <span class="operator">=</span> mapper.selectById(<span class="number">2L</span>);</span><br><span class="line">        System.out.println(student);</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="meta">@Test</span></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">testSelectByBirth</span><span class="params">()</span>&#123;</span><br><span class="line">        <span class="keyword">try</span> &#123;</span><br><span class="line">            <span class="type">Date</span> <span class="variable">birth</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">SimpleDateFormat</span>(<span class="string">&quot;yyyy-MM-dd&quot;</span>).parse(<span class="string">&quot;2022-08-16&quot;</span>);</span><br><span class="line">            List&lt;Student&gt; students = mapper.selectByBirth(birth);</span><br><span class="line">            students.forEach(student -&gt; System.out.println(student));</span><br><span class="line">        &#125; <span class="keyword">catch</span> (ParseException e) &#123;</span><br><span class="line">            <span class="keyword">throw</span> <span class="keyword">new</span> <span class="title class_">RuntimeException</span>(e);</span><br><span class="line">        &#125;</span><br><span class="line"></span><br><span class="line">    &#125;</span><br><span class="line">    <span class="meta">@Test</span></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">testSelectBySex</span><span class="params">()</span>&#123;</span><br><span class="line">        List&lt;Student&gt; students = mapper.selectBySex(<span class="string">&#x27;男&#x27;</span>);</span><br><span class="line">        students.forEach(student -&gt; System.out.println(student));</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><button type="button" class="tab-to-top" aria-label="scroll to top"><i class="fas fa-arrow-up"></i></button></div></div></div><p>通过测试得知，简单类型对于mybatis来说可以自动类型识别的：</p><ul><li>也就是说对于mybatis来说，它是可以自动推断出ps.setXxxx()方法的。<strong>ps.setString()还是ps.setInt()。它可以自动推断。</strong></li></ul><p>其实SQL映射文件中的配置比较完整的写法是：</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></pre></td><td class="code"><pre><span class="line"><span class="tag">&lt;<span class="name">select</span> <span class="attr">id</span>=<span class="string">&quot;selectByName&quot;</span> <span class="attr">resultType</span>=<span class="string">&quot;student&quot;</span> <span class="attr">parameterType</span>=<span class="string">&quot;java.lang.String&quot;</span>&gt;</span></span><br><span class="line">  select * from t_student where name = #&#123;name, javaType=String, jdbcType=VARCHAR&#125;</span><br><span class="line"><span class="tag">&lt;/<span class="name">select</span>&gt;</span></span><br></pre></td></tr></table></figure><p>其中sql语句中的javaType，jdbcType，以及select标签中的parameterType属性，都是用来帮助mybatis进行类型确定的。不过这些配置多数是可以省略的。因为mybatis它有强大的自动类型推断机制。</p><ul><li>javaType：可以省略</li><li>jdbcType：可以省略</li><li>parameterType：可以省略</li></ul><p><strong>如果参数只有一个的话，#{} 里面的内容就随便写了。对于 ${} 来说，注意加单引号。</strong></p><h2 id="Map参数"><a href="#Map参数" class="headerlink" title="Map参数"></a>Map参数</h2><div class="note info no-icon flat"><p>需求：根据name和age查询</p></div><div class="tabs" id="map参数"><ul class="nav-tabs"><li class="tab active"><button type="button" data-href="#map参数-1">StudentMapper接口</button></li><li class="tab"><button type="button" data-href="#map参数-2">StudentMapper.xml</button></li></ul><div class="tab-contents"><div class="tab-item-content active" id="map参数-1"><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><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="comment">* 根据name和age查询</span></span><br><span class="line"><span class="comment">* <span class="doctag">@param</span> paramMap</span></span><br><span class="line"><span class="comment">* <span class="doctag">@return</span></span></span><br><span class="line"><span class="comment">*/</span></span><br><span class="line">List&lt;Student&gt; <span class="title function_">selectByParamMap</span><span class="params">(Map&lt;String,Object&gt; paramMap)</span>;</span><br></pre></td></tr></table></figure><button type="button" class="tab-to-top" aria-label="scroll to top"><i class="fas fa-arrow-up"></i></button></div><div class="tab-item-content" id="map参数-2"><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="tag">&lt;<span class="name">select</span> <span class="attr">id</span>=<span class="string">&quot;selectByParamMap&quot;</span> <span class="attr">resultType</span>=<span class="string">&quot;student&quot;</span>&gt;</span></span><br><span class="line">  select * from t_student where name = #&#123;nameKey&#125; and age = #&#123;ageKey&#125;</span><br><span class="line"><span class="tag">&lt;/<span class="name">select</span>&gt;</span></span><br></pre></td></tr></table></figure><button type="button" class="tab-to-top" aria-label="scroll to top"><i class="fas fa-arrow-up"></i></button></div></div></div><p><strong>该方式是手动封装Map集合，将每个条件以<code>key</code>和<code>value</code>的形式存放到集合中。然后在使用的时候通过#{map集合的key}来取值。</strong></p><h2 id="实体类参数"><a href="#实体类参数" class="headerlink" title="实体类参数"></a>实体类参数</h2><div class="note info no-icon flat"><p>需求：插入一条Student数据</p></div><div class="tabs" id="实体类参数"><ul class="nav-tabs"><li class="tab active"><button type="button" data-href="#实体类参数-1">StudentMapper接口</button></li><li class="tab"><button type="button" data-href="#实体类参数-2">studentMapper.xml</button></li><li class="tab"><button type="button" data-href="#实体类参数-3">测试方法</button></li></ul><div class="tab-contents"><div class="tab-item-content active" id="实体类参数-1"><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><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="comment"> * 保存学生数据</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@param</span> student</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@return</span></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">(Student student)</span>;</span><br></pre></td></tr></table></figure><button type="button" class="tab-to-top" aria-label="scroll to top"><i class="fas fa-arrow-up"></i></button></div><div class="tab-item-content" id="实体类参数-2"><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="tag">&lt;<span class="name">insert</span> <span class="attr">id</span>=<span class="string">&quot;insert&quot;</span>&gt;</span></span><br><span class="line">  insert into t_student values(null,#&#123;name&#125;,#&#123;age&#125;,#&#123;height&#125;,#&#123;birth&#125;,#&#123;sex&#125;)</span><br><span class="line"><span class="tag">&lt;/<span class="name">insert</span>&gt;</span></span><br></pre></td></tr></table></figure><button type="button" class="tab-to-top" aria-label="scroll to top"><i class="fas fa-arrow-up"></i></button></div><div class="tab-item-content" id="实体类参数-3"><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><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="meta">@Test</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">testInsert</span><span class="params">()</span>&#123;</span><br><span class="line">    <span class="type">Student</span> <span class="variable">student</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">Student</span>();</span><br><span class="line">    student.setName(<span class="string">&quot;李四&quot;</span>);</span><br><span class="line">    student.setAge(<span class="number">30</span>);</span><br><span class="line">    student.setHeight(<span class="number">1.70</span>);</span><br><span class="line">    student.setSex(<span class="string">&#x27;男&#x27;</span>);</span><br><span class="line">    student.setBirth(<span class="keyword">new</span> <span class="title class_">Date</span>());</span><br><span class="line">    <span class="type">int</span> <span class="variable">count</span> <span class="operator">=</span> mapper.insert(student);</span><br><span class="line">    SqlSessionUtil.openSession().commit();</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><button type="button" class="tab-to-top" aria-label="scroll to top"><i class="fas fa-arrow-up"></i></button></div></div></div><div class="note warning no-icon flat"><p><strong>这里需要注意的是：#{} 里面写的是属性名字。这个属性名其本质上是：set/get方法名去掉set/get之后的名字。</strong></p></div><h2 id="多参数"><a href="#多参数" class="headerlink" title="多参数"></a>多参数</h2><div class="note info no-icon flat"><p>需求：通过name和sex查询</p></div><div class="tabs" id="多参数"><ul class="nav-tabs"><li class="tab active"><button type="button" data-href="#多参数-1">StudentMapper接口</button></li><li class="tab"><button type="button" data-href="#多参数-2">StudentMapper.xml</button></li><li class="tab"><button type="button" data-href="#多参数-3">测试方法</button></li></ul><div class="tab-contents"><div class="tab-item-content active" id="多参数-1"><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><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="comment"> * 根据name和sex查询</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@param</span> name</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@param</span> sex</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@return</span></span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line">List&lt;Student&gt; <span class="title function_">selectByNameAndSex</span><span class="params">(String name, Character sex)</span>;</span><br></pre></td></tr></table></figure><button type="button" class="tab-to-top" aria-label="scroll to top"><i class="fas fa-arrow-up"></i></button></div><div class="tab-item-content" id="多参数-2"><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="tag">&lt;<span class="name">select</span> <span class="attr">id</span>=<span class="string">&quot;selectByNameAndSex&quot;</span> <span class="attr">resultType</span>=<span class="string">&quot;student&quot;</span>&gt;</span></span><br><span class="line">  select * from t_student where name = #&#123;name&#125; and sex = #&#123;sex&#125;</span><br><span class="line"><span class="tag">&lt;/<span class="name">select</span>&gt;</span></span><br></pre></td></tr></table></figure><button type="button" class="tab-to-top" aria-label="scroll to top"><i class="fas fa-arrow-up"></i></button></div><div class="tab-item-content" id="多参数-3"><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><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="meta">@Test</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">testSelectByNameAndSex</span><span class="params">()</span>&#123;</span><br><span class="line">    List&lt;Student&gt; students = mapper.selectByNameAndSex(<span class="string">&quot;张三&quot;</span>, <span class="string">&#x27;女&#x27;</span>);</span><br><span class="line">    students.forEach(student -&gt; System.out.println(student));</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><button type="button" class="tab-to-top" aria-label="scroll to top"><i class="fas fa-arrow-up"></i></button></div></div></div><p>执行结果：<br><div class="img-wrap"><div class="img-bg"><img class="img" src="https://raw.githubusercontent.com/silvan2077/markdown_pic/main/Picgo/20251021121433.png"/></div></div></p><p>该异常信息描述了：name参数找不到，可用的参数包括[arg1, arg0, param1, param2]</p><p>修改StudentMapper.xml配置文件：尝试使用[arg1, arg0, param1, param2]</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">select</span> <span class="attr">id</span>=<span class="string">&quot;selectByNameAndSex&quot;</span> <span class="attr">resultType</span>=<span class="string">&quot;student&quot;</span>&gt;</span></span><br><span class="line">  <span class="comment">&lt;!--select * from t_student where name = #&#123;name&#125; and sex = #&#123;sex&#125;--&gt;</span></span><br><span class="line">  select * from t_student where name = #&#123;arg0&#125; and sex = #&#123;arg1&#125;</span><br><span class="line"><span class="tag">&lt;/<span class="name">select</span>&gt;</span></span><br></pre></td></tr></table></figure><p>运行结果：</p><div class="img-wrap"><div class="img-bg"><img class="img" src="https://raw.githubusercontent.com/silvan2077/markdown_pic/main/Picgo/20251021141713.png"/></div></div><p>再次尝试修改StudentMapper.xml文件</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></pre></td><td class="code"><pre><span class="line"><span class="tag">&lt;<span class="name">select</span> <span class="attr">id</span>=<span class="string">&quot;selectByNameAndSex&quot;</span> <span class="attr">resultType</span>=<span class="string">&quot;student&quot;</span>&gt;</span></span><br><span class="line">  <span class="comment">&lt;!--select * from t_student where name = #&#123;name&#125; and sex = #&#123;sex&#125;--&gt;</span></span><br><span class="line">  <span class="comment">&lt;!--select * from t_student where name = #&#123;arg0&#125; and sex = #&#123;arg1&#125;--&gt;</span></span><br><span class="line">  <span class="comment">&lt;!--select * from t_student where name = #&#123;param1&#125; and sex = #&#123;param2&#125;--&gt;</span></span><br><span class="line">  select * from t_student where name = #&#123;arg0&#125; and sex = #&#123;param2&#125;</span><br><span class="line"><span class="tag">&lt;/<span class="name">select</span>&gt;</span></span><br></pre></td></tr></table></figure><p>通过测试可以看到：</p><ul><li>arg0 是第一个参数</li><li>param1是第一个参数</li><li>arg1 是第二个参数</li><li>param2是第二个参数</li></ul><div class="note info no-icon flat"><p>实现原理：<strong>实际上在mybatis底层会创建一个map集合，以arg0/param1为key，以方法上的参数为value</strong></p></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></pre></td><td class="code"><pre><span class="line">Map&lt;String,Object&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;arg0&quot;</span>, name);</span><br><span class="line">map.put(<span class="string">&quot;arg1&quot;</span>, sex);</span><br><span class="line">map.put(<span class="string">&quot;param1&quot;</span>, name);</span><br><span class="line">map.put(<span class="string">&quot;param2&quot;</span>, sex);</span><br><span class="line"></span><br><span class="line"><span class="comment">// 所以可以这样取值：#&#123;arg0&#125; #&#123;arg1&#125; #&#123;param1&#125; #&#123;param2&#125;</span></span><br><span class="line"><span class="comment">// 其本质就是#&#123;map集合的key&#125;</span></span><br></pre></td></tr></table></figure><h2 id="Param注解（命名参数）"><a href="#Param注解（命名参数）" class="headerlink" title="@Param注解（命名参数）"></a>@Param注解（命名参数）</h2><p>可以不用arg0 arg1 param1 param2,使用@Param注解来使用自定义的key，这样可以增强可读性。</p><div class="note info no-icon flat"><p>需求：根据name和age查询</p></div><div class="tabs" id="@param注解"><ul class="nav-tabs"><li class="tab active"><button type="button" data-href="#@param注解-1">StudentMapper接口</button></li><li class="tab"><button type="button" data-href="#@param注解-2">StudentMapper.xml</button></li><li class="tab"><button type="button" data-href="#@param注解-3">测试方法</button></li></ul><div class="tab-contents"><div class="tab-item-content active" id="@param注解-1"><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><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="comment"> * 根据name和age查询</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@param</span> name</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@param</span> age</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@return</span></span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line">List&lt;Student&gt; <span class="title function_">selectByNameAndAge</span><span class="params">(<span class="meta">@Param(value=&quot;name&quot;)</span> String name, <span class="meta">@Param(&quot;age&quot;)</span> <span class="type">int</span> age)</span>;</span><br></pre></td></tr></table></figure><button type="button" class="tab-to-top" aria-label="scroll to top"><i class="fas fa-arrow-up"></i></button></div><div class="tab-item-content" id="@param注解-2"><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="tag">&lt;<span class="name">select</span> <span class="attr">id</span>=<span class="string">&quot;selectByNameAndAge&quot;</span> <span class="attr">resultType</span>=<span class="string">&quot;student&quot;</span>&gt;</span></span><br><span class="line">  select * from t_student where name = #&#123;name&#125; and age = #&#123;age&#125;</span><br><span class="line"><span class="tag">&lt;/<span class="name">select</span>&gt;</span></span><br></pre></td></tr></table></figure><button type="button" class="tab-to-top" aria-label="scroll to top"><i class="fas fa-arrow-up"></i></button></div><div class="tab-item-content" id="@param注解-3"><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><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="meta">@Test</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">testSelectByNameAndAge</span><span class="params">()</span>&#123;</span><br><span class="line">    List&lt;Student&gt; stus = mapper.selectByNameAndAge(<span class="string">&quot;张三&quot;</span>, <span class="number">20</span>);</span><br><span class="line">    stus.forEach(student -&gt; System.out.println(student));</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><button type="button" class="tab-to-top" aria-label="scroll to top"><i class="fas fa-arrow-up"></i></button></div></div></div><h2 id="Param源码分析"><a href="#Param源码分析" class="headerlink" title="@Param源码分析"></a>@Param源码分析</h2><div class="img-wrap"><div class="img-bg"><img class="img" src="https://raw.githubusercontent.com/silvan2077/markdown_pic/main/Picgo/20251021142148.png"/></div></div><div class="img-wrap"><div class="img-bg"><img class="img" src="https://raw.githubusercontent.com/silvan2077/markdown_pic/main/Picgo/20251021142203.png"/></div></div><h1 id="MyBatis查询语句专题"><a href="#MyBatis查询语句专题" class="headerlink" title="MyBatis查询语句专题"></a>MyBatis查询语句专题</h1><h2 id="环境准备"><a href="#环境准备" class="headerlink" title="环境准备"></a>环境准备</h2><p>引入依赖：mysql驱动依赖、mybatis依赖、logback依赖、junit依赖。<br>引入配置文件：jdbc.properties、mybatis-config.xml、logback.xml<br>创建pojo类：Car<br>创建Mapper接口：CarMapper<br>创建Mapper接口对应的映射文件：com/powernode/mybatis/mapper/CarMapper.xml<br>创建单元测试：CarMapperTest<br>拷贝工具类：SqlSessionUtil</p><h2 id="返回Car实体类"><a href="#返回Car实体类" class="headerlink" title="返回Car实体类"></a>返回Car实体类</h2><p>当查询的结果，有对应的实体类，并且查询结果只有一条时：</p><div class="tabs" id="返回car实体类"><ul class="nav-tabs"><li class="tab active"><button type="button" data-href="#返回car实体类-1">CarMapper.selectById</button></li><li class="tab"><button type="button" data-href="#返回car实体类-2">CarMapper.xml</button></li><li class="tab"><button type="button" data-href="#返回car实体类-3">测试方法</button></li></ul><div class="tab-contents"><div class="tab-item-content active" id="返回car实体类-1"><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><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">public</span> <span class="keyword">interface</span> <span class="title class_">CarMapper</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 class="doctag">@param</span> id</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@return</span></span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    Car <span class="title function_">selectById</span><span class="params">(Long id)</span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><button type="button" class="tab-to-top" aria-label="scroll to top"><i class="fas fa-arrow-up"></i></button></div><div class="tab-item-content" id="返回car实体类-2"><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="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></span><br><span class="line"><span class="meta">        <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="tag">&lt;<span class="name">mapper</span> <span class="attr">namespace</span>=<span class="string">&quot;com.powernode.mybatis.mapper.CarMapper&quot;</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;selectById&quot;</span> <span class="attr">resultType</span>=<span class="string">&quot;Car&quot;</span>&gt;</span></span><br><span class="line">        select id,car_num carNum,brand,guide_price guidePrice,produce_time produceTime,car_type carType from t_car 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 class="tag">&lt;/<span class="name">mapper</span>&gt;</span></span><br></pre></td></tr></table></figure><button type="button" class="tab-to-top" aria-label="scroll to top"><i class="fas fa-arrow-up"></i></button></div><div class="tab-item-content" id="返回car实体类-3"><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><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">public</span> <span class="keyword">class</span> <span class="title class_">CarMapperTest</span> &#123;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Test</span></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">testSelectById</span><span class="params">()</span>&#123;</span><br><span class="line">        <span class="type">CarMapper</span> <span class="variable">mapper</span> <span class="operator">=</span> SqlSessionUtil.openSession().getMapper(CarMapper.class);</span><br><span class="line">        <span class="type">Car</span> <span class="variable">car</span> <span class="operator">=</span> mapper.selectById(<span class="number">35L</span>);</span><br><span class="line">        System.out.println(car);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><button type="button" class="tab-to-top" aria-label="scroll to top"><i class="fas fa-arrow-up"></i></button></div></div></div><p>执行结果：</p><div class="img-wrap"><div class="img-bg"><img class="img" src="https://raw.githubusercontent.com/silvan2077/markdown_pic/main/Picgo/20251021142538.png"/></div></div><p><strong>查询结果是一条的话也可以使用List集合</strong></p><h2 id="返回List"><a href="#返回List" class="headerlink" title="返回List"></a>返回List<Car></h2><div class="note info no-icon flat"><p>当查询的记录条数是多条的时候，必须使用集合接收。如果使用单个实体类接收会出现异常。</p></div><div class="tabs" id="返回list-2"><ul class="nav-tabs"><li class="tab active"><button type="button" data-href="#返回list-2-1">CarMapper.selectAll</button></li><li class="tab"><button type="button" data-href="#返回list-2-2">CarMapper.xml</button></li><li class="tab"><button type="button" data-href="#返回list-2-3">测试方法</button></li></ul><div class="tab-contents"><div class="tab-item-content active" id="返回list-2-1"><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><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="comment">* 查询所有的Car</span></span><br><span class="line"><span class="comment">* <span class="doctag">@return</span></span></span><br><span class="line"><span class="comment">*/</span></span><br><span class="line">List&lt;Car&gt; <span class="title function_">selectAll</span><span class="params">()</span>;</span><br></pre></td></tr></table></figure><button type="button" class="tab-to-top" aria-label="scroll to top"><i class="fas fa-arrow-up"></i></button></div><div class="tab-item-content" id="返回list-2-2"><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="tag">&lt;<span class="name">select</span> <span class="attr">id</span>=<span class="string">&quot;selectAll&quot;</span> <span class="attr">resultType</span>=<span class="string">&quot;Car&quot;</span>&gt;</span></span><br><span class="line">  select id,car_num carNum,brand,guide_price guidePrice,produce_time produceTime,car_type carType from t_car</span><br><span class="line"><span class="tag">&lt;/<span class="name">select</span>&gt;</span></span><br></pre></td></tr></table></figure><button type="button" class="tab-to-top" aria-label="scroll to top"><i class="fas fa-arrow-up"></i></button></div><div class="tab-item-content" id="返回list-2-3"><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><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="meta">@Test</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">testSelectAll</span><span class="params">()</span>&#123;</span><br><span class="line">    <span class="type">CarMapper</span> <span class="variable">mapper</span> <span class="operator">=</span> SqlSessionUtil.openSession().getMapper(CarMapper.class);</span><br><span class="line">    List&lt;Car&gt; cars = mapper.selectAll();</span><br><span class="line">    cars.forEach(car -&gt; System.out.println(car));</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><button type="button" class="tab-to-top" aria-label="scroll to top"><i class="fas fa-arrow-up"></i></button></div></div></div><p>执行结果：<br><div class="img-wrap"><div class="img-bg"><img class="img" src="https://raw.githubusercontent.com/silvan2077/markdown_pic/main/Picgo/20251021142838.png"/></div></div></p><h2 id="返回Map"><a href="#返回Map" class="headerlink" title="返回Map"></a>返回Map</h2><p>当返回的数据，没有合适的实体类对应的话，可以采用Map集合接收。将字段名做key，字段值作为value。</p><p>查询如果可以保证只有一条数据，则返回一个Map集合即可。</p><div class="img-wrap"><div class="img-bg"><img class="img" src="https://raw.githubusercontent.com/silvan2077/markdown_pic/main/Picgo/20251021143029.png"/></div></div><div class="tabs" id="返回map"><ul class="nav-tabs"><li class="tab active"><button type="button" data-href="#返回map-1">CarMapper.selectByIdRetMap</button></li><li class="tab"><button type="button" data-href="#返回map-2">CarMapper.xml</button></li><li class="tab"><button type="button" data-href="#返回map-3">测试方法</button></li></ul><div class="tab-contents"><div class="tab-item-content active" id="返回map-1"><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><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="comment"> * 通过id查询一条记录，返回Map集合</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@param</span> id</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@return</span></span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line">Map&lt;String, Object&gt; <span class="title function_">selectByIdRetMap</span><span class="params">(Long id)</span>;</span><br></pre></td></tr></table></figure><button type="button" class="tab-to-top" aria-label="scroll to top"><i class="fas fa-arrow-up"></i></button></div><div class="tab-item-content" id="返回map-2"><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="tag">&lt;<span class="name">select</span> <span class="attr">id</span>=<span class="string">&quot;selectByIdRetMap&quot;</span> <span class="attr">resultType</span>=<span class="string">&quot;map&quot;</span>&gt;</span></span><br><span class="line">  select id,car_num carNum,brand,guide_price guidePrice,produce_time produceTime,car_type carType from t_car where 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><button type="button" class="tab-to-top" aria-label="scroll to top"><i class="fas fa-arrow-up"></i></button></div><div class="tab-item-content" id="返回map-3"><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><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="meta">@Test</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">testSelectAllByResultMap</span><span class="params">()</span>&#123;</span><br><span class="line">    <span class="type">CarMapper</span> <span class="variable">carMapper</span> <span class="operator">=</span> SqlSessionUtil.openSession().getMapper(CarMapper.class);</span><br><span class="line">    List&lt;Car&gt; cars = carMapper.selectAllByResultMap();</span><br><span class="line">    System.out.println(cars);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><button type="button" class="tab-to-top" aria-label="scroll to top"><i class="fas fa-arrow-up"></i></button></div></div></div><div class="note info no-icon flat"><p><strong>resultMap=”map”，这是因为mybatis内置了很多别名。【参见mybatis开发手册】</strong></p></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></pre></td><td class="code"><pre><span class="line"><span class="meta">@Test</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">testSelectByIdRetMap</span><span class="params">()</span>&#123;</span><br><span class="line">    <span class="type">CarMapper</span> <span class="variable">mapper</span> <span class="operator">=</span> SqlSessionUtil.openSession().getMapper(CarMapper.class);</span><br><span class="line">    Map&lt;String,Object&gt; car = mapper.selectByIdRetMap(<span class="number">35L</span>);</span><br><span class="line">    System.out.println(car);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><div class="note info no-icon flat"><p>如果返回一条记录则直接返回Map集合即可，但如果返回多条记录。则最好返回一个List集合，否则会出现异常：<code>TooManyResultsException</code></p></div><h2 id="返回List-1"><a href="#返回List-1" class="headerlink" title="返回List"></a>返回List<Map></h2><p>查询结果条数大于等于1条数据，则可以返回一个存储Map集合的List集合。<code>List&lt;Map&gt;</code>等同于<code>List&lt;Car&gt;</code></p><p>只是返回的接收值换为<code>List&lt;Map&gt;</code>，其他不需要改变。</p><h2 id="resultMap结果映射"><a href="#resultMap结果映射" class="headerlink" title="resultMap结果映射"></a>resultMap结果映射</h2><p>查询结果的列名和java对象的属性名对应不上怎么办？</p><ul><li>第一种方式：as 给列起别名</li><li>第二种方式：使用resultMap进行结果映射</li><li>第三种方式：是否开启驼峰命名自动映射（配置settings）</li></ul><h3 id="使用resultMap进行结果映射"><a href="#使用resultMap进行结果映射" class="headerlink" title="使用resultMap进行结果映射"></a>使用resultMap进行结果映射</h3><p>主要为CarMapper.xml文件，添加一个<code>resultMap</code>标签<br><div class="tabs" id="使用resultmap进行结果映射"><ul class="nav-tabs"><li class="tab active"><button type="button" data-href="#使用resultmap进行结果映射-1">CarMapper接口</button></li></ul><div class="tab-contents"><div class="tab-item-content active" id="使用resultmap进行结果映射-1"><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><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="comment">     * 查询所有Car，使用resultMap进行结果映射</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@return</span></span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">List&lt;Car&gt; <span class="title function_">selectAllByResultMap</span><span class="params">()</span>;</span><br></pre></td></tr></table></figure><button type="button" class="tab-to-top" aria-label="scroll to top"><i class="fas fa-arrow-up"></i></button></div></div></div></p><p>执行结果正常。</p><h3 id="是否开启驼峰命名自动映射"><a href="#是否开启驼峰命名自动映射" class="headerlink" title="是否开启驼峰命名自动映射"></a>是否开启驼峰命名自动映射</h3><p>使用这种方式的前提是：属性名遵循Java的命名规范，数据库表的列名遵循SQL的命名规范。</p><p>Java命名规范：首字母小写，后面每个单词首字母大写，遵循驼峰命名方式。</p><p>SQL命名规范：全部小写，单词之间采用下划线分割。</p><p>比如以下的对应关系：</p><div class="table-container"><table><thead><tr><th><strong>实体类中的属性名</strong></th><th><strong>数据库表的列名</strong></th></tr></thead><tbody><tr><td>carNum</td><td>car_num</td></tr><tr><td>carType</td><td>car_type</td></tr><tr><td>produceTime</td><td>produce_time</td></tr></tbody></table></div><p>如何启用该功能，在mybatis-config.xml文件中进行配置：</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></pre></td><td class="code"><pre><span class="line"><span class="comment">&lt;!--放在properties标签后面--&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="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 class="tag">&lt;/<span class="name">settings</span>&gt;</span></span><br><span class="line">/**</span><br><span class="line">* 查询所有Car，启用驼峰命名自动映射</span><br><span class="line">* @return</span><br><span class="line">*/</span><br><span class="line">List<span class="tag">&lt;<span class="name">Car</span>&gt;</span> selectAllByMapUnderscoreToCamelCase();</span><br><span class="line"><span class="tag">&lt;<span class="name">select</span> <span class="attr">id</span>=<span class="string">&quot;selectAllByMapUnderscoreToCamelCase&quot;</span> <span class="attr">resultType</span>=<span class="string">&quot;Car&quot;</span>&gt;</span></span><br><span class="line">  select * from t_car</span><br><span class="line"><span class="tag">&lt;/<span class="name">select</span>&gt;</span></span><br><span class="line">@Test</span><br><span class="line">public void testSelectAllByMapUnderscoreToCamelCase()&#123;</span><br><span class="line">    CarMapper carMapper = SqlSessionUtil.openSession().getMapper(CarMapper.class);</span><br><span class="line">    List<span class="tag">&lt;<span class="name">Car</span>&gt;</span> cars = carMapper.selectAllByMapUnderscoreToCamelCase();</span><br><span class="line">    System.out.println(cars);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>执行结果正常。</p><h1 id="动态SQL"><a href="#动态SQL" class="headerlink" title="动态SQL"></a>动态SQL</h1><p>有的业务场景也需要SQL语句进行动态拼接，例如：</p><ul><li>批量删除时，根据用户选择的id不同删除不同数据。</li></ul><div class="img-wrap"><div class="img-bg"><img class="img" src="https://raw.githubusercontent.com/silvan2077/markdown_pic/main/Picgo/20251023143918.png"/></div></div><figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">delete</span> <span class="keyword">from</span> t_car <span class="keyword">where</span> id <span class="keyword">in</span>(<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="number">6</span>,......这里的值是动态的，根据用户选择的id不同，值是不同的);</span><br></pre></td></tr></table></figure><ul><li>多条件查询时，根据用户选择的条件不同，查询不同数据。</li></ul><div class="img-wrap"><div class="img-bg"><img class="img" src="https://raw.githubusercontent.com/silvan2077/markdown_pic/main/Picgo/20251023144017.png"/></div></div><figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">select</span> <span class="operator">*</span> <span class="keyword">from</span> t_car <span class="keyword">where</span> brand <span class="keyword">like</span> <span class="string">&#x27;丰田%&#x27;</span> <span class="keyword">and</span> guide_price <span class="operator">&gt;</span> <span class="number">30</span> <span class="keyword">and</span> .....;</span><br></pre></td></tr></table></figure><h2 id="if标签"><a href="#if标签" class="headerlink" title="if标签"></a>if标签</h2><div class="note info no-icon flat"><p>需求：多条件查询。</p></div><p>可能的条件包括：品牌（brand）、指导价格（guide_price）、汽车类型（car_type）</p><p>先初步尝试一下编写SQL语句：</p><div class="tabs" id="if-标签"><ul class="nav-tabs"><li class="tab active"><button type="button" data-href="#if-标签-1">CarMapper接口</button></li><li class="tab"><button type="button" data-href="#if-标签-2">CarMapper.xml</button></li><li class="tab"><button type="button" data-href="#if-标签-3">测试程序</button></li></ul><div class="tab-contents"><div class="tab-item-content active" id="if-标签-1"><figure class="highlight java"><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="keyword">public</span> <span class="keyword">interface</span> <span class="title class_">CarMapper</span> &#123;</span><br><span class="line">    List&lt;Car&gt; <span class="title function_">selectByMultiCondition</span><span class="params">(<span class="meta">@Param(&quot;brand&quot;)</span> String brand, <span class="meta">@Param(&quot;guidePrice&quot;)</span> Double guidePrice, <span class="meta">@Param(&quot;carType&quot;)</span> String carType)</span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><button type="button" class="tab-to-top" aria-label="scroll to top"><i class="fas fa-arrow-up"></i></button></div><div class="tab-item-content" id="if-标签-2"><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></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></span><br><span class="line"><span class="meta">        <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="tag">&lt;<span class="name">mapper</span> <span class="attr">namespace</span>=<span class="string">&quot;com.powernode.mybatis.mapper.CarMapper&quot;</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;selectByMultiCondition&quot;</span> <span class="attr">resultType</span>=<span class="string">&quot;car&quot;</span>&gt;</span></span><br><span class="line">        select * from t_car where</span><br><span class="line">        <span class="tag">&lt;<span class="name">if</span> <span class="attr">test</span>=<span class="string">&quot;brand != null and brand != &#x27;&#x27;&quot;</span>&gt;</span></span><br><span class="line">            brand like #&#123;brand&#125;&quot;%&quot;</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;guidePrice != null and guidePrice != &#x27;&#x27;&quot;</span>&gt;</span></span><br><span class="line">            and guide_price &gt;= #&#123;guidePrice&#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;carType != null and carType != &#x27;&#x27;&quot;</span>&gt;</span></span><br><span class="line">            and car_type = #&#123;carType&#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><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><button type="button" class="tab-to-top" aria-label="scroll to top"><i class="fas fa-arrow-up"></i></button></div><div class="tab-item-content" id="if-标签-3"><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><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_">CarMapperTest</span> &#123;</span><br><span class="line">    <span class="meta">@Test</span></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">testSelectByMultiCondition</span><span class="params">()</span>&#123;</span><br><span class="line">        <span class="type">CarMapper</span> <span class="variable">mapper</span> <span class="operator">=</span> SqlSessionUtil.openSession().getMapper(CarMapper.class);</span><br><span class="line">        List&lt;Car&gt; cars = mapper.selectByMultiCondition(<span class="string">&quot;丰田&quot;</span>, <span class="number">20.0</span>, <span class="string">&quot;燃油车&quot;</span>);</span><br><span class="line">        System.out.println(cars);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><button type="button" class="tab-to-top" aria-label="scroll to top"><i class="fas fa-arrow-up"></i></button></div></div></div><p>执行结果：<br><div class="img-wrap"><div class="img-bg"><img class="img" src="https://raw.githubusercontent.com/silvan2077/markdown_pic/main/Picgo/20251023144748.png"/></div></div></p><p>如果第一个条件为空，剩下两个条件不为空，会是怎样呢,后面两条语句的and还会保留吗？</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">List&lt;Car&gt; cars = mapper.selectByMultiCondition(<span class="string">&quot;&quot;</span>, <span class="number">20.0</span>, <span class="string">&quot;燃油车&quot;</span>);</span><br></pre></td></tr></table></figure><p>执行结果：</p><div class="img-wrap"><div class="img-bg"><img class="img" src="https://raw.githubusercontent.com/silvan2077/markdown_pic/main/Picgo/20251023183215.png"/></div></div><p>发现报错了，说明SQL语法有问题，where后面出现了and。这该怎么解决呢？</p><ul><li>可以where后面添加一个恒成立的条件。</li></ul><div class="img-wrap"><div class="img-bg"><img class="img" src="https://raw.githubusercontent.com/silvan2077/markdown_pic/main/Picgo/20251023183338.png"/></div></div><p>执行结果：</p><div class="img-wrap"><div class="img-bg"><img class="img" src="https://raw.githubusercontent.com/silvan2077/markdown_pic/main/Picgo/20251023183407.png"/></div></div><p>发现如果三个条件都是空，此时能够正常运行。<br><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">List&lt;Car&gt; cars = mapper.selectByMultiCondition(<span class="string">&quot;&quot;</span>, <span class="literal">null</span>, <span class="string">&quot;&quot;</span>);</span><br></pre></td></tr></table></figure></p><p>执行结果：<br><div class="img-wrap"><div class="img-bg"><img class="img" src="https://raw.githubusercontent.com/silvan2077/markdown_pic/main/Picgo/20251023183512.png"/></div></div></p><p>三个条件都不为空呢？</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">List&lt;Car&gt; cars = mapper.selectByMultiCondition(<span class="string">&quot;丰田&quot;</span>, <span class="number">20.0</span>, <span class="string">&quot;燃油车&quot;</span>);</span><br></pre></td></tr></table></figure><p>执行结果：</p><div class="img-wrap"><div class="img-bg"><img class="img" src="https://raw.githubusercontent.com/silvan2077/markdown_pic/main/Picgo/20251023183623.png"/></div></div><h2 id="where标签"><a href="#where标签" class="headerlink" title="where标签"></a>where标签</h2><div class="note info no-icon flat"><p>where标签的作用：让where子句更加动态智能</p><ul><li>所有条件都为空时，where标签保证不会生成where子句。</li><li>自动去除某些条件<strong>前面</strong>多余的and或or。。</li></ul></div><p>继续使用if标签中的需求。</p><div class="tabs" id="where标签"><ul class="nav-tabs"><li class="tab active"><button type="button" data-href="#where标签-1">CarMapper接口</button></li><li class="tab"><button type="button" data-href="#where标签-2">CarMapper.xml</button></li></ul><div class="tab-contents"><div class="tab-item-content active" id="where标签-1"><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><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="comment">* 根据多条件查询Car，使用where标签</span></span><br><span class="line"><span class="comment">* <span class="doctag">@param</span> brand</span></span><br><span class="line"><span class="comment">* <span class="doctag">@param</span> guidePrice</span></span><br><span class="line"><span class="comment">* <span class="doctag">@param</span> carType</span></span><br><span class="line"><span class="comment">* <span class="doctag">@return</span></span></span><br><span class="line"><span class="comment">*/</span></span><br><span class="line">List&lt;Car&gt; <span class="title function_">selectByMultiConditionWithWhere</span><span class="params">(<span class="meta">@Param(&quot;brand&quot;)</span> String brand, <span class="meta">@Param(&quot;guidePrice&quot;)</span> Double guidePrice, <span class="meta">@Param(&quot;carType&quot;)</span> String carType)</span>;</span><br></pre></td></tr></table></figure><button type="button" class="tab-to-top" aria-label="scroll to top"><i class="fas fa-arrow-up"></i></button></div><div class="tab-item-content" id="where标签-2"><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></pre></td><td class="code"><pre><span class="line"><span class="tag">&lt;<span class="name">select</span> <span class="attr">id</span>=<span class="string">&quot;selectByMultiConditionWithWhere&quot;</span> <span class="attr">resultType</span>=<span class="string">&quot;car&quot;</span>&gt;</span></span><br><span class="line">  select * from t_car</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;brand != null and brand != &#x27;&#x27;&quot;</span>&gt;</span></span><br><span class="line">      and brand like #&#123;brand&#125;&quot;%&quot;</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;guidePrice != null and guidePrice != &#x27;&#x27;&quot;</span>&gt;</span></span><br><span class="line">      and guide_price &gt;= #&#123;guidePrice&#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;carType != null and carType != &#x27;&#x27;&quot;</span>&gt;</span></span><br><span class="line">      and car_type = #&#123;carType&#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 class="tag">&lt;/<span class="name">select</span>&gt;</span></span><br></pre></td></tr></table></figure><button type="button" class="tab-to-top" aria-label="scroll to top"><i class="fas fa-arrow-up"></i></button></div></div></div><p>运行结果：</p><div class="img-wrap"><div class="img-bg"><img class="img" src="https://raw.githubusercontent.com/silvan2077/markdown_pic/main/Picgo/20251023184627.png"/></div></div><p>如果所有条件都是空呢？</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">List&lt;Car&gt; cars = mapper.selectByMultiConditionWithWhere(<span class="string">&quot;&quot;</span>, <span class="literal">null</span>, <span class="string">&quot;&quot;</span>);</span><br></pre></td></tr></table></figure><p>运行结果：<br><div class="img-wrap"><div class="img-bg"><img class="img" src="https://raw.githubusercontent.com/silvan2077/markdown_pic/main/Picgo/20251023184706.png"/></div></div></p><p>它可以自动去掉前面多余的and，那可以自动去掉后面多余的and吗？</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></pre></td><td class="code"><pre><span class="line"><span class="tag">&lt;<span class="name">select</span> <span class="attr">id</span>=<span class="string">&quot;selectByMultiConditionWithWhere&quot;</span> <span class="attr">resultType</span>=<span class="string">&quot;car&quot;</span>&gt;</span></span><br><span class="line">  select * from t_car</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;brand != null and brand != &#x27;&#x27;&quot;</span>&gt;</span></span><br><span class="line">      brand like #&#123;brand&#125;&quot;%&quot; and</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;guidePrice != null and guidePrice != &#x27;&#x27;&quot;</span>&gt;</span></span><br><span class="line">      guide_price &gt;= #&#123;guidePrice&#125; and</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;carType != null and carType != &#x27;&#x27;&quot;</span>&gt;</span></span><br><span class="line">      car_type = #&#123;carType&#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 class="tag">&lt;/<span class="name">select</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></pre></td><td class="code"><pre><span class="line"><span class="comment">// 让最后一个条件为空</span></span><br><span class="line">List&lt;Car&gt; cars = mapper.selectByMultiConditionWithWhere(<span class="string">&quot;丰田&quot;</span>, <span class="number">20.0</span>, <span class="string">&quot;&quot;</span>);</span><br></pre></td></tr></table></figure><p>运行结果：<br><div class="img-wrap"><div class="img-bg"><img class="img" src="https://raw.githubusercontent.com/silvan2077/markdown_pic/main/Picgo/20251023185032.png"/></div></div></p><p>很显然，后面多余的and是不会被去除的。</p><h2 id="trim标签"><a href="#trim标签" class="headerlink" title="trim标签"></a>trim标签</h2><p>trim标签的属性：</p><ul><li>prefix：在trim标签中的语句前<strong>添加</strong>内容</li><li>suffix：在trim标签中的语句后<strong>添加</strong>内容</li><li>prefixOverrides：前缀<strong>覆盖掉（去掉）</strong></li><li>suffixOverrides：后缀<strong>覆盖掉（去掉）</strong></li></ul><div class="tabs" id="trim标签"><ul class="nav-tabs"><li class="tab active"><button type="button" data-href="#trim标签-1">CarMapper接口</button></li><li class="tab"><button type="button" data-href="#trim标签-2">CarMapper.xml</button></li><li class="tab"><button type="button" data-href="#trim标签-3">测试程序</button></li></ul><div class="tab-contents"><div class="tab-item-content active" id="trim标签-1"><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">List&lt;Car&gt; <span class="title function_">selectByMultiConditionWithTrim</span><span class="params">(<span class="meta">@Param(&quot;brand&quot;)</span> String brand, <span class="meta">@Param(&quot;guidePrice&quot;)</span> Double guidePrice, <span class="meta">@Param(&quot;carType&quot;)</span> String carType)</span>;</span><br></pre></td></tr></table></figure><button type="button" class="tab-to-top" aria-label="scroll to top"><i class="fas fa-arrow-up"></i></button></div><div class="tab-item-content" id="trim标签-2"><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></pre></td><td class="code"><pre><span class="line"><span class="tag">&lt;<span class="name">select</span> <span class="attr">id</span>=<span class="string">&quot;selectByMultiConditionWithTrim&quot;</span> <span class="attr">resultType</span>=<span class="string">&quot;car&quot;</span>&gt;</span></span><br><span class="line">  select * from t_car</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">suffixOverrides</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;brand != null and brand != &#x27;&#x27;&quot;</span>&gt;</span></span><br><span class="line">      brand like #&#123;brand&#125;&quot;%&quot; and</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;guidePrice != null and guidePrice != &#x27;&#x27;&quot;</span>&gt;</span></span><br><span class="line">      guide_price &gt;= #&#123;guidePrice&#125; and</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;carType != null and carType != &#x27;&#x27;&quot;</span>&gt;</span></span><br><span class="line">      car_type = #&#123;carType&#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">trim</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><button type="button" class="tab-to-top" aria-label="scroll to top"><i class="fas fa-arrow-up"></i></button></div><div class="tab-item-content" id="trim标签-3"><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><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="meta">@Test</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">testSelectByMultiConditionWithTrim</span><span class="params">()</span>&#123;</span><br><span class="line">    <span class="type">CarMapper</span> <span class="variable">mapper</span> <span class="operator">=</span> SqlSessionUtil.openSession().getMapper(CarMapper.class);</span><br><span class="line">    List&lt;Car&gt; cars = mapper.selectByMultiConditionWithTrim(<span class="string">&quot;丰田&quot;</span>, <span class="number">20.0</span>, <span class="string">&quot;&quot;</span>);</span><br><span class="line">    System.out.println(cars);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><button type="button" class="tab-to-top" aria-label="scroll to top"><i class="fas fa-arrow-up"></i></button></div></div></div><p>如果所有条件为空，where会被加上吗？</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">List&lt;Car&gt; cars = mapper.selectByMultiConditionWithTrim(<span class="string">&quot;&quot;</span>, <span class="literal">null</span>, <span class="string">&quot;&quot;</span>);</span><br></pre></td></tr></table></figure><p>执行结果：</p><div class="img-wrap"><div class="img-bg"><img class="img" src="https://raw.githubusercontent.com/silvan2077/markdown_pic/main/Picgo/20251023185312.png"/></div></div><h2 id="set标签"><a href="#set标签" class="headerlink" title="set标签"></a>set标签</h2><p>主要使用在update语句中，用来生成set关键字，同时去掉最后多余的“,”</p><p>比如只更新提交不为空的字段，如果提交的数据是空或者””，那么这个字段我们将不更新。</p><div class="tabs" id="set标签"><ul class="nav-tabs"><li class="tab active"><button type="button" data-href="#set标签-1">CarMapper接口</button></li><li class="tab"><button type="button" data-href="#set标签-2">CarMapper.xml</button></li><li class="tab"><button type="button" data-href="#set标签-3">测试程序</button></li></ul><div class="tab-contents"><div class="tab-item-content active" id="set标签-1"><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="type">int</span> <span class="title function_">updateWithSet</span><span class="params">(Car car)</span>;</span><br></pre></td></tr></table></figure><button type="button" class="tab-to-top" aria-label="scroll to top"><i class="fas fa-arrow-up"></i></button></div><div class="tab-item-content" id="set标签-2"><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></pre></td><td class="code"><pre><span class="line"><span class="tag">&lt;<span class="name">update</span> <span class="attr">id</span>=<span class="string">&quot;updateWithSet&quot;</span>&gt;</span></span><br><span class="line">  update t_car</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;carNum != null and carNum != &#x27;&#x27;&quot;</span>&gt;</span>car_num = #&#123;carNum&#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;brand != null and brand != &#x27;&#x27;&quot;</span>&gt;</span>brand = #&#123;brand&#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;guidePrice != null and guidePrice != &#x27;&#x27;&quot;</span>&gt;</span>guide_price = #&#123;guidePrice&#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;produceTime != null and produceTime != &#x27;&#x27;&quot;</span>&gt;</span>produce_time = #&#123;produceTime&#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;carType != null and carType != &#x27;&#x27;&quot;</span>&gt;</span>car_type = #&#123;carType&#125;,<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></pre></td></tr></table></figure><button type="button" class="tab-to-top" aria-label="scroll to top"><i class="fas fa-arrow-up"></i></button></div><div class="tab-item-content" id="set标签-3"><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><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">@Test</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">testUpdateWithSet</span><span class="params">()</span>&#123;</span><br><span class="line">    <span class="type">CarMapper</span> <span class="variable">mapper</span> <span class="operator">=</span> SqlSessionUtil.openSession().getMapper(CarMapper.class);</span><br><span class="line">    <span class="type">Car</span> <span class="variable">car</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">Car</span>(<span class="number">38L</span>,<span class="string">&quot;1001&quot;</span>,<span class="string">&quot;丰田霸道2&quot;</span>,<span class="number">10.0</span>,<span class="string">&quot;&quot;</span>,<span class="literal">null</span>);</span><br><span class="line">    <span class="type">int</span> <span class="variable">count</span> <span class="operator">=</span> mapper.updateWithSet(car);</span><br><span class="line">    System.out.println(count);</span><br><span class="line">    SqlSessionUtil.openSession().commit();</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><button type="button" class="tab-to-top" aria-label="scroll to top"><i class="fas fa-arrow-up"></i></button></div></div></div><h2 id="choose-when-otherwise"><a href="#choose-when-otherwise" class="headerlink" title="choose when otherwise"></a>choose when otherwise</h2><p>这三个标签是在一起使用的：</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></pre></td><td class="code"><pre><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>&gt;</span><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>&gt;</span><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>&gt;</span><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 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></pre></td></tr></table></figure><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></pre></td><td class="code"><pre><span class="line"><span class="keyword">if</span>()&#123;</span><br><span class="line">    </span><br><span class="line">&#125;<span class="keyword">else</span> <span class="keyword">if</span>()&#123;</span><br><span class="line">    </span><br><span class="line">&#125;<span class="keyword">else</span> <span class="keyword">if</span>()&#123;</span><br><span class="line">    </span><br><span class="line">&#125;<span class="keyword">else</span> <span class="keyword">if</span>()&#123;</span><br><span class="line">    </span><br><span class="line">&#125;<span class="keyword">else</span>&#123;</span><br><span class="line"></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>只有一个分支会被选择！！！！</p><div class="note info no-icon flat"><p>需求：先根据品牌查询，如果没有提供品牌，再根据指导价格查询，如果没有提供指导价格，就根据生产日期查询。</p></div><div class="tabs" id="choose"><ul class="nav-tabs"><li class="tab active"><button type="button" data-href="#choose-1">CarMapper接口</button></li><li class="tab"><button type="button" data-href="#choose-2">CarMapper.xml</button></li></ul><div class="tab-contents"><div class="tab-item-content active" id="choose-1"><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">List&lt;Car&gt; <span class="title function_">selectWithChoose</span><span class="params">(<span class="meta">@Param(&quot;brand&quot;)</span> String brand, <span class="meta">@Param(&quot;guidePrice&quot;)</span> Double guidePrice, <span class="meta">@Param(&quot;produceTime&quot;)</span> String produceTime)</span>;</span><br></pre></td></tr></table></figure><button type="button" class="tab-to-top" aria-label="scroll to top"><i class="fas fa-arrow-up"></i></button></div><div class="tab-item-content" id="choose-2"><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="tag">&lt;<span class="name">select</span> <span class="attr">id</span>=<span class="string">&quot;selectWithChoose&quot;</span> <span class="attr">resultType</span>=<span class="string">&quot;car&quot;</span>&gt;</span></span><br><span class="line">  select * from t_car</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">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;brand != null and brand != &#x27;&#x27;&quot;</span>&gt;</span></span><br><span class="line">        brand like #&#123;brand&#125;&quot;%&quot;</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;guidePrice != null and guidePrice != &#x27;&#x27;&quot;</span>&gt;</span></span><br><span class="line">        guide_price &gt;= #&#123;guidePrice&#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">        produce_time &gt;= #&#123;produceTime&#125;</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 class="tag">&lt;/<span class="name">where</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><button type="button" class="tab-to-top" aria-label="scroll to top"><i class="fas fa-arrow-up"></i></button></div></div></div><div class="img-wrap"><div class="img-bg"><img class="img" src="https://raw.githubusercontent.com/silvan2077/markdown_pic/main/Picgo/20251023202316.png"/></div></div><h2 id="foreach标签"><a href="#foreach标签" class="headerlink" title="foreach标签"></a>foreach标签</h2><p>循环数组或集合，动态生成sql，比如这样的SQL：</p><div class="tabs" id="foreach标签"><ul class="nav-tabs"><li class="tab active"><button type="button" data-href="#foreach标签-1">批量删除</button></li><li class="tab"><button type="button" data-href="#foreach标签-2">批量添加</button></li></ul><div class="tab-contents"><div class="tab-item-content active" id="foreach标签-1"><figure class="highlight sql"><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="keyword">delete</span> <span class="keyword">from</span> t_car <span class="keyword">where</span> id <span class="keyword">in</span>(<span class="number">1</span>,<span class="number">2</span>,<span class="number">3</span>);</span><br><span class="line"><span class="keyword">delete</span> <span class="keyword">from</span> t_car <span class="keyword">where</span> id <span class="operator">=</span> <span class="number">1</span> <span class="keyword">or</span> id <span class="operator">=</span> <span class="number">2</span> <span class="keyword">or</span> id <span class="operator">=</span> <span class="number">3</span>;</span><br></pre></td></tr></table></figure><button type="button" class="tab-to-top" aria-label="scroll to top"><i class="fas fa-arrow-up"></i></button></div><div class="tab-item-content" id="foreach标签-2"><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="keyword">insert into</span> t_car <span class="keyword">values</span></span><br><span class="line">  (<span class="keyword">null</span>,<span class="string">&#x27;1001&#x27;</span>,<span class="string">&#x27;凯美瑞&#x27;</span>,<span class="number">35.0</span>,<span class="string">&#x27;2010-10-11&#x27;</span>,<span class="string">&#x27;燃油车&#x27;</span>),</span><br><span class="line">  (<span class="keyword">null</span>,<span class="string">&#x27;1002&#x27;</span>,<span class="string">&#x27;比亚迪唐&#x27;</span>,<span class="number">31.0</span>,<span class="string">&#x27;2020-11-11&#x27;</span>,<span class="string">&#x27;新能源&#x27;</span>),</span><br><span class="line">  (<span class="keyword">null</span>,<span class="string">&#x27;1003&#x27;</span>,<span class="string">&#x27;比亚迪宋&#x27;</span>,<span class="number">32.0</span>,<span class="string">&#x27;2020-10-11&#x27;</span>,<span class="string">&#x27;新能源&#x27;</span>)</span><br></pre></td></tr></table></figure><button type="button" class="tab-to-top" aria-label="scroll to top"><i class="fas fa-arrow-up"></i></button></div></div></div><h3 id="批量删除-1"><a href="#批量删除-1" class="headerlink" title="批量删除"></a>批量删除</h3><ul><li>用in来删除</li></ul><div class="tabs" id="批量删除-2"><ul class="nav-tabs"><li class="tab active"><button type="button" data-href="#批量删除-2-1">CarMapper接口</button></li><li class="tab"><button type="button" data-href="#批量删除-2-2">CarMapper.xml</button></li><li class="tab"><button type="button" data-href="#批量删除-2-3">测试程序</button></li></ul><div class="tab-contents"><div class="tab-item-content active" id="批量删除-2-1"><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><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="comment">* 通过foreach完成批量删除</span></span><br><span class="line"><span class="comment">* <span class="doctag">@param</span> ids</span></span><br><span class="line"><span class="comment">* <span class="doctag">@return</span></span></span><br><span class="line"><span class="comment">*/</span></span><br><span class="line"><span class="type">int</span> <span class="title function_">deleteBatchByForeach</span><span class="params">(<span class="meta">@Param(&quot;ids&quot;)</span> Long[] ids)</span>;</span><br></pre></td></tr></table></figure><button type="button" class="tab-to-top" aria-label="scroll to top"><i class="fas fa-arrow-up"></i></button></div><div class="tab-item-content" id="批量删除-2-2"><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="tag">&lt;<span class="name">delete</span> <span class="attr">id</span>=<span class="string">&quot;deleteBatchByForeach&quot;</span>&gt;</span></span><br><span class="line">  delete from t_car 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">separator</span>=<span class="string">&quot;,&quot;</span> <span class="attr">open</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></pre></td></tr></table></figure><button type="button" class="tab-to-top" aria-label="scroll to top"><i class="fas fa-arrow-up"></i></button></div><div class="tab-item-content" id="批量删除-2-3"><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><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="meta">@Test</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">testDeleteBatchByForeach</span><span class="params">()</span>&#123;</span><br><span class="line">    <span class="type">CarMapper</span> <span class="variable">mapper</span> <span class="operator">=</span> SqlSessionUtil.openSession().getMapper(CarMapper.class);</span><br><span class="line">    <span class="type">int</span> <span class="variable">count</span> <span class="operator">=</span> mapper.deleteBatchByForeach(<span class="keyword">new</span> <span class="title class_">Long</span>[]&#123;<span class="number">40L</span>, <span class="number">41L</span>, <span class="number">42L</span>&#125;);</span><br><span class="line">    System.out.println(<span class="string">&quot;删除了几条记录：&quot;</span> + count);</span><br><span class="line">    SqlSessionUtil.openSession().commit();</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><button type="button" class="tab-to-top" aria-label="scroll to top"><i class="fas fa-arrow-up"></i></button></div></div></div><p>执行结果：<br><div class="img-wrap"><div class="img-bg"><img class="img" src="https://raw.githubusercontent.com/silvan2077/markdown_pic/main/Picgo/20251023202719.png"/></div></div></p><ul><li>用or来删除</li></ul><div class="tabs" id="批量删除1-2"><ul class="nav-tabs"><li class="tab active"><button type="button" data-href="#批量删除1-2-1">CarMapper接口</button></li><li class="tab"><button type="button" data-href="#批量删除1-2-2">CarMapper.xml</button></li><li class="tab"><button type="button" data-href="#批量删除1-2-3">测试程序</button></li></ul><div class="tab-contents"><div class="tab-item-content active" id="批量删除1-2-1"><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><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="comment">* 通过foreach完成批量删除</span></span><br><span class="line"><span class="comment">* <span class="doctag">@param</span> ids</span></span><br><span class="line"><span class="comment">* <span class="doctag">@return</span></span></span><br><span class="line"><span class="comment">*/</span></span><br><span class="line"><span class="type">int</span> <span class="title function_">deleteBatchByForeach2</span><span class="params">(<span class="meta">@Param(&quot;ids&quot;)</span> Long[] ids)</span>;</span><br></pre></td></tr></table></figure><button type="button" class="tab-to-top" aria-label="scroll to top"><i class="fas fa-arrow-up"></i></button></div><div class="tab-item-content" id="批量删除1-2-2"><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="tag">&lt;<span class="name">delete</span> <span class="attr">id</span>=<span class="string">&quot;deleteBatchByForeach2&quot;</span>&gt;</span></span><br><span class="line">  delete from t_car where</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">separator</span>=<span class="string">&quot;or&quot;</span>&gt;</span></span><br><span class="line">    id = #&#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></pre></td></tr></table></figure><button type="button" class="tab-to-top" aria-label="scroll to top"><i class="fas fa-arrow-up"></i></button></div><div class="tab-item-content" id="批量删除1-2-3"><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><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="meta">@Test</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">testDeleteBatchByForeach2</span><span class="params">()</span>&#123;</span><br><span class="line">    <span class="type">CarMapper</span> <span class="variable">mapper</span> <span class="operator">=</span> SqlSessionUtil.openSession().getMapper(CarMapper.class);</span><br><span class="line">    <span class="type">int</span> <span class="variable">count</span> <span class="operator">=</span> mapper.deleteBatchByForeach2(<span class="keyword">new</span> <span class="title class_">Long</span>[]&#123;<span class="number">40L</span>, <span class="number">41L</span>, <span class="number">42L</span>&#125;);</span><br><span class="line">    System.out.println(<span class="string">&quot;删除了几条记录：&quot;</span> + count);</span><br><span class="line">    SqlSessionUtil.openSession().commit();</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><button type="button" class="tab-to-top" aria-label="scroll to top"><i class="fas fa-arrow-up"></i></button></div></div></div><p>执行结果：</p><div class="img-wrap"><div class="img-bg"><img class="img" src="https://raw.githubusercontent.com/silvan2077/markdown_pic/main/Picgo/20251023202859.png"/></div></div><h3 id="批量添加"><a href="#批量添加" class="headerlink" title="批量添加"></a>批量添加</h3><div class="tabs" id="批量添加1-2"><ul class="nav-tabs"><li class="tab active"><button type="button" data-href="#批量添加1-2-1">CarMapper接口</button></li><li class="tab"><button type="button" data-href="#批量添加1-2-2">CarMapper.xml</button></li><li class="tab"><button type="button" data-href="#批量添加1-2-3">测试代码</button></li></ul><div class="tab-contents"><div class="tab-item-content active" id="批量添加1-2-1"><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="type">int</span> <span class="title function_">insertBatchByForeach</span><span class="params">(<span class="meta">@Param(&quot;cars&quot;)</span> List&lt;Car&gt; cars)</span>;</span><br></pre></td></tr></table></figure><button type="button" class="tab-to-top" aria-label="scroll to top"><i class="fas fa-arrow-up"></i></button></div><div class="tab-item-content" id="批量添加1-2-2"><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="tag">&lt;<span class="name">insert</span> <span class="attr">id</span>=<span class="string">&quot;insertBatchByForeach&quot;</span>&gt;</span></span><br><span class="line">  insert into t_car values </span><br><span class="line">  <span class="tag">&lt;<span class="name">foreach</span> <span class="attr">collection</span>=<span class="string">&quot;cars&quot;</span> <span class="attr">item</span>=<span class="string">&quot;car&quot;</span> <span class="attr">separator</span>=<span class="string">&quot;,&quot;</span>&gt;</span></span><br><span class="line">    (null,#&#123;car.carNum&#125;,#&#123;car.brand&#125;,#&#123;car.guidePrice&#125;,#&#123;car.produceTime&#125;,#&#123;car.carType&#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></pre></td></tr></table></figure><button type="button" class="tab-to-top" aria-label="scroll to top"><i class="fas fa-arrow-up"></i></button></div><div class="tab-item-content" id="批量添加1-2-3"><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><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="meta">@Test</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">testInsertBatchByForeach</span><span class="params">()</span>&#123;</span><br><span class="line">    <span class="type">CarMapper</span> <span class="variable">mapper</span> <span class="operator">=</span> SqlSessionUtil.openSession().getMapper(CarMapper.class);</span><br><span class="line">    <span class="type">Car</span> <span class="variable">car1</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">Car</span>(<span class="literal">null</span>, <span class="string">&quot;2001&quot;</span>, <span class="string">&quot;兰博基尼&quot;</span>, <span class="number">100.0</span>, <span class="string">&quot;1998-10-11&quot;</span>, <span class="string">&quot;燃油车&quot;</span>);</span><br><span class="line">    <span class="type">Car</span> <span class="variable">car2</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">Car</span>(<span class="literal">null</span>, <span class="string">&quot;2001&quot;</span>, <span class="string">&quot;兰博基尼&quot;</span>, <span class="number">100.0</span>, <span class="string">&quot;1998-10-11&quot;</span>, <span class="string">&quot;燃油车&quot;</span>);</span><br><span class="line">    <span class="type">Car</span> <span class="variable">car3</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">Car</span>(<span class="literal">null</span>, <span class="string">&quot;2001&quot;</span>, <span class="string">&quot;兰博基尼&quot;</span>, <span class="number">100.0</span>, <span class="string">&quot;1998-10-11&quot;</span>, <span class="string">&quot;燃油车&quot;</span>);</span><br><span class="line">    List&lt;Car&gt; cars = Arrays.asList(car1, car2, car3);</span><br><span class="line">    <span class="type">int</span> <span class="variable">count</span> <span class="operator">=</span> mapper.insertBatchByForeach(cars);</span><br><span class="line">    System.out.println(<span class="string">&quot;插入了几条记录&quot;</span> + count);</span><br><span class="line">    SqlSessionUtil.openSession().commit();</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><button type="button" class="tab-to-top" aria-label="scroll to top"><i class="fas fa-arrow-up"></i></button></div></div></div><p>执行结果：<br><div class="img-wrap"><div class="img-bg"><img class="img" src="https://raw.githubusercontent.com/silvan2077/markdown_pic/main/Picgo/20251023203312.png"/></div></div></p><h2 id="sql标签与include标签"><a href="#sql标签与include标签" class="headerlink" title="sql标签与include标签"></a>sql标签与include标签</h2><p><code>sql标签</code>用来声明sql片段<br><code>include标签</code>用来将声明的sql片段包含到某个sql语句当中<br>作用：代码复用。易维护。</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></pre></td><td class="code"><pre><span class="line"><span class="tag">&lt;<span class="name">sql</span> <span class="attr">id</span>=<span class="string">&quot;carCols&quot;</span>&gt;</span>id,car_num carNum,brand,guide_price guidePrice,produce_time produceTime,car_type carType<span class="tag">&lt;/<span class="name">sql</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;selectAllRetMap&quot;</span> <span class="attr">resultType</span>=<span class="string">&quot;map&quot;</span>&gt;</span></span><br><span class="line">  select <span class="tag">&lt;<span class="name">include</span> <span class="attr">refid</span>=<span class="string">&quot;carCols&quot;</span>/&gt;</span> from t_car</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">select</span> <span class="attr">id</span>=<span class="string">&quot;selectAllRetListMap&quot;</span> <span class="attr">resultType</span>=<span class="string">&quot;map&quot;</span>&gt;</span></span><br><span class="line">  select <span class="tag">&lt;<span class="name">include</span> <span class="attr">refid</span>=<span class="string">&quot;carCols&quot;</span>/&gt;</span> carType from t_car</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">select</span> <span class="attr">id</span>=<span class="string">&quot;selectByIdRetMap&quot;</span> <span class="attr">resultType</span>=<span class="string">&quot;map&quot;</span>&gt;</span></span><br><span class="line">  select <span class="tag">&lt;<span class="name">include</span> <span class="attr">refid</span>=<span class="string">&quot;carCols&quot;</span>/&gt;</span> from t_car where 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><h1 id="MyBatis的高级映射及延迟加载"><a href="#MyBatis的高级映射及延迟加载" class="headerlink" title="MyBatis的高级映射及延迟加载"></a>MyBatis的高级映射及延迟加载</h1><h2 id="环境准备-1"><a href="#环境准备-1" class="headerlink" title="环境准备"></a>环境准备</h2><p>依赖：mybatis依赖、mysql驱动依赖、junit依赖、logback依赖</p><p>配置文件：mybatis-config.xml、logback.xml、jdbc.properties</p><p>拷贝工具类：SqlSessionUtil</p><p>准备数据库表：一个班级对应多个学生。班级表：t_clazz。学生表：t_student</p><p><img src="瑞吉外卖项目实战说明/1704781490591-2940ead2-4686-4774-ad12-55fbd29859f7.png" alt="img"></p><p><img src="瑞吉外卖项目实战说明/1661132819754-23adfa1c-b325-41bc-8520-0355fe4c41cc.png" alt="img"></p><p><img src="https://cdn.nlark.com/yuque/0/2024/png/22016332/1704781512657-8485057a-2c28-4df3-a5df-a800804888f7.png" alt="img"></p><p><img src="瑞吉外卖项目实战说明/1661132610940-7d55793b-df68-48f4-924f-99db58220da9.png" alt="img"></p><p>创建pojo：Student、Clazz</p><div class="tabs" id="创建pojo"><ul class="nav-tabs"><li class="tab active"><button type="button" data-href="#创建pojo-1">Student</button></li><li class="tab"><button type="button" data-href="#创建pojo-2">Clazz</button></li></ul><div class="tab-contents"><div class="tab-item-content active" id="创建pojo-1"><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><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_">Student</span> &#123;</span><br><span class="line">    <span class="keyword">private</span> Integer sid;</span><br><span class="line">    <span class="keyword">private</span> String sname;</span><br><span class="line">    <span class="comment">//......</span></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><button type="button" class="tab-to-top" aria-label="scroll to top"><i class="fas fa-arrow-up"></i></button></div><div class="tab-item-content" id="创建pojo-2"><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><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_">Clazz</span> &#123;</span><br><span class="line">     <span class="keyword">private</span> Integer cid;</span><br><span class="line">    <span class="keyword">private</span> String cname;</span><br><span class="line">    <span class="comment">//......</span></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><button type="button" class="tab-to-top" aria-label="scroll to top"><i class="fas fa-arrow-up"></i></button></div></div></div><p>创建mapper接口：StudentMapper、ClazzMapper</p><div class="tabs" id="创建mapper接口"><ul class="nav-tabs"><li class="tab active"><button type="button" data-href="#创建mapper接口-1">StudentMapper</button></li><li class="tab"><button type="button" data-href="#创建mapper接口-2">ClazzMapper</button></li></ul><div class="tab-contents"><div class="tab-item-content active" id="创建mapper接口-1"><figure class="highlight java"><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="keyword">public</span> <span class="keyword">interface</span> <span class="title class_">StudentMapper</span> &#123;</span><br><span class="line">    Student <span class="title function_">selectBySid</span><span class="params">(Integer id)</span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><button type="button" class="tab-to-top" aria-label="scroll to top"><i class="fas fa-arrow-up"></i></button></div><div class="tab-item-content" id="创建mapper接口-2"><figure class="highlight java"><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="keyword">public</span> <span class="keyword">interface</span> <span class="title class_">ClazzMapper</span> &#123;</span><br><span class="line">    Clazz <span class="title function_">selectByCid</span><span class="params">(Integer id)</span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><button type="button" class="tab-to-top" aria-label="scroll to top"><i class="fas fa-arrow-up"></i></button></div></div></div><p>创建mapper映射文件：StudentMapper.xml、ClazzMapper.xml</p><h2 id="多对一"><a href="#多对一" class="headerlink" title="多对一"></a>多对一</h2><p>多种方式，常见的包括三种：</p><ul><li>第一种方式：一条SQL语句，级联属性映射。</li><li>第二种方式：一条SQL语句，association。</li><li>第三种方式：两条SQL语句，分步查询。（这种方式常用：优点一是可复用。优点二是支持懒加载。）</li></ul><p><strong>多对一的理解</strong></p><div class="img-wrap"><div class="img-bg"><img class="img" src="https://raw.githubusercontent.com/silvan2077/markdown_pic/main/Picgo/20251024170351.png"/></div></div><h3 id="第一种方式：级联属性映射"><a href="#第一种方式：级联属性映射" class="headerlink" title="第一种方式：级联属性映射"></a>第一种方式：级联属性映射</h3><p>pojo类Student中添加一个属性：Clazz clazz; 表示学生关联的班级对象。</p><div class="tabs" id="级联属性映射"><ul class="nav-tabs"><li class="tab active"><button type="button" data-href="#级联属性映射-1">Student</button></li><li class="tab"><button type="button" data-href="#级联属性映射-2">StudentMapper.xml</button></li><li class="tab"><button type="button" data-href="#级联属性映射-3">测试程序</button></li></ul><div class="tab-contents"><div class="tab-item-content active" id="级联属性映射-1"><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><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="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 class="keyword">private</span> Integer sid;</span><br><span class="line">    <span class="keyword">private</span> String sname;</span><br><span class="line">    <span class="keyword">private</span> Clazz clazz;</span><br><span class="line"></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><button type="button" class="tab-to-top" aria-label="scroll to top"><i class="fas fa-arrow-up"></i></button></div><div class="tab-item-content" id="级联属性映射-2"><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="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></span><br><span class="line"><span class="meta">        <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="tag">&lt;<span class="name">mapper</span> <span class="attr">namespace</span>=<span class="string">&quot;com.powernode.mybatis.mapper.StudentMapper&quot;</span>&gt;</span></span><br><span class="line"></span><br><span class="line">    <span class="tag">&lt;<span class="name">resultMap</span> <span class="attr">id</span>=<span class="string">&quot;studentResultMap&quot;</span> <span class="attr">type</span>=<span class="string">&quot;Student&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;sid&quot;</span> <span class="attr">column</span>=<span class="string">&quot;sid&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;sname&quot;</span> <span class="attr">column</span>=<span class="string">&quot;sname&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;clazz.cid&quot;</span> <span class="attr">column</span>=<span class="string">&quot;cid&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;clazz.cname&quot;</span> <span class="attr">column</span>=<span class="string">&quot;cname&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;selectBySid&quot;</span> <span class="attr">resultMap</span>=<span class="string">&quot;studentResultMap&quot;</span>&gt;</span></span><br><span class="line">        select s.*, c.* from t_student s join t_clazz c on s.cid = c.cid where sid = #&#123;sid&#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="tag">&lt;/<span class="name">mapper</span>&gt;</span></span><br></pre></td></tr></table></figure><button type="button" class="tab-to-top" aria-label="scroll to top"><i class="fas fa-arrow-up"></i></button></div><div class="tab-item-content" id="级联属性映射-3"><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><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_">StudentMapperTest</span> &#123;</span><br><span class="line">    <span class="meta">@Test</span></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">testSelectBySid</span><span class="params">()</span>&#123;</span><br><span class="line">        <span class="type">StudentMapper</span> <span class="variable">mapper</span> <span class="operator">=</span> SqlSessionUtil.openSession().getMapper(StudentMapper.class);</span><br><span class="line">        <span class="type">Student</span> <span class="variable">student</span> <span class="operator">=</span> mapper.selectBySid(<span class="number">1</span>);</span><br><span class="line">        System.out.println(student);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><button type="button" class="tab-to-top" aria-label="scroll to top"><i class="fas fa-arrow-up"></i></button></div></div></div><p>执行结果：<br><div class="img-wrap"><div class="img-bg"><img class="img" src="https://raw.githubusercontent.com/silvan2077/markdown_pic/main/Picgo/20251024170808.png"/></div></div></p><h3 id="第二种方式：association"><a href="#第二种方式：association" class="headerlink" title="第二种方式：association"></a>第二种方式：association</h3><p>其他位置都不需要修改，只需要修改resultMap中的配置：association即可。</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></pre></td><td class="code"><pre><span class="line"><span class="tag">&lt;<span class="name">resultMap</span> <span class="attr">id</span>=<span class="string">&quot;studentResultMap&quot;</span> <span class="attr">type</span>=<span class="string">&quot;Student&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;sid&quot;</span> <span class="attr">column</span>=<span class="string">&quot;sid&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;sname&quot;</span> <span class="attr">column</span>=<span class="string">&quot;sname&quot;</span>/&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;clazz&quot;</span> <span class="attr">javaType</span>=<span class="string">&quot;Clazz&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;cid&quot;</span> <span class="attr">column</span>=<span class="string">&quot;cid&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;cname&quot;</span> <span class="attr">column</span>=<span class="string">&quot;cname&quot;</span>/&gt;</span></span><br><span class="line">  <span class="tag">&lt;/<span class="name">association</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><p>association翻译为：关联。<br>学生对象关联一个班级对象。</p><h3 id="第三种方式：分步查询"><a href="#第三种方式：分步查询" class="headerlink" title="第三种方式：分步查询"></a>第三种方式：分步查询</h3><p>其他位置不需要修改，只需要修改以及添加以下三处：</p><p>第一处：association中select位置填写sqlId。sqlId=namespace+id。其中column属性作为这条子sql语句的条件。</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></pre></td><td class="code"><pre><span class="line"><span class="tag">&lt;<span class="name">resultMap</span> <span class="attr">id</span>=<span class="string">&quot;studentResultMap&quot;</span> <span class="attr">type</span>=<span class="string">&quot;Student&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;sid&quot;</span> <span class="attr">column</span>=<span class="string">&quot;sid&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;sname&quot;</span> <span class="attr">column</span>=<span class="string">&quot;sname&quot;</span>/&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;clazz&quot;</span></span></span><br><span class="line"><span class="tag">               <span class="attr">select</span>=<span class="string">&quot;com.powernode.mybatis.mapper.ClazzMapper.selectByCid&quot;</span></span></span><br><span class="line"><span class="tag">               <span class="attr">column</span>=<span class="string">&quot;cid&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;selectBySid&quot;</span> <span class="attr">resultMap</span>=<span class="string">&quot;studentResultMap&quot;</span>&gt;</span></span><br><span class="line">  select s.* from t_student s where sid = #&#123;sid&#125;</span><br><span class="line"><span class="tag">&lt;/<span class="name">select</span>&gt;</span></span><br></pre></td></tr></table></figure><p>第二处：在ClazzMapper接口中添加方法</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></pre></td><td class="code"><pre><span class="line"><span class="keyword">package</span> com.powernode.mybatis.mapper;</span><br><span class="line"></span><br><span class="line"><span class="keyword">import</span> com.powernode.mybatis.pojo.Clazz;</span><br><span class="line"></span><br><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * Clazz映射器接口</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@author</span> 老杜</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@version</span> 1.0</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@since</span> 1.0</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_">ClazzMapper</span> &#123;</span><br><span class="line"></span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     * 根据cid获取Clazz信息</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@param</span> cid</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@return</span></span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    Clazz <span class="title function_">selectByCid</span><span class="params">(Integer cid)</span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>第三处：在ClazzMapper.xml文件中进行配置</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="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></span><br><span class="line"><span class="meta">        <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="tag">&lt;<span class="name">mapper</span> <span class="attr">namespace</span>=<span class="string">&quot;com.powernode.mybatis.mapper.ClazzMapper&quot;</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;selectByCid&quot;</span> <span class="attr">resultType</span>=<span class="string">&quot;Clazz&quot;</span>&gt;</span></span><br><span class="line">        select * from t_clazz where cid = #&#123;cid&#125;</span><br><span class="line">    <span class="tag">&lt;/<span class="name">select</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;/<span class="name">mapper</span>&gt;</span></span><br></pre></td></tr></table></figure><p>执行结果，可以很明显看到先后有两条sql语句执行：</p><p><img src="瑞吉外卖项目实战说明/1661151746372-e3c91810-bb5f-4308-8dd5-e421e11bff50.png" alt="img"></p><p>分步优点：</p><ul><li>第一个优点：代码复用性增强。</li><li>第二个优点：支持延迟加载。【暂时访问不到的数据可以先不查询。提高程序的执行效率。】</li></ul><h2 id="13-2-多对一延迟加载"><a href="#13-2-多对一延迟加载" class="headerlink" title="13.2 多对一延迟加载"></a>13.2 多对一延迟加载</h2><p>要想支持延迟加载，非常简单，只需要在association标签中添加fetchType=”lazy”即可。</p><p>修改StudentMapper.xml文件：</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></pre></td><td class="code"><pre><span class="line"><span class="tag">&lt;<span class="name">resultMap</span> <span class="attr">id</span>=<span class="string">&quot;studentResultMap&quot;</span> <span class="attr">type</span>=<span class="string">&quot;Student&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;sid&quot;</span> <span class="attr">column</span>=<span class="string">&quot;sid&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;sname&quot;</span> <span class="attr">column</span>=<span class="string">&quot;sname&quot;</span>/&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;clazz&quot;</span></span></span><br><span class="line"><span class="tag">               <span class="attr">select</span>=<span class="string">&quot;com.powernode.mybatis.mapper.ClazzMapper.selectByCid&quot;</span></span></span><br><span class="line"><span class="tag">               <span class="attr">column</span>=<span class="string">&quot;cid&quot;</span></span></span><br><span class="line"><span class="tag">               <span class="attr">fetchType</span>=<span class="string">&quot;lazy&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><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></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">StudentMapperTest</span> &#123;</span><br><span class="line">    <span class="meta">@Test</span></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">testSelectBySid</span><span class="params">()</span>&#123;</span><br><span class="line">        <span class="type">StudentMapper</span> <span class="variable">mapper</span> <span class="operator">=</span> SqlSessionUtil.openSession().getMapper(StudentMapper.class);</span><br><span class="line">        <span class="type">Student</span> <span class="variable">student</span> <span class="operator">=</span> mapper.selectBySid(<span class="number">1</span>);</span><br><span class="line">        <span class="comment">//System.out.println(student);</span></span><br><span class="line">        <span class="comment">// 只获取学生姓名</span></span><br><span class="line">        <span class="type">String</span> <span class="variable">sname</span> <span class="operator">=</span> student.getSname();</span><br><span class="line">        System.out.println(<span class="string">&quot;学生姓名：&quot;</span> + sname);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p><img src="https://cdn.nlark.com/yuque/0/2022/png/21376908/1661151882965-633f8039-c309-4657-8ed1-44f836d5b1e4.png?x-oss-process=image%2Fwatermark%2Ctype_d3F5LW1pY3JvaGVp%2Csize_23%2Ctext_5Yqo5Yqb6IqC54K5%2Ccolor_FFFFFF%2Cshadow_50%2Ct_80%2Cg_se%2Cx_10%2Cy_10" alt="img"></p><p>如果后续需要使用到学生所在班级的名称，这个时候才会执行关联的sql语句，修改测试程序：</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></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">StudentMapperTest</span> &#123;</span><br><span class="line">    <span class="meta">@Test</span></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">testSelectBySid</span><span class="params">()</span>&#123;</span><br><span class="line">        <span class="type">StudentMapper</span> <span class="variable">mapper</span> <span class="operator">=</span> SqlSessionUtil.openSession().getMapper(StudentMapper.class);</span><br><span class="line">        <span class="type">Student</span> <span class="variable">student</span> <span class="operator">=</span> mapper.selectBySid(<span class="number">1</span>);</span><br><span class="line">        <span class="comment">//System.out.println(student);</span></span><br><span class="line">        <span class="comment">// 只获取学生姓名</span></span><br><span class="line">        <span class="type">String</span> <span class="variable">sname</span> <span class="operator">=</span> student.getSname();</span><br><span class="line">        System.out.println(<span class="string">&quot;学生姓名：&quot;</span> + sname);</span><br><span class="line">        <span class="comment">// 到这里之后，想获取班级名字了</span></span><br><span class="line">        <span class="type">String</span> <span class="variable">cname</span> <span class="operator">=</span> student.getClazz().getCname();</span><br><span class="line">        System.out.println(<span class="string">&quot;学生的班级名称：&quot;</span> + cname);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p><img src="https://cdn.nlark.com/yuque/0/2022/png/21376908/1661151926961-a0affa2d-2d89-4b67-8cc0-bf8604ded4fc.png?x-oss-process=image%2Fwatermark%2Ctype_d3F5LW1pY3JvaGVp%2Csize_22%2Ctext_5Yqo5Yqb6IqC54K5%2Ccolor_FFFFFF%2Cshadow_50%2Ct_80%2Cg_se%2Cx_10%2Cy_10" alt="img"></p><p>通过以上的执行结果可以看到，只有当使用到班级名称之后，才会执行关联的sql语句，这就是延迟加载。</p><p>在mybatis中如何开启全局的延迟加载呢？需要setting配置，如下：</p><p><img src="https://cdn.nlark.com/yuque/0/2022/png/21376908/1661136161612-a7c3cc7f-fe89-4245-a297-1572d8384566.png?x-oss-process=image%2Fwatermark%2Ctype_d3F5LW1pY3JvaGVp%2Csize_43%2Ctext_5Yqo5Yqb6IqC54K5%2Ccolor_FFFFFF%2Cshadow_50%2Ct_80%2Cg_se%2Cx_10%2Cy_10" alt="img"></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></pre></td><td class="code"><pre><span class="line"><span class="tag">&lt;<span class="name">settings</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;lazyLoadingEnabled&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">settings</span>&gt;</span></span><br></pre></td></tr></table></figure><p><strong>把fetchType=”lazy”去掉。</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></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">StudentMapperTest</span> &#123;</span><br><span class="line">    <span class="meta">@Test</span></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">testSelectBySid</span><span class="params">()</span>&#123;</span><br><span class="line">        <span class="type">StudentMapper</span> <span class="variable">mapper</span> <span class="operator">=</span> SqlSessionUtil.openSession().getMapper(StudentMapper.class);</span><br><span class="line">        <span class="type">Student</span> <span class="variable">student</span> <span class="operator">=</span> mapper.selectBySid(<span class="number">1</span>);</span><br><span class="line">        <span class="comment">//System.out.println(student);</span></span><br><span class="line">        <span class="comment">// 只获取学生姓名</span></span><br><span class="line">        <span class="type">String</span> <span class="variable">sname</span> <span class="operator">=</span> student.getSname();</span><br><span class="line">        System.out.println(<span class="string">&quot;学生姓名：&quot;</span> + sname);</span><br><span class="line">        <span class="comment">// 到这里之后，想获取班级名字了</span></span><br><span class="line">        <span class="type">String</span> <span class="variable">cname</span> <span class="operator">=</span> student.getClazz().getCname();</span><br><span class="line">        System.out.println(<span class="string">&quot;学生的班级名称：&quot;</span> + cname);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p><img src="https://cdn.nlark.com/yuque/0/2022/png/21376908/1661151954051-4b5b94f7-a69d-4d21-b220-1a840862fb85.png?x-oss-process=image%2Fwatermark%2Ctype_d3F5LW1pY3JvaGVp%2Csize_22%2Ctext_5Yqo5Yqb6IqC54K5%2Ccolor_FFFFFF%2Cshadow_50%2Ct_80%2Cg_se%2Cx_10%2Cy_10" alt="img"></p><p>通过以上的测试可以看出，我们已经开启了全局延迟加载策略。</p><p>开启全局延迟加载之后，所有的sql都会支持延迟加载，如果某个sql你不希望它支持延迟加载怎么办呢？将fetchType设置为eager：</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></pre></td><td class="code"><pre><span class="line"><span class="tag">&lt;<span class="name">resultMap</span> <span class="attr">id</span>=<span class="string">&quot;studentResultMap&quot;</span> <span class="attr">type</span>=<span class="string">&quot;Student&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;sid&quot;</span> <span class="attr">column</span>=<span class="string">&quot;sid&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;sname&quot;</span> <span class="attr">column</span>=<span class="string">&quot;sname&quot;</span>/&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;clazz&quot;</span></span></span><br><span class="line"><span class="tag">               <span class="attr">select</span>=<span class="string">&quot;com.powernode.mybatis.mapper.ClazzMapper.selectByCid&quot;</span></span></span><br><span class="line"><span class="tag">               <span class="attr">column</span>=<span class="string">&quot;cid&quot;</span></span></span><br><span class="line"><span class="tag">               <span class="attr">fetchType</span>=<span class="string">&quot;eager&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><p><img src="https://cdn.nlark.com/yuque/0/2022/png/21376908/1661151998854-a31c19fb-0af9-4d69-894d-e8086d9c0333.png?x-oss-process=image%2Fwatermark%2Ctype_d3F5LW1pY3JvaGVp%2Csize_22%2Ctext_5Yqo5Yqb6IqC54K5%2Ccolor_FFFFFF%2Cshadow_50%2Ct_80%2Cg_se%2Cx_10%2Cy_10" alt="img"></p><p>这样的话，针对某个特定的sql，你就关闭了延迟加载机制。</p><p>后期我们要不要开启延迟加载机制，主要看实际的业务需求是怎样的。</p><h2 id="13-3-一对多"><a href="#13-3-一对多" class="headerlink" title="13.3 一对多"></a>13.3 一对多</h2><p>一对多的实现，通常是在一的一方中有List集合属性。</p><p>在Clazz类中添加List<Student> stus; 属性。</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="keyword">public</span> <span class="keyword">class</span> <span class="title class_">Clazz</span> &#123;</span><br><span class="line">    <span class="keyword">private</span> Integer cid;</span><br><span class="line">    <span class="keyword">private</span> String cname;</span><br><span class="line">    <span class="keyword">private</span> List&lt;Student&gt; stus;</span><br><span class="line">    <span class="comment">// set get方法</span></span><br><span class="line">    <span class="comment">// 构造方法</span></span><br><span class="line">    <span class="comment">// toString方法</span></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>一对多的实现通常包括两种实现方式：</p><ul><li>第一种方式：collection</li><li>第二种方式：分步查询</li></ul><p><strong>一对多的理解</strong></p><p><img src="https://cdn.nlark.com/yuque/0/2024/png/22016332/1704786830294-d89d7770-7ab5-4cae-871d-1c3880567a12.png" alt="img"></p><h3 id="第一种方式：collection"><a href="#第一种方式：collection" class="headerlink" title="第一种方式：collection"></a>第一种方式：collection</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="keyword">package</span> com.powernode.mybatis.mapper;</span><br><span class="line"></span><br><span class="line"><span class="keyword">import</span> com.powernode.mybatis.pojo.Clazz;</span><br><span class="line"></span><br><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * Clazz映射器接口</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@author</span> 老杜</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@version</span> 1.0</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@since</span> 1.0</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_">ClazzMapper</span> &#123;</span><br><span class="line"></span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     * 根据cid获取Clazz信息</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@param</span> cid</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@return</span></span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    Clazz <span class="title function_">selectByCid</span><span class="params">(Integer cid)</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 class="doctag">@param</span> cid</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@return</span></span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    Clazz <span class="title function_">selectClazzAndStusByCid</span><span class="params">(Integer cid)</span>;</span><br><span class="line">&#125;</span><br><span class="line">&lt;resultMap id=<span class="string">&quot;clazzResultMap&quot;</span> type=<span class="string">&quot;Clazz&quot;</span>&gt;</span><br><span class="line">  &lt;id property=<span class="string">&quot;cid&quot;</span> column=<span class="string">&quot;cid&quot;</span>/&gt;</span><br><span class="line">  &lt;result property=<span class="string">&quot;cname&quot;</span> column=<span class="string">&quot;cname&quot;</span>/&gt;</span><br><span class="line">  &lt;collection property=<span class="string">&quot;stus&quot;</span> ofType=<span class="string">&quot;Student&quot;</span>&gt;</span><br><span class="line">    &lt;id property=<span class="string">&quot;sid&quot;</span> column=<span class="string">&quot;sid&quot;</span>/&gt;</span><br><span class="line">    &lt;result property=<span class="string">&quot;sname&quot;</span> column=<span class="string">&quot;sname&quot;</span>/&gt;</span><br><span class="line">  &lt;/collection&gt;</span><br><span class="line">&lt;/resultMap&gt;</span><br><span class="line"></span><br><span class="line">&lt;select id=<span class="string">&quot;selectClazzAndStusByCid&quot;</span> resultMap=<span class="string">&quot;clazzResultMap&quot;</span>&gt;</span><br><span class="line">  select * from t_clazz c join t_student s on c.cid = s.cid where c.cid = #&#123;cid&#125;</span><br><span class="line">&lt;/select&gt;</span><br></pre></td></tr></table></figure><p>注意是ofType，表示“集合中的类型”。</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="keyword">package</span> com.powernode.mybatis.test;</span><br><span class="line"></span><br><span class="line"><span class="keyword">import</span> com.powernode.mybatis.mapper.ClazzMapper;</span><br><span class="line"><span class="keyword">import</span> com.powernode.mybatis.pojo.Clazz;</span><br><span class="line"><span class="keyword">import</span> com.powernode.mybatis.utils.SqlSessionUtil;</span><br><span class="line"><span class="keyword">import</span> org.junit.Test;</span><br><span class="line"></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">ClazzMapperTest</span> &#123;</span><br><span class="line">    <span class="meta">@Test</span></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">testSelectClazzAndStusByCid</span><span class="params">()</span> &#123;</span><br><span class="line">        <span class="type">ClazzMapper</span> <span class="variable">mapper</span> <span class="operator">=</span> SqlSessionUtil.openSession().getMapper(ClazzMapper.class);</span><br><span class="line">        <span class="type">Clazz</span> <span class="variable">clazz</span> <span class="operator">=</span> mapper.selectClazzAndStusByCid(<span class="number">1001</span>);</span><br><span class="line">        System.out.println(clazz);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>执行结果：</p><p><img src="https://cdn.nlark.com/yuque/0/2022/png/21376908/1661149977323-90515301-4b5b-4e3e-a17f-04d5473766e7.png?x-oss-process=image%2Fwatermark%2Ctype_d3F5LW1pY3JvaGVp%2Csize_46%2Ctext_5Yqo5Yqb6IqC54K5%2Ccolor_FFFFFF%2Cshadow_50%2Ct_80%2Cg_se%2Cx_10%2Cy_10" alt="img"></p><h3 id="第二种方式：分步查询"><a href="#第二种方式：分步查询" class="headerlink" title="第二种方式：分步查询"></a>第二种方式：分步查询</h3><p>修改以下三个位置即可：</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></pre></td><td class="code"><pre><span class="line"><span class="tag">&lt;<span class="name">resultMap</span> <span class="attr">id</span>=<span class="string">&quot;clazzResultMap&quot;</span> <span class="attr">type</span>=<span class="string">&quot;Clazz&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;cid&quot;</span> <span class="attr">column</span>=<span class="string">&quot;cid&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;cname&quot;</span> <span class="attr">column</span>=<span class="string">&quot;cname&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">collection</span> <span class="attr">property</span>=<span class="string">&quot;stus&quot;</span></span></span><br><span class="line"><span class="tag">              <span class="attr">select</span>=<span class="string">&quot;com.powernode.mybatis.mapper.StudentMapper.selectByCid&quot;</span></span></span><br><span class="line"><span class="tag">              <span class="attr">column</span>=<span class="string">&quot;cid&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;!--sql语句也变化了--&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;selectClazzAndStusByCid&quot;</span> <span class="attr">resultMap</span>=<span class="string">&quot;clazzResultMap&quot;</span>&gt;</span></span><br><span class="line">  select * from t_clazz c where c.cid = #&#123;cid&#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><br><span class="line">* @param cid</span><br><span class="line">* @return</span><br><span class="line">*/</span><br><span class="line">List<span class="tag">&lt;<span class="name">Student</span>&gt;</span> selectByCid(Integer cid);</span><br><span class="line"><span class="tag">&lt;<span class="name">select</span> <span class="attr">id</span>=<span class="string">&quot;selectByCid&quot;</span> <span class="attr">resultType</span>=<span class="string">&quot;Student&quot;</span>&gt;</span></span><br><span class="line">  select * from t_student where cid = #&#123;cid&#125;</span><br><span class="line"><span class="tag">&lt;/<span class="name">select</span>&gt;</span></span><br></pre></td></tr></table></figure><p>执行结果：</p><p><img src="https://cdn.nlark.com/yuque/0/2022/png/21376908/1661151398803-0c0e196e-6c0a-42fb-b9ee-abd42fea2026.png?x-oss-process=image%2Fwatermark%2Ctype_d3F5LW1pY3JvaGVp%2Csize_47%2Ctext_5Yqo5Yqb6IqC54K5%2Ccolor_FFFFFF%2Cshadow_50%2Ct_80%2Cg_se%2Cx_10%2Cy_10" alt="img"></p><h2 id="13-4-一对多延迟加载"><a href="#13-4-一对多延迟加载" class="headerlink" title="13.4 一对多延迟加载"></a>13.4 一对多延迟加载</h2><p>一对多延迟加载机制和多对一是一样的。同样是通过两种方式：</p><ul><li>第一种：fetchType=”lazy”</li><li>第二种：修改全局的配置setting，<strong>lazyLoadingEnabled=true，</strong>如果开启全局延迟加载，想让某个sql不使用延迟加载：fetchType=”eager”</li></ul><p><img src="https://cdn.nlark.com/yuque/0/2022/png/21376908/1659578619308-ceb8077a-94a7-4f64-b41d-e54b3c14e7fb.png?x-oss-process=image%2Fwatermark%2Ctype_d3F5LW1pY3JvaGVp%2Csize_34%2Ctext_5Yqo5Yqb6IqC54K5%2Ccolor_FFFFFF%2Cshadow_50%2Ct_80%2Cg_se%2Cx_10%2Cy_10" alt="img"></p><h1 id="MyBatis的缓存"><a href="#MyBatis的缓存" class="headerlink" title="MyBatis的缓存"></a>MyBatis的缓存</h1><div class="note info no-icon flat"><p>缓存的作用：通过减少IO的方式，来提高程序的执行效率。</p></div><p>mybatis的缓存：将select语句的查询结果放到缓存（内存）当中，下一次还是这条select语句的话，直接从缓存中取，不再查数据库。一方面是减少了IO。另一方面不再执行繁琐的查找算法。效率大大提升。</p><p>mybatis缓存包括：</p><ul><li>一级缓存：将查询到的数据存储到SqlSession中。</li><li>二级缓存：将查询到的数据存储到SqlSessionFactory中。</li><li>集成其它第三方的缓存：比如EhCache【Java语言开发的】、Memcache【C语言开发的】等。</li></ul><p><strong>缓存只针对于DQL语句，也就是说缓存机制只对应select语句。</strong></p><div class="img-wrap"><div class="img-bg"><img class="img" src="https://raw.githubusercontent.com/silvan2077/markdown_pic/main/Picgo/20251024171637.png"/></div></div><h2 id="一级缓存"><a href="#一级缓存" class="headerlink" title="一级缓存"></a>一级缓存</h2><p>一级缓存默认开启,不需要做任何配置。</p><p>原理：只要使用同一个SqlSession对象执行同一条SQL语句，就会走缓存。</p><div class="tabs" id="一级缓存"><ul class="nav-tabs"><li class="tab active"><button type="button" data-href="#一级缓存-1">CarMapper接口</button></li><li class="tab"><button type="button" data-href="#一级缓存-2">CarMapper.xml</button></li><li class="tab"><button type="button" data-href="#一级缓存-3">测试程序</button></li></ul><div class="tab-contents"><div class="tab-item-content active" id="一级缓存-1"><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><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">public</span> <span class="keyword">interface</span> <span class="title class_">CarMapper</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获取Car信息。</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@param</span> id</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@return</span></span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    Car <span class="title function_">selectById</span><span class="params">(Long id)</span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><button type="button" class="tab-to-top" aria-label="scroll to top"><i class="fas fa-arrow-up"></i></button></div><div class="tab-item-content" id="一级缓存-2"><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="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></span><br><span class="line"><span class="meta">        <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="tag">&lt;<span class="name">mapper</span> <span class="attr">namespace</span>=<span class="string">&quot;com.powernode.mybatis.mapper.CarMapper&quot;</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;selectById&quot;</span> <span class="attr">resultType</span>=<span class="string">&quot;Car&quot;</span>&gt;</span></span><br><span class="line">    select * from t_car 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="tag">&lt;/<span class="name">mapper</span>&gt;</span></span><br></pre></td></tr></table></figure><button type="button" class="tab-to-top" aria-label="scroll to top"><i class="fas fa-arrow-up"></i></button></div><div class="tab-item-content" id="一级缓存-3"><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><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_">CarMapperTest</span> &#123;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Test</span></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">testSelectById</span><span class="params">()</span> <span class="keyword">throws</span> Exception&#123;</span><br><span class="line">        <span class="comment">// 注意：不能使用我们封装的SqlSessionUtil工具类。</span></span><br><span class="line">        <span class="type">SqlSessionFactoryBuilder</span> <span class="variable">builder</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">SqlSessionFactoryBuilder</span>();</span><br><span class="line">        <span class="type">SqlSessionFactory</span> <span class="variable">sqlSessionFactory</span> <span class="operator">=</span> builder.build(Resources.getResourceAsStream(<span class="string">&quot;mybatis-config.xml&quot;</span>));</span><br><span class="line"></span><br><span class="line">        <span class="type">SqlSession</span> <span class="variable">sqlSession1</span> <span class="operator">=</span> sqlSessionFactory.openSession();</span><br><span class="line"></span><br><span class="line">        <span class="type">CarMapper</span> <span class="variable">mapper1</span> <span class="operator">=</span> sqlSession1.getMapper(CarMapper.class);</span><br><span class="line">        <span class="type">Car</span> <span class="variable">car1</span> <span class="operator">=</span> mapper1.selectById(<span class="number">83L</span>);</span><br><span class="line">        System.out.println(car1);</span><br><span class="line"></span><br><span class="line">        <span class="type">CarMapper</span> <span class="variable">mapper2</span> <span class="operator">=</span> sqlSession1.getMapper(CarMapper.class);</span><br><span class="line">        <span class="type">Car</span> <span class="variable">car2</span> <span class="operator">=</span> mapper2.selectById(<span class="number">83L</span>);</span><br><span class="line">        System.out.println(car2);</span><br><span class="line"></span><br><span class="line">        <span class="type">SqlSession</span> <span class="variable">sqlSession2</span> <span class="operator">=</span> sqlSessionFactory.openSession();</span><br><span class="line"></span><br><span class="line">        <span class="type">CarMapper</span> <span class="variable">mapper3</span> <span class="operator">=</span> sqlSession2.getMapper(CarMapper.class);</span><br><span class="line">        <span class="type">Car</span> <span class="variable">car3</span> <span class="operator">=</span> mapper3.selectById(<span class="number">83L</span>);</span><br><span class="line">        System.out.println(car3);</span><br><span class="line"></span><br><span class="line">        <span class="type">CarMapper</span> <span class="variable">mapper4</span> <span class="operator">=</span> sqlSession2.getMapper(CarMapper.class);</span><br><span class="line">        <span class="type">Car</span> <span class="variable">car4</span> <span class="operator">=</span> mapper4.selectById(<span class="number">83L</span>);</span><br><span class="line">        System.out.println(car4);</span><br><span class="line"></span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><button type="button" class="tab-to-top" aria-label="scroll to top"><i class="fas fa-arrow-up"></i></button></div></div></div><p>执行结果：<br>每次只执行了一条查询语句但返回了两条数据<br><div class="img-wrap"><div class="img-bg"><img class="img" src="https://raw.githubusercontent.com/silvan2077/markdown_pic/main/Picgo/20251024171918.png"/></div></div></p><p>什么情况下不走缓存？</p><ul><li>第一种：使用不同的SqlSession对象。</li><li>第二种：查询条件变化。</li></ul><p>一级缓存失效情况包括两种：</p><ul><li>第一种：第一次查询和第二次查询之间，手动清空了一级缓存。</li></ul><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">sqlSession.clearCache();</span><br></pre></td></tr></table></figure><ul><li>第二种：第一次查询和第二次查询之间，执行了增删改操作。【这个增删改和哪张表没有关系，只要有insert delete update操作，一级缓存就会失效。】</li></ul><div class="tabs" id="第二种失效"><ul class="nav-tabs"><li class="tab active"><button type="button" data-href="#第二种失效-1">CarMapper接口</button></li><li class="tab"><button type="button" data-href="#第二种失效-2">CarMapper.xml</button></li><li class="tab"><button type="button" data-href="#第二种失效-3">执行程序</button></li></ul><div class="tab-contents"><div class="tab-item-content active" id="第二种失效-1"><figure class="highlight java"><table><tr><td class="gutter"><pre><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">/**</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">void</span> <span class="title function_">insertAccount</span><span class="params">()</span>;</span><br></pre></td></tr></table></figure><button type="button" class="tab-to-top" aria-label="scroll to top"><i class="fas fa-arrow-up"></i></button></div><div class="tab-item-content" id="第二种失效-2"><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="tag">&lt;<span class="name">insert</span> <span class="attr">id</span>=<span class="string">&quot;insertAccount&quot;</span>&gt;</span></span><br><span class="line">  insert into t_act values(3, &#x27;act003&#x27;, 10000)</span><br><span class="line"><span class="tag">&lt;/<span class="name">insert</span>&gt;</span></span><br></pre></td></tr></table></figure><button type="button" class="tab-to-top" aria-label="scroll to top"><i class="fas fa-arrow-up"></i></button></div><div class="tab-item-content" id="第二种失效-3"><p>在之前的基础上再添加上一个插入操作</p><div class="img-wrap"><div class="img-bg"><img class="img" src="https://raw.githubusercontent.com/silvan2077/markdown_pic/main/Picgo/20251024172343.png"/></div></div><button type="button" class="tab-to-top" aria-label="scroll to top"><i class="fas fa-arrow-up"></i></button></div></div></div><p>执行结果：</p><div class="img-wrap"><div class="img-bg"><img class="img" src="https://raw.githubusercontent.com/silvan2077/markdown_pic/main/Picgo/20251024172937.png"/></div></div><h2 id="二级缓存"><a href="#二级缓存" class="headerlink" title="二级缓存"></a>二级缓存</h2><p>二级缓存的范围是SqlSessionFactory。</p><p>使用二级缓存需要具备以下几个条件：</p><ol><li><code>&lt;setting name=&quot;cacheEnabled&quot; value=&quot;true&quot;&gt;</code> 全局性地开启或关闭所有映射器配置文件中已配置的任何缓存。默认就是true，无需设置。</li><li>在需要使用二级缓存的SqlMapper.xml文件中添加配置：<code>&lt;cache /&gt;</code></li><li>使用二级缓存的实体类对象必须是<code>可序列化</code>的，也就是必须实现java.io.Serializable接口</li><li>SqlSession对象关闭或提交之后，一级缓存中的数据才会被写入到二级缓存当中。此时二级缓存才可用。</li></ol><p>测试二级缓存：</p><div class="tabs" id="二级缓存"><ul class="nav-tabs"><li class="tab active"><button type="button" data-href="#二级缓存-1">Car类</button></li></ul><div class="tab-contents"><div class="tab-item-content active" id="二级缓存-1"><figure class="highlight java"><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="keyword">public</span> <span class="keyword">class</span> <span class="title class_">Car</span> <span class="keyword">implements</span> <span class="title class_">Serializable</span> &#123;</span><br><span class="line"><span class="comment">//......</span></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><button type="button" class="tab-to-top" aria-label="scroll to top"><i class="fas fa-arrow-up"></i></button></div></div></div><div class="img-wrap"><div class="img-bg"><img class="img" src="https://raw.githubusercontent.com/silvan2077/markdown_pic/main/Picgo/20251024210754.png"/></div></div><div class="note warning no-icon flat"><p><strong>二级缓存的失效：只要两次查询之间出现了增删改操作。二级缓存就会失效。【一级缓存也会失效】</strong></p></div><p><strong>二级缓存的相关配置：</strong></p><div class="img-wrap"><div class="img-bg"><img class="img" src="https://raw.githubusercontent.com/silvan2077/markdown_pic/main/Picgo/20251024210849.png"/></div></div><ol><li><p>eviction：指定从缓存中移除某个对象的淘汰算法。默认采用LRU策略。</p><ul><li>LRU：Least Recently Used。最近最少使用。优先淘汰在间隔时间内使用频率最低的对象。(其实还有一种淘汰算法LFU，最不常用。)</li><li>FIFO：First In First Out。一种先进先出的数据缓存器。先进入二级缓存的对象最先被淘汰。</li><li>SOFT：软引用。淘汰软引用指向的对象。具体算法和JVM的垃圾回收算法有关。</li><li>WEAK：弱引用。淘汰弱引用指向的对象。具体算法和JVM的垃圾回收算法有关。</li></ul></li><li><p>flushInterval：</p><ul><li>二级缓存的刷新时间间隔。单位毫秒。如果没有设置。就代表不刷新缓存，只要内存足够大，一直会向二级缓存中缓存数据。除非执行了增删改。</li></ul></li><li><p>readOnly：</p><ul><li>true：多条相同的sql语句执行之后返回的对象是共享的同一个。性能好。但是多线程并发可能会存在安全问题。</li><li>false：多条相同的sql语句执行之后返回的对象是副本，调用了clone方法。性能一般。但安全。</li></ul></li><li><p>size：</p><ul><li>设置二级缓存中最多可存储的java对象数量。默认值1024。</li></ul></li></ol><h2 id="MyBatis集成EhCache"><a href="#MyBatis集成EhCache" class="headerlink" title="MyBatis集成EhCache"></a>MyBatis集成EhCache</h2><div class="note info no-icon flat"><p>集成EhCache是为了代替mybatis自带的二级缓存。一级缓存是无法替代的。</p></div><p>mybatis对外提供了接口，也可以集成第三方的缓存组件。比如EhCache、Memcache等。都可以。</p><p>EhCache是Java写的。Memcache是C语言写的。所以mybatis集成EhCache较为常见，按照以下步骤操作，就可以完成集成：</p><p>第一步：引入mybatis整合ehcache的依赖。</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></pre></td><td class="code"><pre><span class="line"><span class="comment">&lt;!--mybatis集成ehcache的组件--&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.caches<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-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.2.2<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>第二步：在类的根路径下新建echcache.xml文件，并提供以下配置信息。</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></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">ehcache</span> <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:noNamespaceSchemaLocation</span>=<span class="string">&quot;http://ehcache.org/ehcache.xsd&quot;</span></span></span><br><span class="line"><span class="tag">         <span class="attr">updateCheck</span>=<span class="string">&quot;false&quot;</span>&gt;</span></span><br><span class="line">    <span class="comment">&lt;!--磁盘存储:将缓存中暂时不使用的对象,转移到硬盘,类似于Windows系统的虚拟内存--&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">diskStore</span> <span class="attr">path</span>=<span class="string">&quot;e:/ehcache&quot;</span>/&gt;</span></span><br><span class="line">  </span><br><span class="line">    <span class="comment">&lt;!--defaultCache：默认的管理策略--&gt;</span></span><br><span class="line">    <span class="comment">&lt;!--eternal：设定缓存的elements是否永远不过期。如果为true，则缓存的数据始终有效，如果为false那么还要根据timeToIdleSeconds，timeToLiveSeconds判断--&gt;</span></span><br><span class="line">    <span class="comment">&lt;!--maxElementsInMemory：在内存中缓存的element的最大数目--&gt;</span></span><br><span class="line">    <span class="comment">&lt;!--overflowToDisk：如果内存中数据超过内存限制，是否要缓存到磁盘上--&gt;</span></span><br><span class="line">    <span class="comment">&lt;!--diskPersistent：是否在磁盘上持久化。指重启jvm后，数据是否有效。默认为false--&gt;</span></span><br><span class="line">    <span class="comment">&lt;!--timeToIdleSeconds：对象空闲时间(单位：秒)，指对象在多长时间没有被访问就会失效。只对eternal为false的有效。默认值0，表示一直可以访问--&gt;</span></span><br><span class="line">    <span class="comment">&lt;!--timeToLiveSeconds：对象存活时间(单位：秒)，指对象从创建到失效所需要的时间。只对eternal为false的有效。默认值0，表示一直可以访问--&gt;</span></span><br><span class="line">    <span class="comment">&lt;!--memoryStoreEvictionPolicy：缓存的3 种清空策略--&gt;</span></span><br><span class="line">    <span class="comment">&lt;!--FIFO：first in first out (先进先出)--&gt;</span></span><br><span class="line">    <span class="comment">&lt;!--LFU：Less Frequently Used (最少使用).意思是一直以来最少被使用的。缓存的元素有一个hit 属性，hit 值最小的将会被清出缓存--&gt;</span></span><br><span class="line">    <span class="comment">&lt;!--LRU：Least Recently Used(最近最少使用). (ehcache 默认值).缓存的元素有一个时间戳，当缓存容量满了，而又需要腾出地方来缓存新的元素的时候，那么现有缓存元素中时间戳离当前时间最远的元素将被清出缓存--&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">defaultCache</span> <span class="attr">eternal</span>=<span class="string">&quot;false&quot;</span> <span class="attr">maxElementsInMemory</span>=<span class="string">&quot;1000&quot;</span> <span class="attr">overflowToDisk</span>=<span class="string">&quot;false&quot;</span> <span class="attr">diskPersistent</span>=<span class="string">&quot;false&quot;</span></span></span><br><span class="line"><span class="tag">                  <span class="attr">timeToIdleSeconds</span>=<span class="string">&quot;0&quot;</span> <span class="attr">timeToLiveSeconds</span>=<span class="string">&quot;600&quot;</span> <span class="attr">memoryStoreEvictionPolicy</span>=<span class="string">&quot;LRU&quot;</span>/&gt;</span></span><br><span class="line"></span><br><span class="line"><span class="tag">&lt;/<span class="name">ehcache</span>&gt;</span></span><br></pre></td></tr></table></figure><p>第三步：修改SqlMapper.xml文件中的<cache/>标签，添加type属性。</p><figure class="highlight xml"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="tag">&lt;<span class="name">cache</span> <span class="attr">type</span>=<span class="string">&quot;org.mybatis.caches.ehcache.EhcacheCache&quot;</span>/&gt;</span></span><br></pre></td></tr></table></figure><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></pre></td><td class="code"><pre><span class="line"><span class="meta">@Test</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">testSelectById2</span><span class="params">()</span> <span class="keyword">throws</span> Exception&#123;</span><br><span class="line">    <span class="type">SqlSessionFactory</span> <span class="variable">sqlSessionFactory</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">SqlSessionFactoryBuilder</span>().build(Resources.getResourceAsStream(<span class="string">&quot;mybatis-config.xml&quot;</span>));</span><br><span class="line">    </span><br><span class="line">    <span class="type">SqlSession</span> <span class="variable">sqlSession1</span> <span class="operator">=</span> sqlSessionFactory.openSession();</span><br><span class="line">    <span class="type">CarMapper</span> <span class="variable">mapper1</span> <span class="operator">=</span> sqlSession1.getMapper(CarMapper.class);</span><br><span class="line">    <span class="type">Car</span> <span class="variable">car1</span> <span class="operator">=</span> mapper1.selectById(<span class="number">83L</span>);</span><br><span class="line">    System.out.println(car1);</span><br><span class="line">    </span><br><span class="line">    sqlSession1.close();</span><br><span class="line">    </span><br><span class="line">    <span class="type">SqlSession</span> <span class="variable">sqlSession2</span> <span class="operator">=</span> sqlSessionFactory.openSession();</span><br><span class="line">    <span class="type">CarMapper</span> <span class="variable">mapper2</span> <span class="operator">=</span> sqlSession2.getMapper(CarMapper.class);</span><br><span class="line">    <span class="type">Car</span> <span class="variable">car2</span> <span class="operator">=</span> mapper2.selectById(<span class="number">83L</span>);</span><br><span class="line">    System.out.println(car2);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><div class="img-wrap"><div class="img-bg"><img class="img" src="https://raw.githubusercontent.com/silvan2077/markdown_pic/main/Picgo/20251024211139.png"/></div></div><h1 id="MyBatis逆向工程"><a href="#MyBatis逆向工程" class="headerlink" title="MyBatis逆向工程"></a>MyBatis逆向工程</h1><p>所谓的逆向工程是：根据数据库表逆向生成Java的pojo类，SqlMapper.xml文件，以及Mapper接口类等。</p><p>要完成这个工作，需要借助别人写好的逆向工程插件。</p><p>思考：使用这个插件的话，需要给这个插件配置哪些信息？</p><ul><li>pojo类名、包名以及生成位置。</li><li>SqlMapper.xml文件名以及生成位置。</li><li>Mapper接口名以及生成位置。</li><li>连接数据库的信息。</li><li>指定哪些表参与逆向工程。</li><li>……</li></ul><h2 id="逆向工程配置与生成"><a href="#逆向工程配置与生成" class="headerlink" title="逆向工程配置与生成"></a>逆向工程配置与生成</h2><ul><li>在pom中添加逆向工程插件</li></ul><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></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">build</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">plugins</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">plugin</span>&gt;</span></span><br><span class="line">      <span class="comment">&lt;!--插件的GAV坐标--&gt;</span></span><br><span class="line">      <span class="tag">&lt;<span class="name">groupId</span>&gt;</span>org.mybatis.generator<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-generator-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>1.4.1<span class="tag">&lt;/<span class="name">version</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">configuration</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">overwrite</span>&gt;</span>true<span class="tag">&lt;/<span class="name">overwrite</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;!--插件的依赖--&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="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>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-java<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.30<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">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></pre></td></tr></table></figure><ul><li>配置generatorConfig.xml</li></ul><p>该文件必须放在类的根路径下，且文件名必须叫做：generatorConfig.xml</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><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="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">generatorConfiguration</span></span></span><br><span class="line"><span class="meta">        <span class="keyword">PUBLIC</span> <span class="string">&quot;-//mybatis.org//DTD MyBatis Generator Configuration 1.0//EN&quot;</span></span></span><br><span class="line"><span class="meta">        <span class="string">&quot;http://mybatis.org/dtd/mybatis-generator-config_1_0.dtd&quot;</span>&gt;</span></span><br><span class="line"></span><br><span class="line"><span class="tag">&lt;<span class="name">generatorConfiguration</span>&gt;</span></span><br><span class="line">    <span class="comment">&lt;!--</span></span><br><span class="line"><span class="comment">        targetRuntime有两个值：</span></span><br><span class="line"><span class="comment">            MyBatis3Simple：生成的是基础版，只有基本的增删改查。</span></span><br><span class="line"><span class="comment">            MyBatis3：生成的是增强版，除了基本的增删改查之外还有复杂的增删改查。</span></span><br><span class="line"><span class="comment">    --&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">context</span> <span class="attr">id</span>=<span class="string">&quot;DB2Tables&quot;</span> <span class="attr">targetRuntime</span>=<span class="string">&quot;MyBatis3&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">plugin</span> <span class="attr">type</span>=<span class="string">&quot;org.mybatis.generator.plugins.UnmergeableXmlMappersPlugin&quot;</span>/&gt;</span></span><br><span class="line">      </span><br><span class="line">        <span class="tag">&lt;<span class="name">commentGenerator</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">property</span> <span class="attr">name</span>=<span class="string">&quot;suppressDate&quot;</span> <span class="attr">value</span>=<span class="string">&quot;true&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">property</span> <span class="attr">name</span>=<span class="string">&quot;suppressAllComments&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">commentGenerator</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">jdbcConnection</span> <span class="attr">driverClass</span>=<span class="string">&quot;com.mysql.cj.jdbc.Driver&quot;</span></span></span><br><span class="line"><span class="tag">                        <span class="attr">connectionURL</span>=<span class="string">&quot;jdbc:mysql://localhost:3306/powernode&quot;</span></span></span><br><span class="line"><span class="tag">                        <span class="attr">userId</span>=<span class="string">&quot;root&quot;</span></span></span><br><span class="line"><span class="tag">                        <span class="attr">password</span>=<span class="string">&quot;root&quot;</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;/<span class="name">jdbcConnection</span>&gt;</span></span><br><span class="line"></span><br><span class="line">        <span class="comment">&lt;!-- 生成pojo包名和位置 --&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">javaModelGenerator</span> <span class="attr">targetPackage</span>=<span class="string">&quot;com.powernode.mybatis.pojo&quot;</span> <span class="attr">targetProject</span>=<span class="string">&quot;src/main/java&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">property</span> <span class="attr">name</span>=<span class="string">&quot;enableSubPackages&quot;</span> <span class="attr">value</span>=<span class="string">&quot;true&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">property</span> <span class="attr">name</span>=<span class="string">&quot;trimStrings&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">javaModelGenerator</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">sqlMapGenerator</span> <span class="attr">targetPackage</span>=<span class="string">&quot;com.powernode.mybatis.mapper&quot;</span> <span class="attr">targetProject</span>=<span class="string">&quot;src/main/resources&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">property</span> <span class="attr">name</span>=<span class="string">&quot;enableSubPackages&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">sqlMapGenerator</span>&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">javaClientGenerator</span></span></span><br><span class="line"><span class="tag">                <span class="attr">type</span>=<span class="string">&quot;xmlMapper&quot;</span></span></span><br><span class="line"><span class="tag">                <span class="attr">targetPackage</span>=<span class="string">&quot;com.powernode.mybatis.mapper&quot;</span></span></span><br><span class="line"><span class="tag">                <span class="attr">targetProject</span>=<span class="string">&quot;src/main/java&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;enableSubPackages&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">javaClientGenerator</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">table</span> <span class="attr">tableName</span>=<span class="string">&quot;t_car&quot;</span> <span class="attr">domainObjectName</span>=<span class="string">&quot;Car&quot;</span>/&gt;</span></span><br><span class="line"></span><br><span class="line">    <span class="tag">&lt;/<span class="name">context</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;/<span class="name">generatorConfiguration</span>&gt;</span></span><br></pre></td></tr></table></figure><h3 id="第四步：运行插件"><a href="#第四步：运行插件" class="headerlink" title="第四步：运行插件"></a>第四步：运行插件</h3><div class="img-wrap"><div class="img-bg"><img class="img" src="https://raw.githubusercontent.com/silvan2077/markdown_pic/main/Picgo/20251024215701.png"/></div></div><h2 id="测试逆向工程"><a href="#测试逆向工程" class="headerlink" title="测试逆向工程"></a>测试逆向工程</h2><ul><li>第一步：环境准备</li></ul><ul><li>依赖：mybatis依赖、mysql驱动依赖、junit依赖、logback依赖、Lombok依赖</li><li>jdbc.properties</li><li>mybatis-config.xml</li><li>logback.xml</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></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">GeneratorTest</span> &#123;</span><br><span class="line">    <span class="meta">@Test</span></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">testGenerator</span><span class="params">()</span> <span class="keyword">throws</span> Exception&#123;</span><br><span class="line">        <span class="type">SqlSessionFactory</span> <span class="variable">sqlSessionFactory</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">SqlSessionFactoryBuilder</span>().build(Resources.getResourceAsStream(<span class="string">&quot;mybatis-config.xml&quot;</span>));</span><br><span class="line">        <span class="type">SqlSession</span> <span class="variable">sqlSession</span> <span class="operator">=</span> sqlSessionFactory.openSession();</span><br><span class="line">        <span class="type">CarMapper</span> <span class="variable">mapper</span> <span class="operator">=</span> sqlSession.getMapper(CarMapper.class);</span><br><span class="line">        <span class="comment">// 增</span></span><br><span class="line">        <span class="comment">/*Car car = new Car();</span></span><br><span class="line"><span class="comment">        car.setCarNum(&quot;1111&quot;);</span></span><br><span class="line"><span class="comment">        car.setBrand(&quot;比亚迪唐&quot;);</span></span><br><span class="line"><span class="comment">        car.setGuidePrice(new BigDecimal(30.0));</span></span><br><span class="line"><span class="comment">        car.setProduceTime(&quot;2010-10-12&quot;);</span></span><br><span class="line"><span class="comment">        car.setCarType(&quot;燃油车&quot;);</span></span><br><span class="line"><span class="comment">        int count = mapper.insert(car);</span></span><br><span class="line"><span class="comment">        System.out.println(&quot;插入了几条记录：&quot; + count);*/</span></span><br><span class="line">        <span class="comment">// 删</span></span><br><span class="line">        <span class="comment">/*int count = mapper.deleteByPrimaryKey(83L);</span></span><br><span class="line"><span class="comment">        System.out.println(&quot;删除了几条记录：&quot; + count);*/</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">/*Car car = new Car();</span></span><br><span class="line"><span class="comment">        car.setId(89L);</span></span><br><span class="line"><span class="comment">        car.setGuidePrice(new BigDecimal(20.0));</span></span><br><span class="line"><span class="comment">        car.setCarType(&quot;新能源&quot;);</span></span><br><span class="line"><span class="comment">        int count = mapper.updateByPrimaryKey(car);</span></span><br><span class="line"><span class="comment">        System.out.println(&quot;更新了几条记录：&quot; + count);*/</span></span><br><span class="line">        <span class="comment">// 根据主键选择性修改</span></span><br><span class="line">        <span class="comment">/*car = new Car();</span></span><br><span class="line"><span class="comment">        car.setId(89L);</span></span><br><span class="line"><span class="comment">        car.setCarNum(&quot;3333&quot;);</span></span><br><span class="line"><span class="comment">        car.setBrand(&quot;宝马520Li&quot;);</span></span><br><span class="line"><span class="comment">        car.setProduceTime(&quot;1999-01-10&quot;);</span></span><br><span class="line"><span class="comment">        count = mapper.updateByPrimaryKeySelective(car);</span></span><br><span class="line"><span class="comment">        System.out.println(&quot;更新了几条记录：&quot; + count);*/</span></span><br><span class="line"></span><br><span class="line">        <span class="comment">// 查一个</span></span><br><span class="line">        <span class="type">Car</span> <span class="variable">car</span> <span class="operator">=</span> mapper.selectByPrimaryKey(<span class="number">89L</span>);</span><br><span class="line">        System.out.println(car);</span><br><span class="line">        <span class="comment">// 查所有</span></span><br><span class="line">        List&lt;Car&gt; cars = mapper.selectByExample(<span class="literal">null</span>);</span><br><span class="line">        cars.forEach(c -&gt; System.out.println(c));</span><br><span class="line">        <span class="comment">// 多条件查询</span></span><br><span class="line">        <span class="comment">// QBC 风格：Query By Criteria 一种查询方式，比较面向对象，看不到sql语句。</span></span><br><span class="line">        <span class="type">CarExample</span> <span class="variable">carExample</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">CarExample</span>();</span><br><span class="line">        carExample.createCriteria()</span><br><span class="line">                .andBrandEqualTo(<span class="string">&quot;丰田霸道&quot;</span>)</span><br><span class="line">                .andGuidePriceGreaterThan(<span class="keyword">new</span> <span class="title class_">BigDecimal</span>(<span class="number">60.0</span>));<span class="comment">//比这个价格大的</span></span><br><span class="line">        carExample.or().andProduceTimeBetween(<span class="string">&quot;2000-10-11&quot;</span>, <span class="string">&quot;2022-10-11&quot;</span>);</span><br><span class="line"></span><br><span class="line">        mapper.selectByExample(carExample);</span><br><span class="line">        sqlSession.commit();</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h1 id="MyBatis使用PageHelper"><a href="#MyBatis使用PageHelper" class="headerlink" title="MyBatis使用PageHelper"></a>MyBatis使用PageHelper</h1><h2 id="limit分页"><a href="#limit分页" class="headerlink" title="limit分页"></a>limit分页</h2><p>mysql的limit后面两个数字：</p><ul><li>第一个数字：startIndex（起始下标。下标从0开始。）</li><li>第二个数字：pageSize（每页显示的记录条数）</li></ul><p>假设已知页码pageNum，还有每页显示的记录条数pageSize，第一个数字可以动态的获取吗？</p><ul><li>startIndex = (pageNum - 1) * pageSize</li></ul><div class="img-wrap"><div class="img-bg"><img class="img" src="https://raw.githubusercontent.com/silvan2077/markdown_pic/main/Picgo/20251024215858.png"/></div></div><p>所以，标准通用的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></pre></td><td class="code"><pre><span class="line"><span class="keyword">select</span> </span><br><span class="line">  <span class="operator">*</span> </span><br><span class="line"><span class="keyword">from</span> </span><br><span class="line">  tableName ...... </span><br><span class="line">limit </span><br><span class="line">  (pageNum <span class="operator">-</span> <span class="number">1</span>) <span class="operator">*</span> pageSize, pageSize</span><br></pre></td></tr></table></figure><p>使用mybatis应该怎么做？</p><div class="tabs" id="limit分页"><ul class="nav-tabs"><li class="tab active"><button type="button" data-href="#limit分页-1">CarMapper接口</button></li><li class="tab"><button type="button" data-href="#limit分页-2">CarMapper.xml</button></li><li class="tab"><button type="button" data-href="#limit分页-3">测试程序</button></li></ul><div class="tab-contents"><div class="tab-item-content active" id="limit分页-1"><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><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">public</span> <span class="keyword">interface</span> <span class="title class_">CarMapper</span> &#123;</span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">    * 通过分页的方式获取Car列表</span></span><br><span class="line"><span class="comment">    * <span class="doctag">@param</span> startIndex 页码</span></span><br><span class="line"><span class="comment">    * <span class="doctag">@param</span> pageSize 每页显示记录条数</span></span><br><span class="line"><span class="comment">    * <span class="doctag">@return</span></span></span><br><span class="line"><span class="comment">    */</span></span><br><span class="line">    List&lt;Car&gt; <span class="title function_">selectAllByPage</span><span class="params">(<span class="meta">@Param(&quot;startIndex&quot;)</span> Integer startIndex, <span class="meta">@Param(&quot;pageSize&quot;)</span> Integer pageSize)</span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><button type="button" class="tab-to-top" aria-label="scroll to top"><i class="fas fa-arrow-up"></i></button></div><div class="tab-item-content" id="limit分页-2"><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></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></span><br><span class="line"><span class="meta">        <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="tag">&lt;<span class="name">mapper</span> <span class="attr">namespace</span>=<span class="string">&quot;com.powernode.mybatis.mapper.CarMapper&quot;</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;selectAllByPage&quot;</span> <span class="attr">resultType</span>=<span class="string">&quot;Car&quot;</span>&gt;</span></span><br><span class="line">        select * from t_car limit #&#123;startIndex&#125;,#&#123;pageSize&#125;</span><br><span class="line">    <span class="tag">&lt;/<span class="name">select</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;/<span class="name">mapper</span>&gt;</span></span><br></pre></td></tr></table></figure><button type="button" class="tab-to-top" aria-label="scroll to top"><i class="fas fa-arrow-up"></i></button></div><div class="tab-item-content" id="limit分页-3"><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><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_">PageTest</span> &#123;</span><br><span class="line">    <span class="meta">@Test</span></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">testPage</span><span class="params">()</span><span class="keyword">throws</span> Exception&#123;</span><br><span class="line">        <span class="type">SqlSessionFactory</span> <span class="variable">sqlSessionFactory</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">SqlSessionFactoryBuilder</span>().build(Resources.getResourceAsStream(<span class="string">&quot;mybatis-config.xml&quot;</span>));</span><br><span class="line">        <span class="type">SqlSession</span> <span class="variable">sqlSession</span> <span class="operator">=</span> sqlSessionFactory.openSession();</span><br><span class="line">        <span class="type">CarMapper</span> <span class="variable">mapper</span> <span class="operator">=</span> sqlSession.getMapper(CarMapper.class);</span><br><span class="line"></span><br><span class="line">        <span class="comment">// 页码</span></span><br><span class="line">        <span class="type">Integer</span> <span class="variable">pageNum</span> <span class="operator">=</span> <span class="number">2</span>;</span><br><span class="line">        <span class="comment">// 每页显示记录条数</span></span><br><span class="line">        <span class="type">Integer</span> <span class="variable">pageSize</span> <span class="operator">=</span> <span class="number">3</span>;</span><br><span class="line">        <span class="comment">// 起始下标</span></span><br><span class="line">        <span class="type">Integer</span> <span class="variable">startIndex</span> <span class="operator">=</span> (pageNum - <span class="number">1</span>) * pageSize;</span><br><span class="line"></span><br><span class="line">        List&lt;Car&gt; cars = mapper.selectAllByPage(startIndex, pageSize);</span><br><span class="line">        cars.forEach(car -&gt; System.out.println(car));</span><br><span class="line"></span><br><span class="line">        sqlSession.commit();</span><br><span class="line">        sqlSession.close();</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><button type="button" class="tab-to-top" aria-label="scroll to top"><i class="fas fa-arrow-up"></i></button></div></div></div><p>执行结果：</p><div class="img-wrap"><div class="img-bg"><img class="img" src="https://raw.githubusercontent.com/silvan2077/markdown_pic/main/Picgo/20251024220415.png"/></div></div><p>获取数据不难，难的是获取分页相关的数据比较难。可以借助mybatis的PageHelper插件。</p><h2 id="PageHelper插件"><a href="#PageHelper插件" class="headerlink" title="PageHelper插件"></a>PageHelper插件</h2><p>使用PageHelper插件进行分页，更加的便捷。</p><ul><li>第一步：引入依赖</li></ul><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>5.3.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><ul><li>第二步：在mybatis-config.xml文件中配置插件</li></ul><p>typeAliases标签下面进行配置：<br><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="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 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><ul><li>第三步：编写Java代码</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></pre></td><td class="code"><pre><span class="line">List&lt;Car&gt; <span class="title function_">selectAll</span><span class="params">()</span>;</span><br><span class="line">&lt;select id=<span class="string">&quot;selectAll&quot;</span> resultType=<span class="string">&quot;Car&quot;</span>&gt;</span><br><span class="line">  select * from t_car</span><br><span class="line">&lt;/select&gt;</span><br></pre></td></tr></table></figure><div class="note info no-icon flat"><p>关键点：</p><ul><li>在查询语句之前开启分页功能。</li><li>在查询语句之后封装PageInfo对象。（PageInfo对象将来会存储到request域当中。在页面上展示。）</li></ul></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="meta">@Test</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">testPageHelper</span><span class="params">()</span> <span class="keyword">throws</span> Exception&#123;</span><br><span class="line">    <span class="type">SqlSessionFactory</span> <span class="variable">sqlSessionFactory</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">SqlSessionFactoryBuilder</span>().build(Resources.getResourceAsStream(<span class="string">&quot;mybatis-config.xml&quot;</span>));</span><br><span class="line">    <span class="type">SqlSession</span> <span class="variable">sqlSession</span> <span class="operator">=</span> sqlSessionFactory.openSession();</span><br><span class="line">    <span class="type">CarMapper</span> <span class="variable">mapper</span> <span class="operator">=</span> sqlSession.getMapper(CarMapper.class);</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 开启分页</span></span><br><span class="line">    PageHelper.startPage(<span class="number">2</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">    List&lt;Car&gt; cars = mapper.selectAll();</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 获取分页信息对象</span></span><br><span class="line">    PageInfo&lt;Car&gt; pageInfo = <span class="keyword">new</span> <span class="title class_">PageInfo</span>&lt;&gt;(cars, <span class="number">5</span>);</span><br><span class="line"></span><br><span class="line">    System.out.println(pageInfo);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>执行结果：</p><blockquote><p>PageInfo{pageNum=2, pageSize=2, size=2, startRow=3, endRow=4, total=6, pages=3, list=Page{count=true, pageNum=2, pageSize=2, startRow=2, endRow=4, total=6, pages=3, reasonable=false, pageSizeZero=false}[Car{id=86, carNum=’1234’, brand=’丰田霸道’, guidePrice=50.5, produceTime=’2020-10-11’, carType=’燃油车’}, Car{id=87, carNum=’1234’, brand=’丰田霸道’, guidePrice=50.5, produceTime=’2020-10-11’, carType=’燃油车’}], prePage=1, nextPage=3, isFirstPage=false, isLastPage=false, hasPreviousPage=true, hasNextPage=true, navigatePages=5, navigateFirstPage=1, navigateLastPage=3, navigatepageNums=[1, 2, 3]}</p></blockquote><p>对执行结果进行格式化：</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><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line">PageInfo&#123;</span><br><span class="line">  pageNum=2, pageSize=2, size=2, startRow=3, endRow=4, total=6, pages=3, </span><br><span class="line">  list=Page&#123;count=true, pageNum=2, pageSize=2, startRow=2, endRow=4, total=6, pages=3, reasonable=false, pageSizeZero=false&#125;</span><br><span class="line">  [Car&#123;id=86, carNum=&#x27;1234&#x27;, brand=&#x27;丰田霸道&#x27;, guidePrice=50.5, produceTime=&#x27;2020-10-11&#x27;, carType=&#x27;燃油车&#x27;&#125;, </span><br><span class="line">  Car&#123;id=87, carNum=&#x27;1234&#x27;, brand=&#x27;丰田霸道&#x27;, guidePrice=50.5, produceTime=&#x27;2020-10-11&#x27;, carType=&#x27;燃油车&#x27;&#125;], </span><br><span class="line">  prePage=1, nextPage=3, isFirstPage=false, isLastPage=false, hasPreviousPage=true, hasNextPage=true, </span><br><span class="line">  navigatePages=5, navigateFirstPage=1, navigateLastPage=3, navigatepageNums=[1, 2, 3]</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h1 id="MyBatis的注解式开发"><a href="#MyBatis的注解式开发" class="headerlink" title="MyBatis的注解式开发"></a>MyBatis的注解式开发</h1><p>mybatis中也提供了注解式开发方式，采用注解可以减少Sql映射文件的配置。</p><p>官方说明：</p><blockquote><p>使用注解来映射简单语句会使代码显得更加简洁，但对于稍微复杂一点的语句，Java 注解不仅力不从心，还会让你本就复杂的 SQL 语句更加混乱不堪。 因此，如果你需要做一些很复杂的操作，最好用 XML 来映射语句。</p></blockquote><div class="note info no-icon flat"><p>原则：简单sql可以注解。复杂sql使用xml。</p></div><h2 id="Insert"><a href="#Insert" class="headerlink" title="@Insert"></a>@Insert</h2><div class="tabs" id="insert"><ul class="nav-tabs"><li class="tab active"><button type="button" data-href="#insert-1">CarMapper接口</button></li><li class="tab"><button type="button" data-href="#insert-2">测试程序</button></li></ul><div class="tab-contents"><div class="tab-item-content active" id="insert-1"><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><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">interface</span> <span class="title class_">CarMapper</span> &#123;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Insert(value=&quot;insert into t_car values(null,#&#123;carNum&#125;,#&#123;brand&#125;,#&#123;guidePrice&#125;,#&#123;produceTime&#125;,#&#123;carType&#125;)&quot;)</span></span><br><span class="line">    <span class="type">int</span> <span class="title function_">insert</span><span class="params">(Car car)</span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><button type="button" class="tab-to-top" aria-label="scroll to top"><i class="fas fa-arrow-up"></i></button></div><div class="tab-item-content" id="insert-2"><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><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_">AnnotationTest</span> &#123;</span><br><span class="line">    <span class="meta">@Test</span></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">testInsert</span><span class="params">()</span> <span class="keyword">throws</span> Exception&#123;</span><br><span class="line">        <span class="type">SqlSessionFactory</span> <span class="variable">sqlSessionFactory</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">SqlSessionFactoryBuilder</span>().build(Resources.getResourceAsStream(<span class="string">&quot;mybatis-config.xml&quot;</span>));</span><br><span class="line">        <span class="type">SqlSession</span> <span class="variable">sqlSession</span> <span class="operator">=</span> sqlSessionFactory.openSession();</span><br><span class="line">        <span class="type">CarMapper</span> <span class="variable">mapper</span> <span class="operator">=</span> sqlSession.getMapper(CarMapper.class);</span><br><span class="line">        <span class="type">Car</span> <span class="variable">car</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">Car</span>(<span class="literal">null</span>, <span class="string">&quot;1112&quot;</span>, <span class="string">&quot;卡罗拉&quot;</span>, <span class="number">30.0</span>, <span class="string">&quot;2000-10-10&quot;</span>, <span class="string">&quot;燃油车&quot;</span>);</span><br><span class="line">        <span class="type">int</span> <span class="variable">count</span> <span class="operator">=</span> mapper.insert(car);</span><br><span class="line">        System.out.println(<span class="string">&quot;插入了几条记录：&quot;</span> + count);</span><br><span class="line">        sqlSession.commit();</span><br><span class="line">        sqlSession.close();</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><button type="button" class="tab-to-top" aria-label="scroll to top"><i class="fas fa-arrow-up"></i></button></div></div></div><h2 id="Delete"><a href="#Delete" class="headerlink" title="@Delete"></a>@Delete</h2><div class="tabs" id="delete"><ul class="nav-tabs"><li class="tab active"><button type="button" data-href="#delete-1">CarMapper接口</button></li><li class="tab"><button type="button" data-href="#delete-2">测试程序</button></li></ul><div class="tab-contents"><div class="tab-item-content active" id="delete-1"><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="meta">@Delete(&quot;delete from t_car where id = #&#123;id&#125;&quot;)</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></pre></td></tr></table></figure><button type="button" class="tab-to-top" aria-label="scroll to top"><i class="fas fa-arrow-up"></i></button></div><div class="tab-item-content" id="delete-2"><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><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">@Test</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">testDelete</span><span class="params">()</span> <span class="keyword">throws</span> Exception&#123;</span><br><span class="line">    <span class="type">SqlSessionFactory</span> <span class="variable">sqlSessionFactory</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">SqlSessionFactoryBuilder</span>().build(Resources.getResourceAsStream(<span class="string">&quot;mybatis-config.xml&quot;</span>));</span><br><span class="line">    <span class="type">SqlSession</span> <span class="variable">sqlSession</span> <span class="operator">=</span> sqlSessionFactory.openSession();</span><br><span class="line">    <span class="type">CarMapper</span> <span class="variable">mapper</span> <span class="operator">=</span> sqlSession.getMapper(CarMapper.class);</span><br><span class="line">    mapper.deleteById(<span class="number">89L</span>);</span><br><span class="line">    sqlSession.commit();</span><br><span class="line">    sqlSession.close();</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><button type="button" class="tab-to-top" aria-label="scroll to top"><i class="fas fa-arrow-up"></i></button></div></div></div><h2 id="Update"><a href="#Update" class="headerlink" title="@Update"></a>@Update</h2><div class="tabs" id="updata"><ul class="nav-tabs"><li class="tab active"><button type="button" data-href="#updata-1">CarMapper接口</button></li><li class="tab"><button type="button" data-href="#updata-2">测试代码</button></li></ul><div class="tab-contents"><div class="tab-item-content active" id="updata-1"><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="meta">@Update(&quot;update t_car set car_num=#&#123;carNum&#125;,brand=#&#123;brand&#125;,guide_price=#&#123;guidePrice&#125;,produce_time=#&#123;produceTime&#125;,car_type=#&#123;carType&#125; where id=#&#123;id&#125;&quot;)</span></span><br><span class="line"><span class="type">int</span> <span class="title function_">update</span><span class="params">(Car car)</span>;</span><br></pre></td></tr></table></figure><button type="button" class="tab-to-top" aria-label="scroll to top"><i class="fas fa-arrow-up"></i></button></div><div class="tab-item-content" id="updata-2"><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><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">@Test</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">testUpdate</span><span class="params">()</span> <span class="keyword">throws</span> Exception&#123;</span><br><span class="line">    <span class="type">SqlSessionFactory</span> <span class="variable">sqlSessionFactory</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">SqlSessionFactoryBuilder</span>().build(Resources.getResourceAsStream(<span class="string">&quot;mybatis-config.xml&quot;</span>));</span><br><span class="line">    <span class="type">SqlSession</span> <span class="variable">sqlSession</span> <span class="operator">=</span> sqlSessionFactory.openSession();</span><br><span class="line">    <span class="type">CarMapper</span> <span class="variable">mapper</span> <span class="operator">=</span> sqlSession.getMapper(CarMapper.class);</span><br><span class="line">    <span class="type">Car</span> <span class="variable">car</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">Car</span>(<span class="number">88L</span>,<span class="string">&quot;1001&quot;</span>, <span class="string">&quot;凯美瑞&quot;</span>, <span class="number">30.0</span>,<span class="string">&quot;2000-11-11&quot;</span>, <span class="string">&quot;新能源&quot;</span>);</span><br><span class="line">    mapper.update(car);</span><br><span class="line">    sqlSession.commit();</span><br><span class="line">    sqlSession.close();</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><button type="button" class="tab-to-top" aria-label="scroll to top"><i class="fas fa-arrow-up"></i></button></div></div></div><h2 id="Select"><a href="#Select" class="headerlink" title="@Select"></a>@Select</h2><div class="tabs" id="select"><ul class="nav-tabs"><li class="tab active"><button type="button" data-href="#select-1">CarMapper接口</button></li><li class="tab"><button type="button" data-href="#select-2">测试程序</button></li></ul><div class="tab-contents"><div class="tab-item-content active" id="select-1"><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><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">@Select(&quot;select * from t_car where id = #&#123;id&#125;&quot;)</span></span><br><span class="line"><span class="meta">@Results(&#123;</span></span><br><span class="line"><span class="meta">    @Result(column = &quot;id&quot;, property = &quot;id&quot;, id = true),</span></span><br><span class="line"><span class="meta">    @Result(column = &quot;car_num&quot;, property = &quot;carNum&quot;),</span></span><br><span class="line"><span class="meta">    @Result(column = &quot;brand&quot;, property = &quot;brand&quot;),</span></span><br><span class="line"><span class="meta">    @Result(column = &quot;guide_price&quot;, property = &quot;guidePrice&quot;),</span></span><br><span class="line"><span class="meta">    @Result(column = &quot;produce_time&quot;, property = &quot;produceTime&quot;),</span></span><br><span class="line"><span class="meta">    @Result(column = &quot;car_type&quot;, property = &quot;carType&quot;)</span></span><br><span class="line"><span class="meta">&#125;)</span></span><br><span class="line">Car <span class="title function_">selectById</span><span class="params">(Long id)</span>;</span><br></pre></td></tr></table></figure><button type="button" class="tab-to-top" aria-label="scroll to top"><i class="fas fa-arrow-up"></i></button></div><div class="tab-item-content" id="select-2"><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><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">@Test</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">testSelectById</span><span class="params">()</span> <span class="keyword">throws</span> Exception&#123;</span><br><span class="line">    <span class="type">SqlSessionFactory</span> <span class="variable">sqlSessionFactory</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">SqlSessionFactoryBuilder</span>().build(Resources.getResourceAsStream(<span class="string">&quot;mybatis-config.xml&quot;</span>));</span><br><span class="line">    <span class="type">SqlSession</span> <span class="variable">sqlSession</span> <span class="operator">=</span> sqlSessionFactory.openSession();</span><br><span class="line">    <span class="type">CarMapper</span> <span class="variable">carMapper</span> <span class="operator">=</span> sqlSession.getMapper(CarMapper.class);</span><br><span class="line">    <span class="type">Car</span> <span class="variable">car</span> <span class="operator">=</span> carMapper.selectById(<span class="number">88L</span>);</span><br><span class="line">    System.out.println(car);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><button type="button" class="tab-to-top" aria-label="scroll to top"><i class="fas fa-arrow-up"></i></button></div></div></div>]]></content>
    
    
      
      
    <summary type="html">&lt;h1 id=&quot;MyBatis入门&quot;&gt;&lt;a href=&quot;#MyBatis入门&quot; class=&quot;headerlink&quot; title=&quot;MyBatis入门&quot;&gt;&lt;/a&gt;MyBatis入门&lt;/h1&gt;&lt;blockquote&gt;
&lt;p&gt;MyBatis 是一款优秀的持久层框架，用于简化 JDBC</summary>
      
    
    
    
    
  </entry>
  
  <entry>
    <title>常用配置文件</title>
    <link href="https://silvan.chat/2025/10/28/%E9%85%8D%E7%BD%AE%E6%96%87%E4%BB%B6-%E9%85%8D%E7%BD%AE%E6%96%87%E4%BB%B6/"/>
    <id>https://silvan.chat/2025/10/28/%E9%85%8D%E7%BD%AE%E6%96%87%E4%BB%B6-%E9%85%8D%E7%BD%AE%E6%96%87%E4%BB%B6/</id>
    <published>2025-10-28T11:35:32.238Z</published>
    <updated>2025-10-28T11:35:32.238Z</updated>
    
    <content type="html"><![CDATA[<div class="note warning no-icon flat"><ul><li>游戏中会存放许多数据,而存放的数据也分为<code>动态数据</code>和<code>静态数据</code>。<ul><li>动态数据：数据会频繁地改变，如当前玩家位置，玩家血量等数据。<strong>通常存储在数据库中。</strong></li><li>静态数据：数据不会频繁改变，如游戏角色的技能冷却时间，地图结构，障碍物位置等基础配置。<strong>通常存储在配置文件中。</strong></li></ul></li></ul></div><h1 id="JSON"><a href="#JSON" class="headerlink" title="JSON"></a>JSON</h1><ul><li><p>JSON是一种轻量级的数据交换格式，它基于JavaScript语言，但是它不是JavaScript语言。</p></li><li><p>JSON对象：键值对，用花括号括起来，键和值用冒号隔开，多个键值对用逗号隔开。</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><span class="line">10</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;myApp&quot;</span><span class="punctuation">,</span></span><br><span class="line">  <span class="attr">&quot;version&quot;</span><span class="punctuation">:</span> <span class="number">1.0</span><span class="punctuation">,</span></span><br><span class="line">  <span class="attr">&quot;debug&quot;</span><span class="punctuation">:</span> <span class="literal"><span class="keyword">true</span></span><span class="punctuation">,</span></span><br><span class="line">  <span class="attr">&quot;database&quot;</span><span class="punctuation">:</span> <span class="punctuation">&#123;</span></span><br><span class="line">    <span class="attr">&quot;host&quot;</span><span class="punctuation">:</span> <span class="string">&quot;localhost&quot;</span><span class="punctuation">,</span></span><br><span class="line">    <span class="attr">&quot;port&quot;</span><span class="punctuation">:</span> <span class="number">3306</span></span><br><span class="line">  <span class="punctuation">&#125;</span><span class="punctuation">,</span></span><br><span class="line">  <span class="attr">&quot;features&quot;</span><span class="punctuation">:</span> <span class="punctuation">[</span><span class="string">&quot;auth&quot;</span><span class="punctuation">,</span> <span class="string">&quot;logging&quot;</span><span class="punctuation">,</span> <span class="string">&quot;metrics&quot;</span><span class="punctuation">]</span></span><br><span class="line"><span class="punctuation">&#125;</span></span><br></pre></td></tr></table></figure></li></ul><div class="tabs" id=""><ul class="nav-tabs"><li class="tab active"><button type="button" data-href="#-1">优点</button></li><li class="tab"><button type="button" data-href="#-2">缺点</button></li><li class="tab"><button type="button" data-href="#-3">适用场景</button></li></ul><div class="tab-contents"><div class="tab-item-content active" id="-1"><ul><li>语法简洁，结构清晰方便解析</li><li>跨语言支持广泛，各种语言都有对应的库可以解析JSON数据</li><li>与XML相比，JSON更轻量，占用更少空间，传输更快</li></ul><button type="button" class="tab-to-top" aria-label="scroll to top"><i class="fas fa-arrow-up"></i></button></div><div class="tab-item-content" id="-2"><ul><li>JSON不支持注释</li><li>JSON不支持对象继承</li><li>配置过大时可读性下降</li></ul><button type="button" class="tab-to-top" aria-label="scroll to top"><i class="fas fa-arrow-up"></i></button></div><div class="tab-item-content" id="-3"><ul><li>Web配置或前后端数据交换</li><li>需要语言跨兼容的配置文件</li></ul><button type="button" class="tab-to-top" aria-label="scroll to top"><i class="fas fa-arrow-up"></i></button></div></div></div><h1 id="YAML"><a href="#YAML" class="headerlink" title="YAML"></a>YAML</h1><ul><li><p>YAML是一种数据序列化格式，是一种可读性高的数据格式，但对缩进要求十分严格。</p></li><li><p>YAML对象：根据缩进来表示层级关系。键值对，用冒号隔开，冒号和值之间应当有一个空格。</p><div class="tabs" id=""><ul class="nav-tabs"><li class="tab active"><button type="button" data-href="#-1">优点</button></li><li class="tab"><button type="button" data-href="#-2">缺点</button></li><li class="tab"><button type="button" data-href="#-3">适用场景</button></li></ul><div class="tab-contents"><div class="tab-item-content active" id="-1"><ul><li>语法简洁，结构清晰，缩进层次较小时能一眼看出</li><li>支持注释（<code>#</code>开头）</li></ul><button type="button" class="tab-to-top" aria-label="scroll to top"><i class="fas fa-arrow-up"></i></button></div><div class="tab-item-content" id="-2"><ul><li>缩进敏感，容易因缩进错误导致解析失败</li><li>不同解析器实现可能有差异</li></ul><button type="button" class="tab-to-top" aria-label="scroll to top"><i class="fas fa-arrow-up"></i></button></div><div class="tab-item-content" id="-3"><button type="button" class="tab-to-top" aria-label="scroll to top"><i class="fas fa-arrow-up"></i></button></div></div></div></li></ul><h1 id="XML"><a href="#XML" class="headerlink" title="XML"></a>XML</h1><ul><li>XML是一种可扩展标记语言，可以高效且精准的描述层次化结构。</li><li>XML对象：用标签来表示数据的开始和结束，标签可以嵌套。</li></ul><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></pre></td><td class="code"><pre><span class="line"><span class="tag">&lt;<span class="name">config</span>&gt;</span></span><br><span class="line">  <span class="tag">&lt;<span class="name">name</span>&gt;</span>myApp<span class="tag">&lt;/<span class="name">name</span>&gt;</span></span><br><span class="line">  <span class="tag">&lt;<span class="name">version</span>&gt;</span>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">debug</span>&gt;</span>true<span class="tag">&lt;/<span class="name">debug</span>&gt;</span></span><br><span class="line">  <span class="tag">&lt;<span class="name">database</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">host</span>&gt;</span>localhost<span class="tag">&lt;/<span class="name">host</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">port</span>&gt;</span>3306<span class="tag">&lt;/<span class="name">port</span>&gt;</span></span><br><span class="line">  <span class="tag">&lt;/<span class="name">database</span>&gt;</span></span><br><span class="line">  <span class="tag">&lt;<span class="name">features</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">feature</span>&gt;</span>auth<span class="tag">&lt;/<span class="name">feature</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">feature</span>&gt;</span>logging<span class="tag">&lt;/<span class="name">feature</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">feature</span>&gt;</span>metrics<span class="tag">&lt;/<span class="name">feature</span>&gt;</span></span><br><span class="line">  <span class="tag">&lt;/<span class="name">features</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;/<span class="name">config</span>&gt;</span></span><br></pre></td></tr></table></figure><div class="tabs" id=""><ul class="nav-tabs"><li class="tab active"><button type="button" data-href="#-1">优点</button></li><li class="tab"><button type="button" data-href="#-2">缺点</button></li><li class="tab"><button type="button" data-href="#-3">适用场景</button></li></ul><div class="tab-contents"><div class="tab-item-content active" id="-1"><ul><li>结构化数据表达能力强，支持层次、属性、命名空间</li><li>可扩展性好，支持自定义标签</li><li>支持模式约束，方便数据校验</li></ul><button type="button" class="tab-to-top" aria-label="scroll to top"><i class="fas fa-arrow-up"></i></button></div><div class="tab-item-content" id="-2"><ul><li>语法较复杂，可读性差</li><li>文件体积大，传输速度慢</li><li>对手动编辑不友好</li></ul><button type="button" class="tab-to-top" aria-label="scroll to top"><i class="fas fa-arrow-up"></i></button></div><div class="tab-item-content" id="-3"><ul><li>传统企业级项目（如 Java EE 配置、Spring XML）</li><li>需要严格数据校验的系统</li><li>文档型数据存储（Office 文档格式底层就是 XML）</li></ul><button type="button" class="tab-to-top" aria-label="scroll to top"><i class="fas fa-arrow-up"></i></button></div></div></div><h1 id="INI"><a href="#INI" class="headerlink" title="INI"></a>INI</h1><ul><li>INI是一种用于配置文件的格式，它基于键值对，用等号（<code>=</code>）来分隔键和值。</li><li>INI对象：使用方括号划分不同的区域名，区域内的键值对表示该区域的配置。并且可以在分号后编写单行注释。</li></ul><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></pre></td><td class="code"><pre><span class="line"><span class="section">[app]</span></span><br><span class="line"><span class="attr">name</span>=myApp</span><br><span class="line"><span class="attr">version</span>=<span class="number">1.0</span></span><br><span class="line"><span class="attr">debug</span>=<span class="literal">true</span></span><br><span class="line"></span><br><span class="line"><span class="section">[database]</span></span><br><span class="line"><span class="attr">host</span>=localhost</span><br><span class="line"><span class="attr">port</span>=<span class="number">3306</span>   <span class="comment">; 数据库端口号</span></span><br><span class="line"></span><br><span class="line"><span class="section">[features]</span></span><br><span class="line"><span class="attr">auth</span>=<span class="literal">true</span></span><br><span class="line"><span class="attr">logging</span>=<span class="literal">true</span></span><br><span class="line"><span class="attr">metrics</span>=<span class="literal">true</span></span><br></pre></td></tr></table></figure><div class="tabs" id=""><ul class="nav-tabs"><li class="tab active"><button type="button" data-href="#-1">优点</button></li><li class="tab"><button type="button" data-href="#-2">缺点</button></li><li class="tab"><button type="button" data-href="#-3">适用场景</button></li></ul><div class="tab-contents"><div class="tab-item-content active" id="-1"><ul><li>语法简单，结构清晰</li><li>编辑方便，适合小型项目配置</li><li>支持分区，方便管理</li></ul><button type="button" class="tab-to-top" aria-label="scroll to top"><i class="fas fa-arrow-up"></i></button></div><div class="tab-item-content" id="-2"><ul><li>数据类型有限（通常只能表示字符串，需要应用层解析）。</li><li>不支持复杂嵌套</li><li>没有严格标准，不同解析器可能有差异</li></ul><button type="button" class="tab-to-top" aria-label="scroll to top"><i class="fas fa-arrow-up"></i></button></div><div class="tab-item-content" id="-3"><ul><li>小型应用、本地配置文件</li><li>桌面软件设置、轻量级服务配置</li><li>不需要复杂嵌套的场景</li></ul><button type="button" class="tab-to-top" aria-label="scroll to top"><i class="fas fa-arrow-up"></i></button></div></div></div><!-- # TOML* TOML的设计目标是"比JSON更容易读写",语法直观，键值对清晰* TOML对象：键值对，用等号（`=`）来分隔键和值。```TOMLname = "myApp"version = 1.0debug = true --><h1 id="总结"><a href="#总结" class="headerlink" title="总结"></a>总结</h1><div class="table-container"><table><thead><tr><th>格式</th><th>可读性</th><th>表达能力</th><th>解析性能</th><th>注释</th><th>常见场景</th></tr></thead><tbody><tr><td>JSON</td><td>中等</td><td>中等</td><td>高</td><td>无</td><td>Web API、微服务</td></tr><tr><td>YAML</td><td>高</td><td>高</td><td>中等</td><td>支持</td><td>DevOps、K8s、CI/CD</td></tr><tr><td>XML</td><td>低</td><td>很高</td><td>低</td><td>支持</td><td>企业级应用、模式验证</td></tr><tr><td>INI</td><td>高</td><td>低</td><td>高</td><td>部分</td><td>小型应用、本地配置</td></tr></tbody></table></div><div class="table-container"><table><thead><tr><th>格式</th><th>可读性</th><th>文件体积</th><th>解析速度</th><th>跨平台兼容性</th><th>适用场景</th></tr></thead><tbody><tr><td><strong>YAML</strong></td><td>★★★★☆</td><td>小</td><td>中等</td><td>较好</td><td>人工可读配置（如 CI/CD、游戏配置）</td></tr><tr><td><strong>INI</strong></td><td>★★★★☆</td><td>很小</td><td>很快</td><td>一般</td><td>简单应用配置（桌面软件、老系统）</td></tr><tr><td><strong>JSON</strong></td><td>★★★☆☆</td><td>小</td><td>很快</td><td>极佳</td><td>前后端通信、现代应用配置</td></tr><tr><td><strong>XML</strong></td><td>★★☆☆☆</td><td>大</td><td>慢</td><td>极佳</td><td>需要严格结构/文档描述（如 SOAP、旧系统）</td></tr></tbody></table></div>]]></content>
    
    
    <summary type="html">个人对常用配置文件的适用场景的看法</summary>
    
    
    
    <category term="配置文件" scheme="https://silvan.chat/categories/%E9%85%8D%E7%BD%AE%E6%96%87%E4%BB%B6/"/>
    
    
  </entry>
  
  <entry>
    <title>Git 基础命令</title>
    <link href="https://silvan.chat/2025/10/22/git/"/>
    <id>https://silvan.chat/2025/10/22/git/</id>
    <published>2025-10-22T07:09:32.350Z</published>
    <updated>2025-11-07T01:37:09.709Z</updated>
    
    <content type="html"><![CDATA[<h2 id="Git概述"><a href="#Git概述" class="headerlink" title="Git概述"></a>Git概述</h2><ul><li>Git是一个分布式版本控制工具，主要用于管理开发过程中的源代码文件（Java类、xml文件、html页面等），在软件开发过程中被广泛使用。</li><li>作用<ol><li>代码回溯</li><li>版本切换</li><li>多人协作</li><li>远程备份</li></ol></li></ul><h3 id="简介"><a href="#简介" class="headerlink" title="简介"></a>简介</h3><ul><li>Git 是一个分布式版本控制工具，通常用来对软件开发过程中的源代码文件进行管理。通过Git仓库来存储和管理这些文件，Git仓库分为两种:<ol><li>本地仓库：开发人员自己电脑上的Git仓库</li><li>远程仓库：远程服务器上的Git仓库</li></ol></li><li><code>commit</code>：提交。将本地文件和版本信息保存到本地仓库</li><li><code>push</code>：推送，将本地仓库文件和版本信息上传到远程仓库</li><li><code>pull</code>：拉取，将远程仓库文件和版本信息瞎啊滋到本地仓库</li></ul><h2 id="Git常用命令"><a href="#Git常用命令" class="headerlink" title="Git常用命令"></a>Git常用命令</h2><div class="tabs" id="本地和远程"><ul class="nav-tabs"><li class="tab active"><button type="button" data-href="#本地和远程-1">本地仓库</button></li><li class="tab"><button type="button" data-href="#本地和远程-2">远程仓库</button></li><li class="tab"><button type="button" data-href="#本地和远程-3">分支操作</button></li></ul><div class="tab-contents"><div class="tab-item-content active" id="本地和远程-1"><div class="table-container"><table><thead><tr><th style="text-align:center">命令</th><th style="text-align:center">功能</th></tr></thead><tbody><tr><td style="text-align:center">git config —global user.name 用户名</td><td style="text-align:center">设置用户签名</td></tr><tr><td style="text-align:center">git config —global user.email 邮箱</td><td style="text-align:center">设置用户签名</td></tr><tr><td style="text-align:center">git init</td><td style="text-align:center">初始化本地库</td></tr><tr><td style="text-align:center">git status</td><td style="text-align:center">查看文件状态</td></tr><tr><td style="text-align:center">git add [文件名称]</td><td style="text-align:center">将文件的修改加入暂存区</td></tr><tr><td style="text-align:center">git reset [文件名称]</td><td style="text-align:center">将暂存区的文件取消暂存</td></tr><tr><td style="text-align:center">git reset —hard [版本号]</td><td style="text-align:center">切换到指定版本</td></tr><tr><td style="text-align:center">git commit [文件名]</td><td style="text-align:center">将暂存区文件提交到版本库中</td></tr><tr><td style="text-align:center">git commit -m “日志信息”</td><td style="text-align:center">将暂存区的文件提交到版本库中</td></tr><tr><td style="text-align:center">git log</td><td style="text-align:center">查看日志</td></tr><tr><td style="text-align:center">git reflog</td><td style="text-align:center">查看历史记录</td></tr></tbody></table></div><button type="button" class="tab-to-top" aria-label="scroll to top"><i class="fas fa-arrow-up"></i></button></div><div class="tab-item-content" id="本地和远程-2"><div class="table-container"><table><thead><tr><th style="text-align:center">命令</th><th style="text-align:center">功能</th></tr></thead><tbody><tr><td style="text-align:center">git remote</td><td style="text-align:center">查看远程仓库</td></tr><tr><td style="text-align:center">git remote -v</td><td style="text-align:center">查看当前所有远程地址别名</td></tr><tr><td style="text-align:center">git remote add [short-name] [url]</td><td style="text-align:center">添加远程仓库</td></tr><tr><td style="text-align:center">git remote rm [short-name]</td><td style="text-align:center">移除远程仓库</td></tr><tr><td style="text-align:center">git clone [url]</td><td style="text-align:center">从远程仓库克隆</td></tr><tr><td style="text-align:center">git pull [short-name] [branch-name]</td><td style="text-align:center">从远程仓库拉取</td></tr><tr><td style="text-align:center">git push [short-name] [branch-name]</td><td style="text-align:center">推送到远程仓库</td></tr></tbody></table></div><button type="button" class="tab-to-top" aria-label="scroll to top"><i class="fas fa-arrow-up"></i></button></div><div class="tab-item-content" id="本地和远程-3"><div class="table-container"><table><thead><tr><th style="text-align:center">命令</th><th style="text-align:center">功能</th></tr></thead><tbody><tr><td style="text-align:center">git branch</td><td style="text-align:center">查看分支：列出本地的所有分支</td></tr><tr><td style="text-align:center">git branch -r</td><td style="text-align:center">查看分支：列出所有的远程分支</td></tr><tr><td style="text-align:center">git branch -a</td><td style="text-align:center">查看分支：列出所有的本地分支和远程分支</td></tr><tr><td style="text-align:center">git branch [branch-name]</td><td style="text-align:center">创建分支</td></tr><tr><td style="text-align:center">git checkout [branch-name]</td><td style="text-align:center">切换分支</td></tr><tr><td style="text-align:center">git push [short-Name] [branch-name]</td><td style="text-align:center">推送至远程仓库分支</td></tr><tr><td style="text-align:center">git merge [branch-name]</td><td style="text-align:center">合并分支</td></tr><tr><td style="text-align:center">git branch -d [branch-name]</td><td style="text-align:center">删除分支</td></tr><tr><td style="text-align:center">git branch -D [branch-name]</td><td style="text-align:center">删除分支（即使该分支中进行了一些开发动作）</td></tr><tr><td style="text-align:center">git push [short-Name] –d [branch-name]</td><td style="text-align:center">删除远程仓库中的分支</td></tr></tbody></table></div><button type="button" class="tab-to-top" aria-label="scroll to top"><i class="fas fa-arrow-up"></i></button></div></div></div><h3 id="初始化"><a href="#初始化" class="headerlink" title="初始化"></a>初始化</h3><ul><li>初始化设置用户名和邮箱<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">git config --global user.name <span class="string">&quot;yourname&quot;</span></span><br><span class="line">git config --global user.email <span class="string">&quot;youremail@example.com&quot;</span></span><br></pre></td></tr></table></figure><div class="note info no-icon flat"><ul><li>—global：全局配置，对所有仓库生效</li><li>—system： 系统配置，对所有用户生效</li><li>默认(local)：只对本地仓库有效config</li></ul></div></li></ul><div class="note warning no-icon flat"><p>签名的作用是为了区分不同操作者的身份，以确认在每个版本的提交信息中能够看到是谁提交的，git首次安装必须填写用户签名，否则无法提交代码</p></div><ul><li>查看配置信息命令<figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">git config --list</span><br></pre></td></tr></table></figure>通过该命令设置的信息都保存在<code>~/.gitconfig</code>文件中</li></ul><h1 id="创建仓库"><a href="#创建仓库" class="headerlink" title="创建仓库"></a>创建仓库</h1><ul><li>在本地初始化一个新的Git仓库(不常用)<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">git init &lt;仓库名称&gt;</span><br></pre></td></tr></table></figure></li><li>下载一个远程存在的仓库(常用)<figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">git <span class="built_in">clone</span> &lt;远程仓库地址&gt;</span><br></pre></td></tr></table></figure><div class="note warning no-icon flat"><p>仓库不能嵌套，因此必须在一个不被git管理的目录下克隆/创建仓库</p></div></li></ul><h1 id="工作区域和文件状态"><a href="#工作区域和文件状态" class="headerlink" title="工作区域和文件状态"></a>工作区域和文件状态</h1><h2 id="工作区域"><a href="#工作区域" class="headerlink" title="工作区域"></a>工作区域</h2><ol><li><strong>工作区（Working Directory / Working Tree）</strong></li></ol><blockquote><p>存放当前正在编辑、查看的项目文件。</p><ul><li>就是你电脑上看到的文件夹内容。</li><li>修改文件后，它们处于<code>已修改（modified）</code>状态。</li><li>工作区是从本地仓库中某个提交版本检出的副本。</li></ul></blockquote><ol><li><p><strong>暂存区（Staging Area / Index）</strong></p><blockquote><p>临时保存即将提交的变更。</p><ul><li>当执行 <code>git add &lt;file&gt;</code> 时，修改会被放入暂存区。</li><li>暂存区记录了<strong>下一次提交所包含的文件快照</strong>。</li><li>实际上，它是 <code>.git/index</code> 文件。</li></ul></blockquote></li><li><p><strong>本地仓库（Local Repository）</strong></p><blockquote><p>保存了项目的完整历史记录（所有提交、分支、标签等）。</p><ul><li>位于 <code>.git/</code> 目录中。</li><li>当执行 <code>git commit</code> 时，Git 会把暂存区的内容保存为一个新的提交对象（commit）到本地仓库。</li></ul></blockquote></li><li><p><strong>远程仓库（Remote Repository）</strong></p><blockquote><p>托管在远程服务器（如 GitHub、GitLab、Gitee）上的仓库。</p><ul><li>通过网络与本地仓库同步（<code>git push</code>、<code>git pull</code>）。</li><li>通常多人协作时使用。</li></ul></blockquote></li></ol><div class="table-container"><table><thead><tr><th>区域</th><th>说明</th><th>关键命令</th></tr></thead><tbody><tr><td>工作区</td><td>你看到和编辑的文件</td><td><code>git status</code></td></tr><tr><td>暂存区</td><td>临时保存将要提交的修改</td><td><code>git add</code></td></tr><tr><td>本地仓库</td><td>保存所有历史提交</td><td><code>git commit</code></td></tr><tr><td>远程仓库</td><td>服务器上的共享仓库</td><td><code>git push / pull</code></td></tr></tbody></table></div><h2 id="文件状态"><a href="#文件状态" class="headerlink" title="文件状态"></a>文件状态</h2><div class="table-container"><table><thead><tr><th>状态</th><th>说明</th><th>典型命令</th></tr></thead><tbody><tr><td>未追踪(Untracked)</td><td>Git 未跟踪的文件</td><td><code>git add</code></td></tr><tr><td>已暂存(Modified)</td><td>工作区修改但未暂存</td><td><code>git add</code> 或 <code>git restore</code></td></tr><tr><td>已暂存(Staged)</td><td>已暂存等待提交</td><td><code>git commit</code> 或 <code>git restore --staged</code></td></tr><tr><td>已提交(Committed)</td><td>已提交到本地仓库</td><td><code>git log</code> 查看</td></tr></tbody></table></div><h1 id="基本概念"><a href="#基本概念" class="headerlink" title="基本概念"></a>基本概念</h1><ul><li>main：默认主分支</li><li>origin：默认远程仓库</li><li>HEAD：指向当前所在的分支或提交</li><li>HEAD^：上一个版本</li><li>HEAD~4：倒数第四个版本(可以是任意数字)</li></ul><h1 id="特殊文件"><a href="#特殊文件" class="headerlink" title="特殊文件"></a>特殊文件</h1><ul><li>.git: Git仓库的元数据和对象数据库</li><li>.gitignore: Git忽略文件列表，用于指定哪些文件或目录不被 Git 跟踪。</li><li>.gitattributes：指定文件的属性，比如换行符</li><li>.gitkeep：使空目录被提交到仓库</li><li>.gitmodules：如果项目包含子模块，这个文件会记录子模块的信息。</li><li>.gitconfig：用户级别的Git配置文件，用于设置用户信息、默认编辑器等。</li></ul><h1 id="添加和提交"><a href="#添加和提交" class="headerlink" title="添加和提交"></a>添加和提交</h1><ul><li>添加单个文件到仓库<figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">git add &lt;文件名&gt;</span><br></pre></td></tr></table></figure></li><li>添加所有文件到仓库<figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">git add .</span><br></pre></td></tr></table></figure></li><li>提交所有暂存区的文件到仓库<figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">git commit -m <span class="string">&quot;提交信息&quot;</span></span><br></pre></td></tr></table></figure></li><li>提交所有已修改的文件到仓库（被git追踪即可，不需要添加到暂存区）<figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">git commit -am <span class="string">&quot;提交信息&quot;</span></span><br></pre></td></tr></table></figure></li></ul><h1 id="查看"><a href="#查看" class="headerlink" title="查看"></a>查看</h1><ul><li><code>git status</code> 查看文件状态，<code>git status -s</code>使输出信息更加简洁<figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">git status</span><br></pre></td></tr></table></figure></li><li>查看提交历史,—oneline可省略<figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">git <span class="built_in">log</span> --oneline</span><br></pre></td></tr></table></figure></li><li>查看未暂存的文件更新了哪些部分<figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">git diff</span><br></pre></td></tr></table></figure></li><li>查看两次提交之间的差异<figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">git diff [commit1] [commit2]</span><br></pre></td></tr></table></figure><h1 id="远程仓库"><a href="#远程仓库" class="headerlink" title="远程仓库"></a>远程仓库</h1></li><li>添加远程仓库<figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">git remote add &lt;远程仓库名&gt; [远程仓库url]</span><br></pre></td></tr></table></figure></li><li>查看远程仓库<figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">git remote -v</span><br></pre></td></tr></table></figure></li><li>删除远程仓库<figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">git remote <span class="built_in">rm</span> &lt;远程仓库名&gt;</span><br></pre></td></tr></table></figure></li><li>重命名远程仓库<figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">git remote rename &lt;远程仓库名&gt; &lt;新远程仓库名&gt;</span><br></pre></td></tr></table></figure></li><li>从远程仓库拉取代码<figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">git pull &lt;远程仓库名&gt; &lt;分支名&gt;</span><br></pre></td></tr></table></figure></li><li>推送代码到远程仓库<figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">git push &lt;远程仓库名&gt; &lt;分支名&gt;</span><br></pre></td></tr></table></figure></li><li>获取所有远程分支<figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">git fetch &lt;远程仓库名&gt;</span><br></pre></td></tr></table></figure></li><li>查看远程分支<figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">git branch -r</span><br></pre></td></tr></table></figure><h1 id="分支"><a href="#分支" class="headerlink" title="分支"></a>分支</h1></li><li>查看本地所有分支，当前分支前面有一个<code>*</code>，<code>-r</code>查看远程分支，<code>-a</code>查看所有分支<figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">git branch </span><br></pre></td></tr></table></figure></li><li>创建新分支<figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">git branch &lt;分支名&gt;</span><br></pre></td></tr></table></figure></li><li>切换到指定分支并更新工作区<figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">git checkout &lt;分支名&gt;</span><br></pre></td></tr></table></figure></li><li>创建新分支并切换到该分支<figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">git checkout -b &lt;分支名&gt;</span><br></pre></td></tr></table></figure></li><li>删除一个已经合并的分支<figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">git branch -d &lt;分支名&gt;</span><br></pre></td></tr></table></figure></li><li>删除一个分支，不管是否合并<figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">git branch -D &lt;分支名&gt;</span><br></pre></td></tr></table></figure></li><li>给当前的提交打上标签，通常用于版本发布<figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">git tag &lt;标签名&gt;</span><br></pre></td></tr></table></figure><h1 id="合并分支"><a href="#合并分支" class="headerlink" title="合并分支"></a>合并分支</h1></li><li>合并分支a到分支b，<code>-no-ff</code>表示禁用<code>fast-forward</code>模式，合并后的历史有分支，能够清晰的看出曾经做过合并，而<code>-ff</code>则表示使用<code>fast-forward</code>模式，合并后的历史会变成一条直线，没有分支。<div class="tabs" id="合并分支"><ul class="nav-tabs"><li class="tab active"><button type="button" data-href="#合并分支-1">禁用fast-forward模式</button></li><li class="tab"><button type="button" data-href="#合并分支-2">使用fast-forward模式</button></li></ul><div class="tab-contents"><div class="tab-item-content active" id="合并分支-1"><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">git merge -no-ff <span class="string">&quot;message&quot;</span> &lt;分支名&gt;</span><br></pre></td></tr></table></figure><div class="img-wrap"><div class="img-bg"><img class="img" src="https://raw.githubusercontent.com/silvan2077/markdown_pic/main/Picgo/20251023112524.png"/></div></div><button type="button" class="tab-to-top" aria-label="scroll to top"><i class="fas fa-arrow-up"></i></button></div><div class="tab-item-content" id="合并分支-2"><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">git merge -ff  <span class="string">&quot;message&quot;</span> &lt;分支名&gt;</span><br></pre></td></tr></table></figure><div class="img-wrap"><div class="img-bg"><img class="img" src="https://raw.githubusercontent.com/silvan2077/markdown_pic/main/Picgo/20251023112719.png"/></div></div><button type="button" class="tab-to-top" aria-label="scroll to top"><i class="fas fa-arrow-up"></i></button></div></div></div>合并&amp;squash所有提交到一个提交<figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">git merge --squash &lt;分支名&gt;</span><br></pre></td></tr></table></figure></li></ul>]]></content>
    
    
    <summary type="html">简单介绍git的基础命令</summary>
    
    
    
    <category term="Git" scheme="https://silvan.chat/categories/Git/"/>
    
    
    <category term="Git" scheme="https://silvan.chat/tags/Git/"/>
    
  </entry>
  
  <entry>
    <title>SpringBoot2</title>
    <link href="https://silvan.chat/2025/10/12/Springboot-SpringBoot2-%E9%BB%91%E9%A9%AC/"/>
    <id>https://silvan.chat/2025/10/12/Springboot-SpringBoot2-%E9%BB%91%E9%A9%AC/</id>
    <published>2025-10-12T07:03:18.913Z</published>
    <updated>2025-10-31T09:54:00.739Z</updated>
    
    <content type="html"><![CDATA[<h1 id="课程说明"><a href="#课程说明" class="headerlink" title="课程说明"></a>课程说明</h1><h2 id="内容说明"><a href="#内容说明" class="headerlink" title="内容说明"></a>内容说明</h2><ul><li>SpringBoot这门技术是用来加速开发Spring程序的，因此学习这门技术有一定的门槛，即至少学完Spring，SpringMVC，Mybatis等相关技术，才能够学习这门技术。</li><li>该课程主要分为基础篇、应用篇、原理篇</li><li>每个课程单元内容设置不同，目标也不一样</li></ul><div class="table-container"><table><thead><tr><th>课程单元</th><th>学习目标</th></tr></thead><tbody><tr><td><font color="#ff0000"><b>基础篇</b></font></td><td>能够创建SpringBoot工程<br/>基于SpringBoot实现ssm/ssmp整合</td></tr><tr><td><font color="#cc0000"><b>应用篇</b></font></td><td>能够掌握SpringBoot程序多环境开发<br/>能够基于Linux系统发布SpringBoot工程<br/>能够解决线上灵活配置SpringBoot工程的需求<br/>能够基于SpringBoot整合任意第三方技术</td></tr><tr><td><font color="#990000"><b>原理篇</b></font></td><td>掌握SpringBoot内部工作流程<br/>理解SpringBoot整合第三方技术的原理<br/>实现自定义开发整合第三方技术的组件</td></tr></tbody></table></div><h2 id="前置知识说明"><a href="#前置知识说明" class="headerlink" title="前置知识说明"></a>前置知识说明</h2><div class="table-container"><table><thead><tr><th>课程单元</th><th>前置知识</th><th>要求</th></tr></thead><tbody><tr><td><font color="#ff0000"><b>基础篇</b></font></td><td>Java基础语法</td><td>面向对象，封装，继承，多态，类与接口，集合，IO，网络编程等</td></tr><tr><td><font color="#ff0000"><b>基础篇</b></font></td><td>Spring与SpringMVC</td><td>知道Spring是用来管理bean，能够基于Restful实现页面请求交互功能</td></tr><tr><td><font color="#ff0000"><b>基础篇</b></font></td><td>Mybatis与Mybatis-Plus</td><td>基于Mybatis和MybatisPlus能够开发出包含基础CRUD功能的标准Dao模块</td></tr><tr><td><font color="#ff0000"><b>基础篇</b></font></td><td>数据库MySQL</td><td>能够读懂基础CRUD功能的SQL语句</td></tr><tr><td><font color="#ff0000"><b>基础篇</b></font></td><td>服务器</td><td>知道服务器与web工程的关系，熟悉web服务器的基础配置</td></tr><tr><td><font color="#ff0000"><b>基础篇</b></font></td><td>maven</td><td>知道maven的依赖关系，知道什么是依赖范围，依赖传递，排除依赖，可选依赖，继承</td></tr><tr><td><font color="#ff0000"><b>基础篇</b></font></td><td>web技术（含vue，ElementUI)</td><td>知道vue如何发送ajax请求，如何获取响应数据，如何进行数据模型双向绑定</td></tr><tr><td><font color="#cc0000"><b>应用篇</b></font></td><td>Linux（CenterOS7）</td><td>熟悉常用的Linux基础指令，熟悉Linux系统目录结构</td></tr><tr><td><font color="#cc0000"><b>应用篇</b></font></td><td>实用开发技术</td><td>缓存：Redis、MongoDB、……<br/>消息中间件:RocketMq、RabbitMq、……</td></tr><tr><td><font color="#990000"><b>原理篇</b></font></td><td>Spring</td><td>了解Spring加载bean的各种方式<br/>知道Spring容器底层工作原理，能够阅读简单的Spring底层源码</td></tr></tbody></table></div><h1 id="SpringBoot基础篇"><a href="#SpringBoot基础篇" class="headerlink" title="SpringBoot基础篇"></a>SpringBoot基础篇</h1><h2 id="快速上手SpringBoot"><a href="#快速上手SpringBoot" class="headerlink" title="快速上手SpringBoot"></a>快速上手SpringBoot</h2><p>SpringBoot技术由Pivotal团队研发制作，功能的话简单概括就是加速Spring程序的开发，这个加速要从如下两个方面来说</p><ul><li>Spring程序初始搭建过程<ul><li>原始的Spring至少有一个配置文件或配置类，Web开发还需加载指定Spring配置。</li></ul></li><li>Spring程序的开发过程<ul><li>开发过程无外乎关于导入依赖，将相关类交给Spring管理。</li></ul></li></ul><h3 id="入门程序-一"><a href="#入门程序-一" class="headerlink" title="入门程序 (一)"></a>入门程序 (一)</h3><p>课程版本：</p><ul><li>IDEA 2020.3</li><li>Maven 3.6.1</li><li>JDK8</li></ul><div class="note info no-icon flat"><p>需求：使用SpringBoot快速构建SpringMVC程序</p></div><ul><li>步骤一：创建Spring工程<div class="img-wrap"><div class="img-bg"><img class="img" src="https://raw.githubusercontent.com/silvan2077/markdown_pic/main/Picgo/image-20211116125259385.png"/></div></div></li><li>步骤二：选择需要的技术集</li></ul><p>根据需求，左侧选择Web然后中间选择SpringMVC即可。<br><div class="img-wrap"><div class="img-bg"><img class="img" src="https://raw.githubusercontent.com/silvan2077/markdown_pic/main/Picgo/image-20211116125615728.png"/></div></div></p><ul><li>步骤三：开发控制器类<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><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">//Rest模式</span></span><br><span class="line"><span class="meta">@RestController</span></span><br><span class="line"><span class="meta">@RequestMapping(&quot;/books&quot;)</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">BookController</span> &#123;</span><br><span class="line">    <span class="meta">@GetMapping</span></span><br><span class="line">    <span class="keyword">public</span> String <span class="title function_">getById</span><span class="params">()</span>&#123;</span><br><span class="line">        System.out.println(<span class="string">&quot;springboot is running...&quot;</span>);</span><br><span class="line">        <span class="keyword">return</span> <span class="string">&quot;springboot is running...&quot;</span>;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></li></ul><p>​    入门案例制作的SpringMVC的控制器基于Rest风格开发，当然此处使用原始格式制作SpringMVC的程序也没有问题，上例中的<code>@RestController</code>与<code>@GetMapping</code>注解是基于<code>Restful</code>开发的典型注解</p><p>​<div class="note danger no-icon flat"><p>做到这里SpringBoot程序的最基础的开发已经做完了，现在就可以正常的运行Spring程序了。可能会有疑惑，Tomcat服务器没有配置，Spring也没有配置，什么都没有配置这就能用吗？这就是SpringBoot技术的强大之处。关于内部工作流程后面再说，现在只是展示开发步骤。</p></div></p><ul><li>步骤四：运行自动生成的Application类</li></ul><div class="img-wrap"><div class="img-bg"><img class="img" src="https://raw.githubusercontent.com/silvan2077/markdown_pic/main/Picgo/image-20211116130152452.png"/></div></div><p>​    使用带main方法的java程序的运行形式来运行程序，运行完毕后，控制台输出上述信息。</p><p>​    可以发现，SpringBoot启动成功，并且已经将8080端口映射到本机，这说明在内部SpringBoot容器为我们启动了一个Tomcat服务器，此时访问8080端口，就能测试功能是否正常工作了</p><blockquote><p>访问路径: <a href="http://localhost:8080/books">http://localhost:8080/books</a></p></blockquote><p>​<div class="note info no-icon flat"><p>是不是感觉很神奇？目前的效果其实依赖的底层逻辑还是很复杂的，但是从开发者角度来看，目前只有两个文件展现到了开发者面前</p></div></p><p>​<br><div class="tabs" id="springboot2入门程序一"><ul class="nav-tabs"><li class="tab active"><button type="button" data-href="#springboot2入门程序一-1">pom.xml</button></li><li class="tab"><button type="button" data-href="#springboot2入门程序一-2">Application</button></li></ul><div class="tab-contents"><div class="tab-item-content active" id="springboot2入门程序一-1"><p>该配置文件中两个信息需要关注，一个是<code>parent</code>，一个是<code>dependencies</code><br><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></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 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 https://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">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>2.5.4<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">groupId</span>&gt;</span>com.itheima<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>springboot_01_01_quickstart<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>0.0.1-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">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><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 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></p><button type="button" class="tab-to-top" aria-label="scroll to top"><i class="fas fa-arrow-up"></i></button></div><div class="tab-item-content" id="springboot2入门程序一-2"><ul><li>该类的执行的功能就一句代码，前面运行程序就是运行的这个类<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><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="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></li></ul><button type="button" class="tab-to-top" aria-label="scroll to top"><i class="fas fa-arrow-up"></i></button></div></div></div></p><p><strong>SpringBoot的优势：</strong></p><div class="table-container"><table><thead><tr><th><strong>类配置文件</strong></th><th><strong>Spring</strong></th><th><strong>SpringBoot</strong></th></tr></thead><tbody><tr><td>pom文件中的坐标</td><td><strong>手工添加</strong></td><td><strong>勾选添加</strong></td></tr><tr><td>web3.0配置类</td><td><strong>手工制作</strong></td><td><strong>无</strong></td></tr><tr><td>Spring/SpringMVC配置类</td><td><strong>手工制作</strong></td><td><strong>无</strong></td></tr><tr><td>控制器</td><td><strong>手工制作</strong></td><td><strong>手工制作</strong></td></tr></tbody></table></div><p>​    一句话总结一下就是<font color="#ff0000"><b>能少写就少写</b></font>，<font color="#ff0000"><b>能不写就不写</b></font>，这就是SpringBoot技术给我们带来的好处。</p><p><strong>总结</strong></p><ol><li>开发SpringBoot程序可以根据向导进行联网快速制作</li><li>SpringBoot程序需要基于JDK8以上版本进行制作</li><li>SpringBoot程序中需要使用何种功能通过勾选来选择技术，也可以手工添加对应的要使用的技术（后期讲解）</li><li>运行SpringBoot程序通过运行Application程序入口进行</li></ol><p><strong>思考</strong></p><p>​* 前面制作的时候说过，这个过程必须联网才可以进行，但是有些时候你会遇到一些莫名其妙的问题，比如基于Idea开发时，你会发现你配置了一些坐标，然后Maven下载对应东西的时候死慢死慢的，甚至还会失败。其实这和Idea这款IDE工具有关，万一Idea不能正常访问网络的话，我们是不是就无法制作SpringBoot程序了呢？下一节来解决这个问题</p><h3 id="入门程序（二）"><a href="#入门程序（二）" class="headerlink" title="入门程序（二）"></a>入门程序（二）</h3><p>​    开发SpringBoot也可以不基于任何IDE工具，直接在SprigBoot官网创建程序</p><p>​    SpringBoot官网和Spring的官网是在一起的，都是<a href="https://spring.io">https://spring.io</a>  。可以通过项目一级一级的找到SpringBoot技术的介绍页，然后在页面中间部位找到如下内容<br><div class="img-wrap"><div class="img-bg"><img class="img" src="https://raw.githubusercontent.com/silvan2077/markdown_pic/main/Picgo/image-20211122150444816.png"/></div></div></p><p><strong>步骤一：</strong>：点击Spring Initializr后进入到创建SpringBoot程序的界面上，下面是输入信息的过程，和前面的一样，只是界面变了而已，根据自己的要求，在左侧选择对应信息和输入对应的信息即可</p><div class="img-wrap"><div class="img-bg"><img class="img" src="https://raw.githubusercontent.com/silvan2077/markdown_pic/main/Picgo/image-20211122150608039.png"/></div></div><p><strong>步骤②</strong>：右侧的ADD DEPENDENCIES选择需要使用的技术，和之前勾选的Spring WEB是在做同一件事，仅仅是界面不同，点击后打开网页版的技术选择界面</p><div class="img-wrap"><div class="img-bg"><img class="img" src="https://raw.githubusercontent.com/silvan2077/markdown_pic/main/Picgo/image-20211122161257361.png"/></div></div><p><strong>步骤③</strong>：所有信息设置完毕后，点击下面左侧按钮，生成一个文件包,将该文件包解压后就是创建的SpringBoot工程文件夹了，直接导入IDEA即可使用</p><div class="note warning no-icon flat"><p>其实在IDEA工具中创建的SpringBoot工程和这种方式创建的SpringBoot工程一样，只是IDEA将界面进行了整合展示。</p></div><p><strong>思考</strong>     </p><ul><li>这两种创建方式都是访问国外的Spring主站，但是互联网访问是可以控制的，如果一天这个网站你在国内都无法访问了，那前面这两种方式都无法创建SpringBoot工程了，这时候又该怎么解决这个问题呢？</li></ul><h3 id="入门程序（三）"><a href="#入门程序（三）" class="headerlink" title="入门程序（三）"></a>入门程序（三）</h3><p>前面两种方式都是通过SpringBoot的官网创建的SpringBoot工程，那如果我们国内有这么一个网站能提供这样的功能，是不是就解决了呢？答案是肯定的,阿里就为我们提供了这样一个网站</p><p>​    创建工程时，切换选择starter服务路径，然后输入阿里云提供给我们的使用地址即可。地址：<code>http://start.aliyun.com或https://start.aliyun.com</code></p><div class="img-wrap"><div class="img-bg"><img class="img" src="https://raw.githubusercontent.com/silvan2077/markdown_pic/main/Picgo/image-20211122163605950.png"/></div></div><p>​    阿里为了便于自己开发使用，因此在依赖坐标中添加了一些阿里相关的技术，也是为了推广自己的技术吧，所以在依赖选择列表中有了更多的选择。不过阿里云地址默认创建的SpringBoot工程版本是<font color="#ff0000"><b>2.4.1</b></font>，所以如果想更换其他的版本，创建项目后手工修改即可，别忘了刷新一下，加载新版本信息<br><div class="img-wrap"><div class="img-bg"><img class="img" src="https://raw.githubusercontent.com/silvan2077/markdown_pic/main/Picgo/image-20211122163937408.png"/></div></div></p><p>​<br>​<div class="note warning no-icon flat"><p>注意：</p><ul><li>阿里云提供的工程创建地址初始化完毕后和使用SpringBoot官网创建出来的工程略有区别。主要是在配置文件的形式上有区别。这个信息在后面讲解Boot程序的执行流程时再次介绍。</li></ul></div></p><p><strong>思考</strong></p><ul><li>​做到这里已经有了三种方式创建SpringBoot工程，但是每种方式都要求你必须能上网才能创建工程。假如有一天，你加入了一个保密级别比较高的项目组，整个项目组没有外网，整个事情是不是就不能做了呢？</li></ul><h3 id="入门程序（四）"><a href="#入门程序（四）" class="headerlink" title="入门程序（四）"></a>入门程序（四）</h3><p>​    不能上网，还想创建SpringBoot工程，能不能做呢？能做，但是你要先问问自己联网和不联网到底差别是什么？这个信息找到以后，把联网要干的事情都提前准备好，就无需联网了。</p><p>​    联网做什么呢？首先SpringBoot工程也是基于Maven构建的，而Maven工程当使用了一些自己需要使用又不存在的东西时，就要去下载。其实SpringBoot工程创建的时候就是去下载一些必要的组件的。提前将这些组件准备好就可以避免联网了。</p><p><strong>步骤一</strong>：创建工程时，选择手工创建Maven工程</p><div class="img-wrap"><div class="img-bg"><img class="img" src="https://raw.githubusercontent.com/silvan2077/markdown_pic/main/Picgo/image-20211122165341684.png"/></div></div><p><strong>步骤二</strong>：参照标准SpringBoot工程的pom文件，书写自己的pom文件即可</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></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 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">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>2.5.4<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">groupId</span>&gt;</span>com.itheima<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>springboot_01_04_quickstart<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">maven.compiler.source</span>&gt;</span>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>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">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">dependencies</span>&gt;</span></span><br><span class="line"></span><br><span class="line"><span class="tag">&lt;/<span class="name">project</span>&gt;</span></span><br></pre></td></tr></table></figure><p><strong>步骤三</strong>：之前运行SpringBoot工程需要一个类，这个缺不了，手写一个就行了，建议按照之前的目录结构来创建，先别玩花样，先学走后学跑。类名可以自定义，关联的名称一起修改即可</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></pre></td><td class="code"><pre><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(&lt;Application.class);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>​    <font color="#ff0000"><b>关注</b></font>：类上面的注解@SpringBootApplication是核心注解一定不能少</p><p>​    <font color="#ff0000"><b>关注</b></font>：类名可以自定义，只要保障下面代码中使用的类名和你自己定义的名称一样即可，也就是run方法中的那个class对应的名称</p><p><strong>步骤四</strong>：下面就可以自己创建一个Controller测试一下是否能用了，和之前没有差别了</p><ul><li>其实实践一遍就明白了，前面集中方法和手写其实内容一样，只是帮我们直接生成了代码，更加方便一些</li></ul><div class="note warning no-icon flat"><p><strong>注意：</strong></p><ul><li>如果你的计算机上从来没有创建成功过SpringBoot工程，自然也就没有下载过SpringBoot对应的坐标，那用手写创建的方式在不联网的情况下肯定是不能用的。所谓手写，就是自己写别人帮你生成的东西，但是引用的坐标对应的资源必须保障maven仓库里面有才行，如果没有，还是要去下载的</li></ul></div><p><strong>总结</strong></p><ol><li>创建普通Maven工程</li><li>继承spring-boot-starter-parent</li><li>添加依赖spring-boot-starter-web</li><li>制作引导类Application</li></ol><h3 id="小技巧：隐藏指定文件-文件夹"><a href="#小技巧：隐藏指定文件-文件夹" class="headerlink" title="小技巧：隐藏指定文件/文件夹"></a>小技巧：隐藏指定文件/文件夹</h3><ul><li>无论哪种方式，创建SpringBoot工程时都会额外有一些用不到的文件夹，可能看起来就很臃肿别扭。</li><li>如果我们足够了解这些文件/目录的作用，处理这些用不到的文件夹的方法无外乎两种：<ul><li>删除</li><li>隐藏</li></ul></li><li>删除直接Delete就好，这里记录下隐藏方法</li></ul><p><strong>步骤一</strong>：打开设置，【Files】→【Settings】</p><div class="img-wrap"><div class="img-bg"><img class="img" src="https://raw.githubusercontent.com/silvan2077/markdown_pic/main/Picgo/image-20211122173835517.png"/></div></div><p><strong>步骤二</strong>：打开文件类型设置界面，【Editor】→【File Types】→【Ignored Files and Folders】，忽略文件或文件夹显示</p><div class="img-wrap"><div class="img-bg"><img class="img" src="https://raw.githubusercontent.com/silvan2077/markdown_pic/main/Picgo/image-20211122174020028.png"/></div></div><p><strong>步骤三</strong>：添加要隐藏的文件名称或文件夹名称，可以使用*号通配符，表示任意，设置完毕即可</p><h3 id="小结"><a href="#小结" class="headerlink" title="小结"></a>小结</h3><ul><li>这里简单介绍了SpringBoot的四种创建方案：<ul><li>IDEA脚手架创建</li><li>Spring官网创建</li><li>阿里脚手架创建</li><li>手动创建</li></ul></li></ul><div class="note warning no-icon flat"><p>前面三种创建方法都需要联网创建，手动创建的方式不需要联网但前提是maven仓库中已经有了需要的SpringBoot依赖</p></div><h2 id="SpringBoot简介"><a href="#SpringBoot简介" class="headerlink" title="SpringBoot简介"></a>SpringBoot简介</h2><p>通过入门案例，可以清楚的感受到SpringBoot的功能——<code>加速Spring程序开发</code>。</p><p>​SpringBoot在哪些方面进行了简化？</p><ul><li>起步依赖（简化依赖配置）<ul><li>依赖配置的书写简化就是靠这个起步依赖达成的，添加一个起步依赖即可引入许多相关坐标</li></ul></li><li>自动配置（简化常用工程相关配置）<ul><li>配置过于繁琐，使用自动配置就可以做响应的简化，但是内部还是很复杂的，后面具体展开说明</li></ul></li><li>辅助功能（内置服务器，……）<ul><li>除了上面的功能，其实SpringBoot程序还有其他的一些优势，比如我们没有配置Tomcat服务器，但是能正常运行，这是SpringBoot程序的一个可以感知到的功能，也是SpringBoot的辅助功能之一。</li></ul></li></ul><p>​    下面结合入门程序来说说这些简化操作都在哪些方面进行体现的，一共分为4个方面</p><ul><li>parent</li><li>starter</li><li>引导类</li><li>内嵌tomcat</li></ul><h3 id="parent"><a href="#parent" class="headerlink" title="parent"></a>parent</h3><ul><li><p>开发者在引入依赖时，对于一些依赖往往具有固定的搭配格式，因为这些依赖在不同版本可能存在冲突比如A技术的2.0版与B技术的3.5版可以合作在一起，但是和B技术的3.7版合并使用时就有冲突。而SpringBoot就将各种技术的常见依赖版本搭配收集整理起来，相当于SpringBoot做了无数个技术版本搭配的列表，这个技术搭配列表的名字叫做<code>parent</code></p></li><li><p><code>parent</code>自身存在多个版本，每个<code>parent</code>版本都有几百个其他版本的版本号，开发者不用考虑其他问题，使用某些技术时，直接使用SpringBoot提供的<code>parent</code>即可<br>​</p><div class="note info no-icon flat"><p>比如现在要使用Spring配合MyBatis开发</p><ul><li>没有<code>parent</code>之前需要选个Spring的版本，再选个MyBatis的版本，再把这些技术使用时关联的其他技术的版本逐一确定下来。当Spring的版本发生变化需要切换时，MyBatis版本有可能也要跟着切换，连关联技术可能都要切换，而且切换后还可能出现问题。</li><li>使用<code>parent</code>就不用关注不同技术间的版本冲突问题，只需要关注用到的技术即可</li></ul></div><p>​<br>​parent会不会将一些我不想使用的依赖也导入进来？</p></li><li>记清楚，这一点很关键，<code>parent</code>仅仅帮我们进行版本管理，它不负责导入坐标，说白了用什么还是你自己定，只不过版本不需要自己管理了。整体上来说，<font color="#ff0000"><b>使用parent可以帮助开发者进行版本的统一管理</b></font></li></ul><p>​    <font color="#ff0000"><b>关注</b></font>：parent定义出来以后，并不是直接使用的，仅仅给了开发者一个说明书，但是并没有实际使用，只有在项目中引入parent才会生效</p><ul><li><p>项目中的pom.xml中继承了一个坐标</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">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>2.5.4<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></pre></td></tr></table></figure></li><li><p>打开后可以查阅到其中又继承了一个坐标</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">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-dependencies<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.5.4<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></pre></td></tr></table></figure></li><li><p>这个坐标中定义了两组信息，第一组是各式各样的依赖版本号属性，第二组是各式各样的依赖坐标信息</p></li></ul><div class="tabs" id="parent1"><ul class="nav-tabs"><li class="tab active"><button type="button" data-href="#parent1-1">版本号属性</button></li><li class="tab"><button type="button" data-href="#parent1-2">依赖坐标信息</button></li></ul><div class="tab-contents"><div class="tab-item-content active" id="parent1-1"><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="tag">&lt;<span class="name">properties</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">activemq.version</span>&gt;</span>5.16.3<span class="tag">&lt;/<span class="name">activemq.version</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">aspectj.version</span>&gt;</span>1.9.7<span class="tag">&lt;/<span class="name">aspectj.version</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">assertj.version</span>&gt;</span>3.19.0<span class="tag">&lt;/<span class="name">assertj.version</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">commons-codec.version</span>&gt;</span>1.15<span class="tag">&lt;/<span class="name">commons-codec.version</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">commons-dbcp2.version</span>&gt;</span>2.8.0<span class="tag">&lt;/<span class="name">commons-dbcp2.version</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">commons-lang3.version</span>&gt;</span>3.12.0<span class="tag">&lt;/<span class="name">commons-lang3.version</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">commons-pool.version</span>&gt;</span>1.6<span class="tag">&lt;/<span class="name">commons-pool.version</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">commons-pool2.version</span>&gt;</span>2.9.0<span class="tag">&lt;/<span class="name">commons-pool2.version</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">h2.version</span>&gt;</span>1.4.200<span class="tag">&lt;/<span class="name">h2.version</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">hibernate.version</span>&gt;</span>5.4.32.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">hibernate-validator.version</span>&gt;</span>6.2.0.Final<span class="tag">&lt;/<span class="name">hibernate-validator.version</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">httpclient.version</span>&gt;</span>4.5.13<span class="tag">&lt;/<span class="name">httpclient.version</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">jackson-bom.version</span>&gt;</span>2.12.4<span class="tag">&lt;/<span class="name">jackson-bom.version</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">javax-jms.version</span>&gt;</span>2.0.1<span class="tag">&lt;/<span class="name">javax-jms.version</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">javax-json.version</span>&gt;</span>1.1.4<span class="tag">&lt;/<span class="name">javax-json.version</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">javax-websocket.version</span>&gt;</span>1.1<span class="tag">&lt;/<span class="name">javax-websocket.version</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">jetty-el.version</span>&gt;</span>9.0.48<span class="tag">&lt;/<span class="name">jetty-el.version</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">junit.version</span>&gt;</span>4.13.2<span class="tag">&lt;/<span class="name">junit.version</span>&gt;</span></span><br></pre></td></tr></table></figure><button type="button" class="tab-to-top" aria-label="scroll to top"><i class="fas fa-arrow-up"></i></button></div><div class="tab-item-content" id="parent1-2"><p>这些依赖坐标定义中没有具体的依赖版本号，而是引用了第一组信息中定义的依赖版本属性值<br><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></pre></td><td class="code"><pre><span class="line"><span class="tag">&lt;<span class="name">dependencyManagement</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">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 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>$&#123;junit.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 class="tag">&lt;/<span class="name">dependencyManagement</span>&gt;</span></span><br></pre></td></tr></table></figure></p><button type="button" class="tab-to-top" aria-label="scroll to top"><i class="fas fa-arrow-up"></i></button></div></div></div><div class="note warning no-icon flat"><p>注意：</p><ul><li>上面的依赖坐标定义在<code>&lt;dependencyManagement&gt;</code>标签中，这是依赖管理，不是直接导入依赖，只有我们在<code>&lt;dependencies&gt;</code>引入了这些依赖，Maven才会真正声明并下载对应版本的依赖</li><li>因为在maven中继承机会只有一次，上述 继承的格式还可以切换成导入的形式进行，并且在阿里云的starter创建工程时就使用了此种形式</li></ul></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></pre></td><td class="code"><pre><span class="line"><span class="tag">&lt;<span class="name">dependencyManagement</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">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-dependencies<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;spring-boot.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">type</span>&gt;</span>pom<span class="tag">&lt;/<span class="name">type</span>&gt;</span></span><br><span class="line">            <span class="tag">&lt;<span class="name">scope</span>&gt;</span>import<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 class="tag">&lt;/<span class="name">dependencies</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;/<span class="name">dependencyManagement</span>&gt;</span></span><br></pre></td></tr></table></figure><p><strong>总结</strong></p><ol><li>开发SpringBoot程序要继承spring-boot-starter-parent</li><li>spring-boot-starter-parent中定义了若干个依赖管理</li><li>继承parent模块可以避免多个依赖使用相同技术时出现依赖版本冲突</li><li>继承parent的形式也可以采用引入依赖的形式实现效果</li></ol><p><strong>思考</strong></p><p>​    parent中定义了若干个依赖版本管理，但是也没有使用，那这个设定也就不生效啊，究竟谁在使用这些定义呢？</p><h3 id="starter"><a href="#starter" class="headerlink" title="starter"></a>starter</h3><p>​SpringBoot关注到开发者在实际开发时，对于依赖坐标的使用往往都有一些固定的组合方式，比如使用spring-webmvc就一定要使用spring-web。每次都要固定搭配着写，非常繁琐，而且格式固定，没有任何技术含量。<br>于是SpringBoot将这些技术使用的固定搭配格式开发出来，以后开发者在使用某种技术时，就可以直接添加一个名字中包含<code>starter</code>的依赖，而不需要再添加其他的依赖了。</p><p>​    starter定义了使用某种技术时对于依赖的固定搭配格式，也是一种最佳解决方案，<font color="#ff0000"><b><strong>使用starter可以帮助开发者减少依赖配置</strong></b></font></p><p>​    入门案例中的web功能就是使用这种方式添加依赖的。可以查阅SpringBoot的配置源码，看到这些定义</p><ul><li>项目中的pom.xml定义了使用SpringMVC技术，但是并没有写SpringMVC的坐标，而是添加了一个名字中包含starter的依赖</li></ul><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.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></pre></td></tr></table></figure><ul><li>在spring-boot-starter-web中又定义了若干个具体依赖的坐标</li></ul><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></pre></td><td class="code"><pre><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<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.5.4<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>compile<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 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-json<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.5.4<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>compile<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 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-tomcat<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.5.4<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>compile<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 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<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-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>5.3.9<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>compile<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 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<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-webmvc<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.3.9<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>compile<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 class="tag">&lt;/<span class="name">dependencies</span>&gt;</span></span><br></pre></td></tr></table></figure><p>​    之前提到过开发SpringMVC程序需要导入spring-webmvc的坐标和spring整合web开发的坐标，就是上面这组坐标中的最后两个了。</p><p>​    但是我们发现除了这两个还有其他的，比如第二个，叫做spring-boot-starter-json。看名称就知道，这个是与json有关的坐标了，但是看名字发现和最后两个又不太一样，它的名字中也有starter，打开看看里面有什么？</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><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="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<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.5.4<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>compile<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 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<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-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>5.3.9<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>compile<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 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.fasterxml.jackson.core<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>jackson-databind<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.12.4<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>compile<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 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.fasterxml.jackson.datatype<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>jackson-datatype-jdk8<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.12.4<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>compile<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 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.fasterxml.jackson.datatype<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>jackson-datatype-jsr310<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.12.4<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>compile<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 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.fasterxml.jackson.module<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>jackson-module-parameter-names<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.12.4<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>compile<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 class="tag">&lt;/<span class="name">dependencies</span>&gt;</span></span><br></pre></td></tr></table></figure><p>​    我们可以发现，这个starter中又包含了若干个坐标，其实就是使用SpringMVC开发通常都会使用到Json，使用json又离不开这里面定义的这些坐标，SpringBoot把我们开发中使用的东西能用到的都给提前做好了。仔细看完会发现，里面有一些你没用过的。的确会出现这种过量导入的可能性，没关系，可以通过maven中的排除依赖剔除掉一部分。不过你不管它也没事，大不了就是过量导入呗。</p><p>​    到这里基本上得到了一个信息，使用starter可以帮开发者快速配置依赖关系。以前写依赖3个坐标的，现在导入一个就搞定了，就是加速依赖配置的。</p><p><strong>starter与parent的区别</strong></p><p>​    朦朦胧胧中感觉starter与parent好像都是帮助我们简化配置的，但是功能又不一样，梳理一下。</p><ul><li><p><code>starter</code>是一个坐标中定了若干个坐标，以前写多个的，现在写一个，<strong>用来减少依赖配置的书写量</strong></p></li><li><p><code>parent</code>定义了几百个依赖版本号，以前写依赖需要自己手工控制版本，现在由SpringBoot统一管理，这样就不存在版本冲突了，<strong>用来减少依赖冲突</strong></p></li></ul><p><strong>实际开发应用方式</strong></p><ul><li><p>实际开发中如果需要用什么技术，先去找有没有这个技术对应的starter</p><ul><li>如果有对应的starter，直接写starter，而且无需指定版本，版本由parent提供</li><li>如果没有对应的starter，手写坐标即可</li></ul></li><li><p>实际开发中如果发现坐标出现了冲突现象，确认你要使用的可行的版本号，使用手工书写的方式添加对应依赖，覆盖SpringBoot提供给我们的配置管理</p><ul><li>方式一：直接写坐标</li><li>方式二：覆盖<code>&lt;properties&gt;</code>中定义的版本号，就是下面这堆东西了，哪个冲突了覆盖哪个就OK了</li></ul><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></pre></td><td class="code"><pre><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">activemq.version</span>&gt;</span>5.16.3<span class="tag">&lt;/<span class="name">activemq.version</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">aspectj.version</span>&gt;</span>1.9.7<span class="tag">&lt;/<span class="name">aspectj.version</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">assertj.version</span>&gt;</span>3.19.0<span class="tag">&lt;/<span class="name">assertj.version</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">commons-codec.version</span>&gt;</span>1.15<span class="tag">&lt;/<span class="name">commons-codec.version</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">commons-dbcp2.version</span>&gt;</span>2.8.0<span class="tag">&lt;/<span class="name">commons-dbcp2.version</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">commons-lang3.version</span>&gt;</span>3.12.0<span class="tag">&lt;/<span class="name">commons-lang3.version</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">commons-pool.version</span>&gt;</span>1.6<span class="tag">&lt;/<span class="name">commons-pool.version</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">commons-pool2.version</span>&gt;</span>2.9.0<span class="tag">&lt;/<span class="name">commons-pool2.version</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">h2.version</span>&gt;</span>1.4.200<span class="tag">&lt;/<span class="name">h2.version</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">hibernate.version</span>&gt;</span>5.4.32.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">hibernate-validator.version</span>&gt;</span>6.2.0.Final<span class="tag">&lt;/<span class="name">hibernate-validator.version</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">httpclient.version</span>&gt;</span>4.5.13<span class="tag">&lt;/<span class="name">httpclient.version</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">jackson-bom.version</span>&gt;</span>2.12.4<span class="tag">&lt;/<span class="name">jackson-bom.version</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">javax-jms.version</span>&gt;</span>2.0.1<span class="tag">&lt;/<span class="name">javax-jms.version</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">javax-json.version</span>&gt;</span>1.1.4<span class="tag">&lt;/<span class="name">javax-json.version</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">javax-websocket.version</span>&gt;</span>1.1<span class="tag">&lt;/<span class="name">javax-websocket.version</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">jetty-el.version</span>&gt;</span>9.0.48<span class="tag">&lt;/<span class="name">jetty-el.version</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">junit.version</span>&gt;</span>4.13.2<span class="tag">&lt;/<span class="name">junit.version</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;/<span class="name">properties</span>&gt;</span></span><br></pre></td></tr></table></figure></li></ul><div class="note info no-icon flat"><p>SpringBoot官方给出了好多个<code>starter</code>的定义，方便我们使用，而且名称都是如下格式<br><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">命名规则：spring-boot-starter-技术名称</span><br></pre></td></tr></table></figure></p></div><p><strong>总结</strong></p><ol><li>开发SpringBoot程序需要导入坐标时通常导入对应的starter</li><li>每个不同的starter根据功能不同，通常包含多个依赖坐标</li><li>使用starter可以实现快速配置的效果，达到简化配置的目的</li></ol><h3 id="引导类"><a href="#引导类" class="headerlink" title="引导类"></a>引导类</h3><p>通过前面学习的<code>parent</code>和<code>starter</code>帮助我们减少了很多的配置工作，下面说一下程序是如何运行的。目前程序运行的入口就是SpringBoot工程创建时自带的带有main方法的那个类，运行这个类就可以启动SpringBoot工程的运行</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></pre></td><td class="code"><pre><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_">Springboot0101QuickstartApplication</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(Springboot0101QuickstartApplication.class, args);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>SpringBoot本身是为了加速Spring程序的开发的，而Spring程序运行的基础是需要创建自己的Spring容器对象（IoC容器）并将所有的对象交给Spring的容器管理。通过SpringBoot加速开发Spring程序，这个容器也一定在。当前这个类运行后就会产生一个Spring容器对象，并且可以将这个对象保存起来，通过容器对象直接操作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></pre></td><td class="code"><pre><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_">Springboot0101QuickstartApplication</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">ConfigurableApplicationContext</span> <span class="variable">ctx</span> <span class="operator">=</span> SpringApplication.run(Springboot0101QuickstartApplication.class, args);</span><br><span class="line">        <span class="type">BookController</span> <span class="variable">bean</span> <span class="operator">=</span> ctx.getBean(BookController.class);</span><br><span class="line">        System.out.println(<span class="string">&quot;bean======&gt;&quot;</span> + bean);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>​    通过上述操作不难看出，其实SpringBoot程序启动还是创建了一个Spring容器对象。这个类在SpringBoot程序中是所有功能的入口，称这个类为<code>引导类</code>。</p><p>​    作为一个引导类最典型的特征就是当前类上方声明了一个注解<code>@SpringBootApplication</code></p><p><strong>总结</strong></p><ol><li>SpringBoot工程提供引导类用来启动程序</li><li>SpringBoot工程启动后创建并初始化Spring容器</li></ol><p><strong>思考</strong><br>​    程序现在已经运行了。但是运行java程序不应该是执行完就结束了吗？但是我们现在明显是启动了一个web服务器，不然网页怎么能正常访问呢？这个服务器是在哪里写的呢？</p><h3 id="内嵌tomcat"><a href="#内嵌tomcat" class="headerlink" title="内嵌tomcat"></a>内嵌tomcat</h3><ul><li>当前我们做的SpringBoot入门案例勾选了<code>Spirng-web</code>的功能，并且导入了对应的starter。</li></ul><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.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></pre></td></tr></table></figure><div class="note info no-icon flat"><p>​如果我们要做Web程序，那么肯定离不开使用Web服务器，所以SpringBoot直接在程序中内嵌了一个tomcat服务器。由于这个功能不属于程序的主体功能，可用可不用，于是SpringBoot将其定位成辅助功能，这个辅助功能帮助我们开发者减少了许多设置性工作</p></div><p>围绕内置的tomcat服务器来研究几个问题</p><ol><li>这个服务器在什么位置定义的</li><li>这个服务器是怎么运行的</li><li>这个服务器如果想换怎么换？(一般不换)</li></ol><p><strong>内嵌Tomcat定义位置</strong></p><ul><li><p>当我们不开发Web程序时肯定用不到这个Tomcat服务器，而开发又要依赖这个服务器，所以SpringBoot将这个Tomcat服务器伴随着web的起步依赖导入进来</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.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></pre></td></tr></table></figure></li><li><p>打开查看web的starter导入了哪些东西</p></li></ul><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></pre></td><td class="code"><pre><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<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.5.4<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>compile<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 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-json<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.5.4<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>compile<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 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-tomcat<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.5.4<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>compile<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 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<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-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>5.3.9<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>compile<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 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<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-webmvc<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.3.9<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>compile<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 class="tag">&lt;/<span class="name">dependencies</span>&gt;</span></span><br></pre></td></tr></table></figure><ul><li>第三个依赖就是这个tomcat对应的东西了，也是一个starter，再打开看看<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></pre></td><td class="code"><pre><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>jakarta.annotation<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.annotation-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>1.3.5<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>compile<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 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.tomcat.embed<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-embed-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>9.0.52<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>compile<span class="tag">&lt;/<span class="name">scope</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">exclusions</span>&gt;</span></span><br><span class="line">            <span class="tag">&lt;<span class="name">exclusion</span>&gt;</span></span><br><span class="line">                <span class="tag">&lt;<span class="name">artifactId</span>&gt;</span>tomcat-annotations-api<span class="tag">&lt;/<span class="name">artifactId</span>&gt;</span></span><br><span class="line">                <span class="tag">&lt;<span class="name">groupId</span>&gt;</span>org.apache.tomcat<span class="tag">&lt;/<span class="name">groupId</span>&gt;</span></span><br><span class="line">            <span class="tag">&lt;/<span class="name">exclusion</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;/<span class="name">exclusions</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.apache.tomcat.embed<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-embed-el<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>9.0.52<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>compile<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 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.tomcat.embed<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-embed-websocket<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>9.0.52<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>compile<span class="tag">&lt;/<span class="name">scope</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">exclusions</span>&gt;</span></span><br><span class="line">            <span class="tag">&lt;<span class="name">exclusion</span>&gt;</span></span><br><span class="line">                <span class="tag">&lt;<span class="name">artifactId</span>&gt;</span>tomcat-annotations-api<span class="tag">&lt;/<span class="name">artifactId</span>&gt;</span></span><br><span class="line">                <span class="tag">&lt;<span class="name">groupId</span>&gt;</span>org.apache.tomcat<span class="tag">&lt;/<span class="name">groupId</span>&gt;</span></span><br><span class="line">            <span class="tag">&lt;/<span class="name">exclusion</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;/<span class="name">exclusions</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></pre></td></tr></table></figure></li></ul><p><code>tomcat-embed-core</code>是这里的核心，叫做<code>Tomcat内嵌核心</code>。就是这个东西把tomcat功能引入到了我们的程序中。</p><p><strong>内嵌Tomcat运行原理</strong></p><p>​    Tomcat服务器是一款软件，而且是一款使用java语言开发的软件，既然是java开发的，运行的时候肯定符合java程序运行的原理。而java程序运行靠的是对象，所以在SpringBoot中，<strong>Tomcat服务器以对象的形式在Spring容器中运行</strong>，具体运行的就是前面提到的Tomcat内嵌核心<br><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></pre></td><td class="code"><pre><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.apache.tomcat.embed<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-embed-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>9.0.52<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>compile<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 class="tag">&lt;/<span class="name">dependencies</span>&gt;</span></span><br></pre></td></tr></table></figure></p><ul><li>既然是对象，那么将这个对象从Spring容器中去掉就没有Web服务器的功能了。<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="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">exclusions</span>&gt;</span></span><br><span class="line">            <span class="tag">&lt;<span class="name">exclusion</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-tomcat<span class="tag">&lt;/<span class="name">artifactId</span>&gt;</span></span><br><span class="line">            <span class="tag">&lt;/<span class="name">exclusion</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;/<span class="name">exclusions</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></pre></td></tr></table></figure></li></ul><p>使用maven的排除依赖去掉了使用tomcat的starter。这下容器中就没有这个对象了，重新启动程序可以观察到程序运行了，但是并没有像之前那样运行后会等着用户发请求，而是直接停掉了，就是这个原因了。</p><p><strong>更换内嵌Tomcat</strong><br>SpringBoot提供了3款内置的服务器供我们更换</p><ul><li><code>tomcat(默认)</code>：apache出品，粉丝多，应用面广，负载了若干较重的组件</li><li><code>jetty</code>：更轻量级，负载性能远不及tomcat</li><li><code>undertow</code>：负载性能勉强跑赢tomcat</li></ul><p>想用哪个，加个坐标就OK。前提是把tomcat排除掉，因为tomcat是默认加载的。</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></pre></td><td class="code"><pre><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">exclusions</span>&gt;</span></span><br><span class="line">            <span class="tag">&lt;<span class="name">exclusion</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-tomcat<span class="tag">&lt;/<span class="name">artifactId</span>&gt;</span></span><br><span class="line">            <span class="tag">&lt;/<span class="name">exclusion</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;/<span class="name">exclusions</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.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-jetty<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">dependencies</span>&gt;</span></span><br></pre></td></tr></table></figure><p>​    现在就已经成功替换了web服务器，核心思想就是用什么加入对应坐标就可以了。如果有starter，优先使用starter。</p><p><strong>总结</strong></p><ol><li>内嵌Tomcat服务器是SpringBoot辅助功能之一</li><li>内嵌Tomcat工作原理是将Tomcat服务器作为对象运行，并将该对象交给Spring容器管理</li><li>变更内嵌服务器思想是去除现有服务器，添加全新的服务器启动器</li></ol><h2 id="SpringBoot基础配置"><a href="#SpringBoot基础配置" class="headerlink" title="SpringBoot基础配置"></a>SpringBoot基础配置</h2><p>​    通过入门案例，我们能够感知到一个信息，SpringBoot没有具体的功能，它的作用是辅助加快Spring程序的开发效率。SpringBoot有各种默认配置，这些默认配置都是为了简化Spring程序的开发而设计的，接下来我们来解决不想使用SpringBoot提供的默认配置的情况。</p><h3 id="属性配置"><a href="#属性配置" class="headerlink" title="属性配置"></a>属性配置</h3><p>​    SpringBoot通过配置文件<code>application.properties</code>就可以修改默认的配置，当前访问tomcat的默认端口是8080,如果我们想修改为80端口，该如何操作呢？<br><div class="img-wrap"><div class="img-bg"><img class="img" src="https://raw.githubusercontent.com/silvan2077/markdown_pic/main/Picgo/image-20211123165428245.png"/></div></div></p><ul><li><p>找到resource目录下的<code>application.properties</code>文件，尝试性的更改端口配置，当输入port时就发现IDEA给我们了许多提示</p><div class="img-wrap"><div class="img-bg"><img class="img" src="https://raw.githubusercontent.com/silvan2077/markdown_pic/main/Picgo/image-20211123165719091.png"/></div></div></li><li><p>​根据提示敲回车，输入80端口就完成了端口号的修改</p><figure class="highlight properties"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="attr">server.port</span>=<span class="string">80</span></span><br></pre></td></tr></table></figure></li></ul><p>从这个案例可以得到如下三个信息</p><ol><li>SpringBoot程序可以在<code>application.properties</code>文件中进行属性配置</li><li>application.properties文件中只要输入要配置的属性关键字就可以根据提示进行设置</li><li>SpringBoot将配置信息集中在一个文件中写，不管你是服务器的配置，还是数据库的配置，总之都写在一起，逃离一个项目十几种配置文件格式的尴尬局面</li></ol><ul><li>做完了端口的配置，趁热打铁，再做几个配置，目前项目启动时会显示一些日志信息，就来改一改这里面的一些设置。</li></ul><p><strong>关闭运行日志图表（banner)</strong></p><figure class="highlight properties"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="attr">spring.main.banner-mode</span>=<span class="string">off</span></span><br></pre></td></tr></table></figure><p><strong>设置运行日志的显示级别</strong></p><figure class="highlight properties"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="attr">logging.level.root</span>=<span class="string">debug</span></span><br></pre></td></tr></table></figure><ul><li>能够发现，这样搞配置比之前方便的多，之前不同的技术有专用的配置文件，文件的格式也不统一，而现在只需要在一个配置文件中统一格式进行配置。</li></ul><p><strong>该文件中能配置哪些属性？</strong></p><ul><li>从SpringBoot官方文档中，打开查看附录中的<a href="https://docs.spring.io/spring-boot/docs/current/reference/html/application-properties.html#application-properties">Application Properties</a>就可以查看所有的配置项。</li></ul><p><strong>这些配置项与什么技术有关？</strong></p><ul><li>这些配置项与SpringBoot的starter有关，我们引入了哪些技术的starter，就可以配置哪些属性。</li></ul><div class="note info no-icon flat"><p>所有的Starter都会依赖<code>spring-boot-starter</code>，该起步器是SpringBoot的基础起步器，里面定义了SpringBoot相关的基础配置<br><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="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<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.5.4<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>compile<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></pre></td></tr></table></figure></p></div><h3 id="配置文件分类"><a href="#配置文件分类" class="headerlink" title="配置文件分类"></a>配置文件分类</h3><p>现在能够在properties格式的文件下进行SpringBoot相关的配置了，但是有的人认为properties格式的配置在一些场景下会很繁琐，如一堆配置有相同的前缀，每次配置都要写一遍。</p><p>SpringBoot除了支持properties格式的配置文件，还支持另外两种格式的配置文件。分别如下:</p><ul><li>properties格式</li><li>yml格式</li><li>yaml格式</li></ul><div class="tabs" id="配置文件分类"><ul class="nav-tabs"><li class="tab active"><button type="button" data-href="#配置文件分类-1">properties格式</button></li><li class="tab"><button type="button" data-href="#配置文件分类-2">yml格式</button></li><li class="tab"><button type="button" data-href="#配置文件分类-3">yaml格式</button></li></ul><div class="tab-contents"><div class="tab-item-content active" id="配置文件分类-1"><figure class="highlight properties"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="attr">server.port</span>=<span class="string">80</span></span><br></pre></td></tr></table></figure><button type="button" class="tab-to-top" aria-label="scroll to top"><i class="fas fa-arrow-up"></i></button></div><div class="tab-item-content" id="配置文件分类-2"><figure class="highlight yml"><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="attr">server:</span></span><br><span class="line">  <span class="attr">port:</span> <span class="number">81</span></span><br></pre></td></tr></table></figure><button type="button" class="tab-to-top" aria-label="scroll to top"><i class="fas fa-arrow-up"></i></button></div><div class="tab-item-content" id="配置文件分类-3"><figure class="highlight yaml"><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="attr">server:</span></span><br><span class="line">  <span class="attr">port:</span> <span class="number">82</span></span><br></pre></td></tr></table></figure><button type="button" class="tab-to-top" aria-label="scroll to top"><i class="fas fa-arrow-up"></i></button></div></div></div><div class="note warning no-icon flat"><ul><li>yml格式和yaml格式是完全一样的，只是文件后缀不同，所以可以合并成一种格式</li><li>以后yml格式使用较多，因为它的格式更简单，更易读</li></ul></div><p><strong>思考</strong></p><p>现在我们已经知道使用三种格式都可以做配置了，那么问题来了，万一我三个都写了，他们三个谁说了算呢？</p><h3 id="配置文件优先级"><a href="#配置文件优先级" class="headerlink" title="配置文件优先级"></a>配置文件优先级</h3><div class="note info no-icon flat"><p>​三个文件如果共存的话，谁生效说的就是配置文件加载的优先级别。这种情况很少出现，但是这个知识还是可以学习一下的。我们就让三个配置文件书写同样的信息，比如都配置端口，然后我们让每个文件配置的端口号都不一样，最后启动程序后看启动端口是多少就知道谁的加载优先级比较高了。</p></div><div class="tabs" id="配置文件分类1"><ul class="nav-tabs"><li class="tab active"><button type="button" data-href="#配置文件分类1-1">properties格式</button></li><li class="tab"><button type="button" data-href="#配置文件分类1-2">yml格式</button></li><li class="tab"><button type="button" data-href="#配置文件分类1-3">yaml格式</button></li></ul><div class="tab-contents"><div class="tab-item-content active" id="配置文件分类1-1"><figure class="highlight properties"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="attr">server.port</span>=<span class="string">80</span></span><br></pre></td></tr></table></figure><button type="button" class="tab-to-top" aria-label="scroll to top"><i class="fas fa-arrow-up"></i></button></div><div class="tab-item-content" id="配置文件分类1-2"><figure class="highlight yml"><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="attr">server:</span></span><br><span class="line">  <span class="attr">port:</span> <span class="number">81</span></span><br></pre></td></tr></table></figure><button type="button" class="tab-to-top" aria-label="scroll to top"><i class="fas fa-arrow-up"></i></button></div><div class="tab-item-content" id="配置文件分类1-3"><figure class="highlight yaml"><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="attr">server:</span></span><br><span class="line">  <span class="attr">port:</span> <span class="number">82</span></span><br></pre></td></tr></table></figure><button type="button" class="tab-to-top" aria-label="scroll to top"><i class="fas fa-arrow-up"></i></button></div></div></div><p>​启动后发现目前的启动端口为80，把80对应的文件删除掉，然后再启动，现在端口又改成了81。现在我们就已经知道了3个文件的加载优先顺序是什么</p><div class="note danger no-icon flat"><p>application.properties  &gt;  application.yml  &gt;  application.yaml</p></div><p>最后我们把配置文件内容给修改一下</p><div class="tabs" id="配置文件分类1"><ul class="nav-tabs"><li class="tab active"><button type="button" data-href="#配置文件分类1-1">properties格式</button></li><li class="tab"><button type="button" data-href="#配置文件分类1-2">yml格式</button></li><li class="tab"><button type="button" data-href="#配置文件分类1-3">yaml格式</button></li></ul><div class="tab-contents"><div class="tab-item-content active" id="配置文件分类1-1"><figure class="highlight properties"><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="attr">server.port</span>=<span class="string">80</span></span><br><span class="line"><span class="attr">spring.main.banner-mode</span>=<span class="string">off</span></span><br></pre></td></tr></table></figure><button type="button" class="tab-to-top" aria-label="scroll to top"><i class="fas fa-arrow-up"></i></button></div><div class="tab-item-content" id="配置文件分类1-2"><figure class="highlight yml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><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="attr">server:</span></span><br><span class="line">  <span class="attr">port:</span> <span class="number">81</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">debug</span></span><br></pre></td></tr></table></figure><button type="button" class="tab-to-top" aria-label="scroll to top"><i class="fas fa-arrow-up"></i></button></div><div class="tab-item-content" id="配置文件分类1-3"><figure class="highlight yaml"><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="attr">server:</span></span><br><span class="line">  <span class="attr">port:</span> <span class="number">82</span></span><br></pre></td></tr></table></figure><button type="button" class="tab-to-top" aria-label="scroll to top"><i class="fas fa-arrow-up"></i></button></div></div></div><p>​此时发现不仅端口生效了，最终显示80，同时其他两条配置也生效了，看来每个配置文件中的项都会生效，只不过如果多个配置文件中有相同类型的配置会优先级高的文件覆盖优先级的文件中的配置。如果配置项不同的话，那所有的配置项都会生效。</p><div class="note danger no-icon flat"><p><strong>总结</strong></p><ol><li>配置文件间的加载优先级    properties（最高）&gt;  yml  &gt;  yaml（最低）</li><li>不同配置文件中相同配置按照加载优先级相互覆盖，不同配置文件中不同配置全部保留 </li></ol></div><p><strong>总结</strong></p><ol><li><p>指定SpringBoot配置文件</p><ul><li>Setting → Project Structure → Facets</li><li>选中对应项目/工程</li><li>Customize Spring Boot</li><li>选择配置文件</li></ul></li></ol><h3 id="yaml文件"><a href="#yaml文件" class="headerlink" title="yaml文件"></a>yaml文件</h3><p>SpringBoot的配置主要使用yml结尾的这种文件格式，并且在书写时可以通过提示的形式加载正确的格式。但是这种文件还是有严格的书写格式要求的。下面就来说一下具体的语法格式。</p><p><code>YAML（YAML Ain&#39;t Markup Language）</code>，一种数据序列化格式。具有容易阅读、容易与脚本语言交互、以数据为核心，重数据轻格式的特点。常见的文件扩展名有两种：</p><ul><li>.yml格式（主流）</li><li>.yaml格式</li></ul><p>对于文件自身在书写时，具有严格的语法格式要求，具体如下：</p><ol><li>大小写敏感</li><li>属性层级关系使用多行描述，<strong>每行结尾使用冒号结束</strong></li><li>使用缩进表示层级关系，同层级左侧对齐，<strong>不允许使用Tab键</strong></li><li>属性值前面添加空格（属性名与属性值之间使用冒号+空格作为分隔）</li><li><code>#</code>号 表示注释</li></ol><div class="note warning no-icon flat"><p><strong>核心规则：数据前面要加空格与冒号隔开</strong></p></div><p>​下面列出常见的数据书写格式，熟悉一下<br><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="attr">boolean:</span> <span class="literal">TRUE</span>  <span class="comment">#TRUE,true,True,FALSE,false，False均可</span></span><br><span class="line"><span class="attr">float:</span> <span class="number">3.14</span>    <span class="comment">#6.8523015e+5  #支持科学计数法</span></span><br><span class="line"><span class="attr">int:</span> <span class="number">123</span>       <span class="comment">#0b1010_0111_0100_1010_1110    #支持二进制、八进制、十六进制</span></span><br><span class="line"><span class="attr">null:</span> <span class="string">~</span>        <span class="comment">#使用~表示null</span></span><br><span class="line"><span class="attr">string:</span> <span class="string">HelloWorld</span>      <span class="comment">#字符串可以直接书写</span></span><br><span class="line"><span class="attr">string2:</span> <span class="string">&quot;Hello World&quot;</span>  <span class="comment">#可以使用双引号包裹特殊字符</span></span><br><span class="line"><span class="attr">date:</span> <span class="number">2018-02-17</span>        <span class="comment">#日期必须使用yyyy-MM-dd格式</span></span><br><span class="line"><span class="attr">datetime:</span> <span class="number">2018-02-17T15:02:31+08:00</span>  <span class="comment">#时间和日期之间使用T连接，最后使用+代表时区</span></span><br></pre></td></tr></table></figure></p><p>此外，yaml格式中也可以表示数组，在属性名书写位置的下方使用减号作为数据开始符号，每行书写一个数据，减号与数据间空格分隔</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></pre></td><td class="code"><pre><span class="line"><span class="attr">subject:</span></span><br><span class="line"><span class="bullet">-</span> <span class="string">Java</span></span><br><span class="line"><span class="bullet">-</span> <span class="string">前端</span></span><br><span class="line"><span class="bullet">-</span> <span class="string">大数据</span></span><br><span class="line"><span class="attr">enterprise:</span></span><br><span class="line"><span class="attr">name:</span> <span class="string">itcast</span></span><br><span class="line">    <span class="attr">age:</span> <span class="number">16</span></span><br><span class="line">    <span class="attr">subject:</span></span><br><span class="line">    <span class="bullet">-</span> <span class="string">Java</span></span><br><span class="line">        <span class="bullet">-</span> <span class="string">前端</span></span><br><span class="line">        <span class="bullet">-</span> <span class="string">大数据</span></span><br><span class="line"><span class="attr">likes:</span> [<span class="string">王者荣耀</span>,<span class="string">刺激战场</span>]<span class="comment">#数组书写缩略格式</span></span><br><span class="line"><span class="attr">users:</span> <span class="comment">#对象数组格式一</span></span><br><span class="line">  <span class="bullet">-</span> <span class="attr">name:</span> <span class="string">Tom</span></span><br><span class="line">   <span class="attr">age:</span> <span class="number">4</span></span><br><span class="line">  <span class="bullet">-</span> <span class="attr">name:</span> <span class="string">Jerry</span></span><br><span class="line">    <span class="attr">age:</span> <span class="number">5</span></span><br><span class="line"><span class="attr">users:</span> <span class="comment">#对象数组格式二</span></span><br><span class="line">  <span class="bullet">-</span>  </span><br><span class="line">    <span class="attr">name:</span> <span class="string">Tom</span></span><br><span class="line">    <span class="attr">age:</span> <span class="number">4</span></span><br><span class="line">  <span class="bullet">-</span>   </span><br><span class="line">    <span class="attr">name:</span> <span class="string">Jerry</span></span><br><span class="line">    <span class="attr">age:</span> <span class="number">5</span>    </span><br><span class="line"><span class="attr">users2:</span> [ &#123; <span class="string">name:Tom</span> , <span class="string">age:4</span> &#125; , &#123; <span class="string">name:Jerry</span> , <span class="string">age:5</span> &#125; ]<span class="comment">#对象数组缩略格式</span></span><br></pre></td></tr></table></figure><p><strong>总结</strong></p><ol><li>yaml语法规则<ul><li>大小写敏感</li><li>属性层级关系使用多行描述，每行结尾使用冒号结束</li><li>使用缩进表示层级关系，同层级左侧对齐，<strong>只允许使用空格（不允许使用Tab键）</strong></li><li>属性值前面添加空格（属性名与属性值之间使用冒号+空格作为分隔）</li><li><code>#</code>号 表示注释</li></ul></li><li>注意属性名冒号后面与数据之间有一个<strong>空格</strong></li><li>字面值、对象数据格式、数组数据格式</li></ol><p><strong>思考</strong><br>我们现在在文件中定义的数据都是给SpringBoot框架内部使用，都是内部定义好的，如果现在想配置一些数据在自己写的代码中使用，该如何操作呢？</p><h3 id="yaml数据读取"><a href="#yaml数据读取" class="headerlink" title="yaml数据读取"></a>yaml数据读取</h3><p>对于yaml文件中的数据，可以想象成一个小型的数据库，里面保存着若干数据，每个数据有一个独立的名字，下面介绍3种读取数据的方法</p><h4 id="读取单一数据"><a href="#读取单一数据" class="headerlink" title="读取单一数据"></a>读取单一数据</h4><p>​    yaml中保存的单个数据，可以使用Spring中的注解直接读取，使用<code>@Value</code>可以读取单个数据，属性名引用方式：<code>$&#123;一级属性名.二级属性名……&#125;</code></p><div class="img-wrap"><div class="img-bg"><img class="img" src="https://raw.githubusercontent.com/silvan2077/markdown_pic/main/Picgo/image-20211126180433356.png"/></div></div><p>使用<code>@Value</code>注解时，将该注解写在某一个指定的Spring管控的bean的属性名上方即可</p><p><strong>总结</strong></p><h4 id="读取全部数据"><a href="#读取全部数据" class="headerlink" title="读取全部数据"></a>读取全部数据</h4><p>可以使用<code>value</code>注解可以读取单一数据，但定义的数据量过大时，一个一个写非常麻烦，SpringBoot为我们提供了一个对象，可以将所有数据都封装到这一个对象，这个对象叫做Environment，使用自动装配注解可以将所有的yaml数据封装到这个对象中</p><div class="img-wrap"><div class="img-bg"><img class="img" src="https://raw.githubusercontent.com/silvan2077/markdown_pic/main/Picgo/image-20211126180738569.png"/></div></div><p>​<br>数据封装到了Environment对象后，可以通过Environment接口提供的<code>getProperties（String）</code>方法获取属性值，参数填写属性名即可</p><h4 id="读取对象数据"><a href="#读取对象数据" class="headerlink" title="读取对象数据"></a>读取对象数据</h4><ul><li><p>读取对象数据时，使用单一数据读取书写比较繁琐，全数据封装又封装的太厉害了，每次拿数据还要一个一个的<code>getProperties(String)</code>,总之用起来都不是很舒服。由于Java是一个面向对象的语言，很多情况下，我们会将一组数据封装成一个对象。SpringBoot也提供了可以将一组yaml对象数据封装一个Java对象的操作</p></li><li><p>首先定义一个对象，并将该对象纳入Spring管控的范围，也就是定义成一个bean，然后使用注解<code>@ConfigurationProperties</code>指定该对象加载哪一组yaml中配置的信息。</p></li></ul><div class="img-wrap"><div class="img-bg"><img class="img" src="https://raw.githubusercontent.com/silvan2077/markdown_pic/main/Picgo/image-20211126181423432.png"/></div></div><p>​这个<code>@ConfigurationProperties</code>需要告诉他加载的数据前缀是什么，这样当前前缀下的所有属性就会封装到这个对象中。数据属性名要与对象的变量名一一对应，不然没法封装。其实以后如果你要定义一组数据自己使用，就可以先写一个对象，然后定义好属性，下面到配置中根据这个格式书写即可。</p><p>​<div class="img-wrap"><div class="img-bg"><img class="img" src="https://raw.githubusercontent.com/silvan2077/markdown_pic/main/Picgo/image-20211126181423432.png"/></div></div></p><div class="note warning no-icon flat"><p>自定义的数据在yaml文件中书写时没有提示弹出，等到了原理篇再学习如何弹出提示</p></div><p><strong>总结</strong></p><ol><li>使用@ConfigurationProperties注解绑定配置信息到封装类中</li><li>封装类需要定义为Spring管理的bean，否则无法进行属性注入</li></ol><h4 id="yaml文件中的数据引用"><a href="#yaml文件中的数据引用" class="headerlink" title="yaml文件中的数据引用"></a>yaml文件中的数据引用</h4><p>如果你在书写yaml数据时，经常出现如下现象，比如很多个文件都具有相同的目录前缀</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></pre></td><td class="code"><pre><span class="line"><span class="attr">center:</span></span><br><span class="line"><span class="attr">dataDir:</span> <span class="string">/usr/local/fire/data</span></span><br><span class="line">    <span class="attr">tmpDir:</span> <span class="string">/usr/local/fire/tmp</span></span><br><span class="line">    <span class="attr">logDir:</span> <span class="string">/usr/local/fire/log</span></span><br><span class="line">    <span class="attr">msgDir:</span> <span class="string">/usr/local/fire/msgDir</span></span><br></pre></td></tr></table></figure><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></pre></td><td class="code"><pre><span class="line"><span class="attr">center:</span></span><br><span class="line"><span class="attr">dataDir:</span> <span class="string">D:/usr/local/fire/data</span></span><br><span class="line">    <span class="attr">tmpDir:</span> <span class="string">D:/usr/local/fire/tmp</span></span><br><span class="line">    <span class="attr">logDir:</span> <span class="string">D:/usr/local/fire/log</span></span><br><span class="line">    <span class="attr">msgDir:</span> <span class="string">D:/usr/local/fire/msgDir</span></span><br></pre></td></tr></table></figure><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></pre></td><td class="code"><pre><span class="line"><span class="attr">baseDir:</span> <span class="string">/usr/local/fire</span></span><br><span class="line"><span class="attr">center:</span></span><br><span class="line">    <span class="attr">dataDir:</span> <span class="string">$&#123;baseDir&#125;/data</span></span><br><span class="line">    <span class="attr">tmpDir:</span> <span class="string">$&#123;baseDir&#125;/tmp</span></span><br><span class="line">    <span class="attr">logDir:</span> <span class="string">$&#123;baseDir&#125;/log</span></span><br><span class="line">    <span class="attr">msgDir:</span> <span class="string">$&#123;baseDir&#125;/msgDir</span></span><br></pre></td></tr></table></figure><p>​    还有一个注意事项，在书写字符串时，如果需要使用转义字符，需要将数据字符串使用双引号包裹起来</p><figure class="highlight yaml"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="attr">lesson:</span> <span class="string">&quot;Spring\tboot\nlesson&quot;</span></span><br></pre></td></tr></table></figure><p><strong>总结</strong></p><ol><li>在配置文件中可以使用${属性名}方式引用属性值</li><li>如果属性中出现特殊字符，可以使用双引号包裹起来作为字符解析</li></ol><h2 id="基于SpringBoot实现SSMP整合"><a href="#基于SpringBoot实现SSMP整合" class="headerlink" title="基于SpringBoot实现SSMP整合"></a>基于SpringBoot实现SSMP整合</h2><p>SpringBoot之所以好用，就是它能方便快捷的整合其他技术，这一部分就来学习如何快捷方便的整合其他技术。这一章咱们学习如下技术的整合方式</p><ul><li>整合JUnit</li><li>整合MyBatis</li><li>整合MyBatis-Plus</li><li>整合Druid<h3 id="整合JUnit"><a href="#整合JUnit" class="headerlink" title="整合JUnit"></a>整合JUnit</h3></li></ul><div class="note info no-icon flat"><p>SpringBoot技术的定位用于<code>简化开发</code>，再具体点是简化Spring程序的开发。所以在整合任意技术的时候，如果你想直观感触到简化的效果，必须先知道使用非SpringBoot技术时对应的整合是如何做的，然后再看基于SpringBoot的整合是如何做的，才能比对出来简化在了哪里。</p></div><p>不使用SpringBoot技术时，Spring整合<code>JUnit</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></pre></td><td class="code"><pre><span class="line"><span class="comment">//加载spring整合junit专用的类运行器</span></span><br><span class="line"><span class="meta">@RunWith(SpringJUnit4ClassRunner.class)</span></span><br><span class="line"><span class="comment">//指定对应的配置信息</span></span><br><span class="line"><span class="meta">@ContextConfiguration(classes = SpringConfig.class)</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">AccountServiceTestCase</span> &#123;</span><br><span class="line">    <span class="comment">//注入你要测试的对象</span></span><br><span class="line">    <span class="meta">@Autowired</span></span><br><span class="line">    <span class="keyword">private</span> AccountService accountService;</span><br><span class="line">    <span class="comment">// 添加测试注解</span></span><br><span class="line">    <span class="meta">@Test</span></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">testGetById</span><span class="params">()</span>&#123;</span><br><span class="line">        <span class="comment">//执行要测试的对象对应的方法</span></span><br><span class="line">        System.out.println(accountService.findById(<span class="number">2</span>));</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br></pre></td></tr></table></figure><ul><li>核心代码是前两个注解，第一个注解<code>@RunWith</code>是设置Spring专用于测试的类运行器，简单说就是Spring程序执行程序有自己的一套独立的运行程序的方式，不能使用JUnit提供的类运行方式了，必须指定一下，但是格式是固定，第二个注解<code>@ContextConfiguration</code>是用来设置Spring核心配置文件或配置类的，简单说就是加载Spring的环境你要告诉Spring具体的环境配置是在哪里写的，虽然每次加载的文件都有可能不同，但是仔细想想，如果文件名是固定的，这个貌似也是一个固定格式。<font color="#ff0000"><b>既然有可能是固定格式，那就有可能每次都写一样的东西，也是一个没有技术含量的内容书写</b></font></li></ul><p>SpringBoot就将上述两条没有技术含量的内容书写进行开发简化，能走默认值的走默认值，能不写的就不写，具体格式如下</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">@SpringBootTest</span></span><br><span class="line"><span class="keyword">class</span> <span class="title class_">Springboot04JunitApplicationTests</span> &#123;</span><br><span class="line">    <span class="comment">//注入你要测试的对象</span></span><br><span class="line">    <span class="meta">@Autowired</span></span><br><span class="line">    <span class="keyword">private</span> BookDao bookDao;</span><br><span class="line">    <span class="meta">@Test</span></span><br><span class="line">    <span class="keyword">void</span> <span class="title function_">contextLoads</span><span class="params">()</span> &#123;</span><br><span class="line">        <span class="comment">//执行要测试的对象对应的方法</span></span><br><span class="line">        bookDao.save();</span><br><span class="line">        System.out.println(<span class="string">&quot;two...&quot;</span>);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>SpringBoot直接使用<code>@SpringBootTest</code>一个注解就解决了，而且没有参数。至于内部是怎么回事？和之前一样，只不过都走的默认值。通过默认值加载的配置类或配置文件是哪个？就是前面启动程序使用的引导类。也可以通过给<code>@SpringBootTest</code>注解添加classes属性来指定配置类。<br><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><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">@SpringBootTest(classes = Springboot04JunitApplication.class)</span></span><br><span class="line"><span class="keyword">class</span> <span class="title class_">Springboot04JunitApplicationTests</span> &#123;</span><br><span class="line">    <span class="comment">//注入你要测试的对象</span></span><br><span class="line">    <span class="meta">@Autowired</span></span><br><span class="line">    <span class="keyword">private</span> BookDao bookDao;</span><br><span class="line">    <span class="meta">@Test</span></span><br><span class="line">    <span class="keyword">void</span> <span class="title function_">contextLoads</span><span class="params">()</span> &#123;</span><br><span class="line">        <span class="comment">//执行要测试的对象对应的方法</span></span><br><span class="line">        bookDao.save();</span><br><span class="line">        System.out.println(<span class="string">&quot;two...&quot;</span>);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></p><p>​    <font color="#f0f"><b>温馨提示</b></font></p><div class="note warning no-icon flat"><p>使用SpringBoot整合JUnit需要保障导入test对应的<code>starter</code>，由于初始化项目时此项是默认导入的，所以此处没有提及，其实和之前学习的内容一样，用什么技术导入对应的starter即可。</p></div>​**总结**1. 导入测试对应的`starter`2. 测试类使用`@SpringBootTest`修饰3. 使用`自动装配`的形式添加要测试的对象4. 测试类如果存在于引导类所在包或子包中无需指定引导类5. 测试类如果不存在于引导类所在的包或子包中需要通过classes属性指定引导类### 整合MyBatis接下来整合MyBatis。下面列举出原始Spring整合Mybatis的过程，以配置类的形式为例进行。- 导入坐标MyBatis坐标不能少，Spring整合MyBatis还有自己专用的坐标，此外Spring进行数据库操作的jdbc坐标是必须的，剩下还有mysql驱动坐标，本例中使用了Druid数据源，这个倒是可以不要  <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="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>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.1.16<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.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>3.5.6<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>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-java<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.1.47<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="comment">&lt;!--1.导入mybatis与spring整合的jar包--&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-spring<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.3.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="comment">&lt;!--导入spring操作数据库必选的包--&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<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-jdbc<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.2.10.RELEASE<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></pre></td></tr></table></figure><ul><li>Spring核心配置</li></ul><p>添加核心配置类<br><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><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="meta">@Configuration</span></span><br><span class="line"><span class="meta">@ComponentScan(&quot;com.itheima&quot;)</span></span><br><span class="line"><span class="meta">@PropertySource(&quot;jdbc.properties&quot;)</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">SpringConfig</span> &#123;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></p><ul><li><p>MyBatis要交给Spring接管的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></pre></td><td class="code"><pre><span class="line"><span class="comment">//定义mybatis专用的配置类</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_">MyBatisConfig</span> &#123;</span><br><span class="line"><span class="comment">//    定义创建SqlSessionFactory对应的bean</span></span><br><span class="line">    <span class="meta">@Bean</span></span><br><span class="line">    <span class="keyword">public</span> SqlSessionFactoryBean <span class="title function_">sqlSessionFactory</span><span class="params">(DataSource dataSource)</span>&#123;</span><br><span class="line">        <span class="comment">//SqlSessionFactoryBean是由mybatis-spring包提供的，专用于整合用的对象</span></span><br><span class="line">        <span class="type">SqlSessionFactoryBean</span> <span class="variable">sfb</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">SqlSessionFactoryBean</span>();</span><br><span class="line">        <span class="comment">//设置数据源替代原始配置中的environments的配置</span></span><br><span class="line">        sfb.setDataSource(dataSource);</span><br><span class="line">        <span class="comment">//设置类型别名替代原始配置中的typeAliases的配置</span></span><br><span class="line">        sfb.setTypeAliasesPackage(<span class="string">&quot;com.itheima.domain&quot;</span>);</span><br><span class="line">        <span class="keyword">return</span> sfb;</span><br><span class="line">    &#125;</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> MapperScannerConfigurer <span class="title function_">mapperScannerConfigurer</span><span class="params">()</span>&#123;</span><br><span class="line">        <span class="type">MapperScannerConfigurer</span> <span class="variable">msc</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">MapperScannerConfigurer</span>();</span><br><span class="line">        msc.setBasePackage(<span class="string">&quot;com.itheima.dao&quot;</span>);</span><br><span class="line">        <span class="keyword">return</span> msc;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></li><li><p>数据源对应的bean，此处使用Druid数据源</p></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="meta">@Configuration</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">JdbcConfig</span> &#123;</span><br><span class="line">    <span class="meta">@Value(&quot;$&#123;jdbc.driver&#125;&quot;)</span></span><br><span class="line">    <span class="keyword">private</span> String driver;</span><br><span class="line">    <span class="meta">@Value(&quot;$&#123;jdbc.url&#125;&quot;)</span></span><br><span class="line">    <span class="keyword">private</span> String url;</span><br><span class="line">    <span class="meta">@Value(&quot;$&#123;jdbc.username&#125;&quot;)</span></span><br><span class="line">    <span class="keyword">private</span> String userName;</span><br><span class="line">    <span class="meta">@Value(&quot;$&#123;jdbc.password&#125;&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="meta">@Bean(&quot;dataSource&quot;)</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">DruidDataSource</span> <span class="variable">ds</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">DruidDataSource</span>();</span><br><span class="line">        ds.setDriverClassName(driver);</span><br><span class="line">        ds.setUrl(url);</span><br><span class="line">        ds.setUsername(userName);</span><br><span class="line">        ds.setPassword(password);</span><br><span class="line">        <span class="keyword">return</span> ds;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><ul><li><p>数据库连接信息（properties格式）</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></pre></td><td class="code"><pre><span class="line"><span class="attr">jdbc.driver</span>=<span class="string">com.mysql.jdbc.Driver</span></span><br><span class="line"><span class="attr">jdbc.url</span>=<span class="string">jdbc:mysql://localhost:3306/spring_db?useSSL=false</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">root</span></span><br></pre></td></tr></table></figure><div class="note info no-icon flat"><p>上述格式基本上是最简单的格式了，要写的东西不少。下面看看SpringBoot整合MyBaits格式</p></div></li></ul><p><strong>步骤一</strong>：创建模块时勾选要使用的技术，MyBatis，由于要操作数据库，还要勾选对应数据库</p><div class="img-wrap"><div class="img-bg"><img class="img" src="https://raw.githubusercontent.com/silvan2077/markdown_pic/main/Picgo/image-20211129092156020.png"/></div></div><div class="img-wrap"><div class="img-bg"><img class="img" src="https://raw.githubusercontent.com/silvan2077/markdown_pic/main/Picgo/image-20211129092210993.png"/></div></div><p>或者手工导入对应技术的starter，和对应数据库的坐标</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></pre></td><td class="code"><pre><span class="line"><span class="tag">&lt;<span class="name">dependencies</span>&gt;</span></span><br><span class="line">    <span class="comment">&lt;!--1.导入对应的starter--&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.spring.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>mybatis-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.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="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>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-java<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 class="tag">&lt;/<span class="name">dependencies</span>&gt;</span></span><br></pre></td></tr></table></figure><p><strong>步骤二</strong>：配置数据源相关信息</p><figure class="highlight yml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><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">#2.配置相关信息</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/ssm_db</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">root</span></span><br></pre></td></tr></table></figure><ul><li>这就配置结束了，SpringBoot将可能出现的通用配置都给简化了。下面写Mybatis的程序运行需要的Dao（或者Mapper）就可以运行了</li></ul><div class="tabs" id="springboot整合mybatis"><ul class="nav-tabs"><li class="tab active"><button type="button" data-href="#springboot整合mybatis-1">实体类</button></li><li class="tab"><button type="button" data-href="#springboot整合mybatis-2">映射接口（Dao）</button></li><li class="tab"><button type="button" data-href="#springboot整合mybatis-3">测试类</button></li></ul><div class="tab-contents"><div class="tab-item-content active" id="springboot整合mybatis-1"><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><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="keyword">public</span> <span class="keyword">class</span> <span class="title class_">Book</span> &#123;</span><br><span class="line">    <span class="keyword">private</span> Integer id;</span><br><span class="line">    <span class="keyword">private</span> String type;</span><br><span class="line">    <span class="keyword">private</span> String name;</span><br><span class="line">    <span class="keyword">private</span> String description;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><button type="button" class="tab-to-top" aria-label="scroll to top"><i class="fas fa-arrow-up"></i></button></div><div class="tab-item-content" id="springboot整合mybatis-2"><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><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="meta">@Mapper</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">interface</span> <span class="title class_">BookDao</span> &#123;</span><br><span class="line">    <span class="meta">@Select(&quot;select * from tbl_book where id = #&#123;id&#125;&quot;)</span></span><br><span class="line">    <span class="keyword">public</span> Book <span class="title function_">getById</span><span class="params">(Integer id)</span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><button type="button" class="tab-to-top" aria-label="scroll to top"><i class="fas fa-arrow-up"></i></button></div><div class="tab-item-content" id="springboot整合mybatis-3"><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><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">@SpringBootTest</span></span><br><span class="line"><span class="keyword">class</span> <span class="title class_">Springboot05MybatisApplicationTests</span> &#123;</span><br><span class="line">    <span class="meta">@Autowired</span></span><br><span class="line">    <span class="keyword">private</span> BookDao bookDao;</span><br><span class="line">    <span class="meta">@Test</span></span><br><span class="line">    <span class="keyword">void</span> <span class="title function_">contextLoads</span><span class="params">()</span> &#123;</span><br><span class="line">        System.out.println(bookDao.getById(<span class="number">1</span>));</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><button type="button" class="tab-to-top" aria-label="scroll to top"><i class="fas fa-arrow-up"></i></button></div></div></div><div class="note warning no-icon flat"><p>​当前使用的SpringBoot版本是2.5.4，对应的坐标设置中Mysql驱动使用的是8x版本。当SpringBoot2.4.3（不含）版本之前会出现一个小BUG，就是MySQL驱动升级到8以后要求强制配置时区，如果不设置会出问题。解决方案很简单，驱动url上面添加上对应设置就行了</p></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></pre></td><td class="code"><pre><span class="line"><span class="comment">#2.配置相关信息</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/ssm_db?serverTimezone=UTC</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">root</span></span><br></pre></td></tr></table></figure><p>​    这里设置的UTC是全球标准时间，也可以理解为是英国时间，中国处在东八区，需要在这个基础上加上8小时，这样才能和中国地区的时间对应的，也可以修改配置不写UTC，写Asia/Shanghai也可以解决这个问题。</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></pre></td><td class="code"><pre><span class="line"><span class="comment">#2.配置相关信息</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/ssm_db?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">root</span></span><br></pre></td></tr></table></figure><p>​    如果不想每次都设置这个东西，也可以去修改mysql中的配置文件mysql.ini，在mysqld项中添加default-time-zone=+8:00也可以解决这个问题。</p><p>​<div class="note info no-icon flat"><p>此外在运行程序时还会给出一个提示，说数据库驱动过时的警告，根据提示修改配置即可，弃用<strong>com.mysql.jdbc.Driver</strong>，换用<font color="#ff0000"><b>com.mysql.cj.jdbc.Driver</b></font>。前面的例子中已经更换了驱动了，在此说明一下。</p></div></p><p><strong>总结</strong></p><ol><li>整合操作需要勾选MyBatis技术，也就是导入MyBatis对应的starter</li><li>数据库连接相关信息转换成配置</li><li>数据库SQL映射需要添加@Mapper被容器识别到</li><li>MySQL 8.X驱动强制要求设置时区<ul><li>修改url，添加serverTimezone设定</li><li>修改MySQL数据库配置</li></ul></li><li>驱动类过时，提醒更换为com.mysql.cj.jdbc.Driver</li></ol><h3 id="整合MyBatis-Plus"><a href="#整合MyBatis-Plus" class="headerlink" title="整合MyBatis-Plus"></a>整合MyBatis-Plus</h3><p>使用SpringBoot整合第三方技术的核心总结起来就两句话</p><ul><li>导入对应技术的starter坐标</li><li>根据对应技术的要求做配置</li></ul><p>​接下来在MyBatis的基础上升级一下，整合MyBaitsPlus（简称MP）</p><p><strong>步骤一</strong>：导入对应的starter<br><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.4.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><br>Mybatis-plus的起步依赖与之前使用的略有不同，这是第三方提供的启动器，之前使用的都是官方提供的<br>| starter所属 | 命名规则                                                    | 示例                                                  |<br>| —————- | —————————————————————————————- | ——————————————————————————- |<br>| 官方提供    | spring-boot-starter-技术名称                                | spring-boot-starter-web <br/>spring-boot-starter-test |<br>| 第三方提供  | 第三方技术名称-spring-boot-starter                          | druid-spring-boot-starter                             |<br>| 第三方提供  | 第三方技术名称-boot-starter（第三方技术名称过长，简化命名） | mybatis-plus-boot-starter                             |</p><div class="note warning no-icon flat"><p>没有办法在创建项目时通过勾选的形式添加MyBatis-Plus的起步依赖，因为Springboot的官网还未收录此坐标，所以需要手工添加坐标，如果换用阿里云的url创建项目可以找到对应坐标</p></div><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></pre></td><td class="code"><pre><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/ssm_db</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">root</span></span><br></pre></td></tr></table></figure><p><strong>步骤三</strong>：编写映射接口<br><figure class="highlight java"><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="meta">@Mapper</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">interface</span> <span class="title class_">BookDao</span> <span class="keyword">extends</span> <span class="title class_">BaseMapper</span>&lt;Book&gt; &#123;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></p><div class="note info no-icon flat"><p>核心在于Dao接口继承了一个BaseMapper的接口，这个接口中帮助开发者预定了若干个常用的API接口，简化了通用API接口的开发工作。</p></div><div class="img-wrap"><div class="img-bg"><img class="img" src="https://raw.githubusercontent.com/silvan2077/markdown_pic/main/Picgo/image-20211129100313919.png"/></div></div><p>​<div class="note warning no-icon flat"><p>目前数据库的表名定义规则是tbl_模块名称，为了能和实体类相对应，需要做一个配置，相关知识各位小伙伴可以到MyBatisPlus课程中去学习，此处仅给出解决方案。配置application.yml文件，添加如下配置即可，设置所有表名的通用前缀名<br><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="attr">mybatis-plus:</span></span><br><span class="line">  <span class="attr">global-config:</span></span><br><span class="line">    <span class="attr">db-config:</span></span><br><span class="line">      <span class="attr">table-prefix:</span> <span class="string">tbl_</span><span class="comment">#设置所有表的通用前缀名称为tbl_</span></span><br></pre></td></tr></table></figure></p></div></p><p><strong>总结</strong></p><ol><li>手工添加MyBatis-Plus对应的starter</li><li>数据层接口使用BaseMapper简化开发</li><li>需要使用的第三方技术无法通过勾选确定时，需要手工添加坐标</li></ol><h3 id="整合Druid"><a href="#整合Druid" class="headerlink" title="整合Druid"></a>整合Druid</h3><p>可以发现SpringBoot整合第三方技术的核心就是导入对应的starter坐标，然后根据对应技术的要求做配置。只需要一直强化这套思想即可。</p><p>前面整合Mybatis和MP时使用的数据源对象是SpringBoot默认的数据源对象，接下来我们手动指定一个数据源对象。<br><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></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">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/ssm_db?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">root</span></span><br></pre></td></tr></table></figure></p><p>没有指定数据源时，SpringBoot会默认使用一个它认为最好的数据源对象，这就是<code>HiKari</code>。通过启动日志可以查看到对应的身影。</p><figure class="highlight tex"><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">2021-11-29 09:39:15.202  INFO 12260 --- [           main] com.zaxxer.hikari.HikariDataSource       : HikariPool-1 - Starting...</span><br><span class="line">2021-11-29 09:39:15.208  WARN 12260 --- [           main] com.zaxxer.hikari.util.DriverDataSource  : Registered driver with driverClassName=com.mysql.jdbc.Driver was not found, trying direct instantiation.</span><br><span class="line">2021-11-29 09:39:15.551  INFO 12260 --- [           main] com.zaxxer.hikari.HikariDataSource       : HikariPool-1 - Start completed.</span><br></pre></td></tr></table></figure><p>​    上述信息中每一行都有<code>HiKari</code>的身影，如果需要更换数据源，其实只需要两步即可。</p><ol><li>导入对应的技术坐标</li><li>配置使用指定的数据源类型</li></ol><p>​下面就切换一下数据源对象</p><p><strong>步骤一</strong>：导入对应的坐标（注意，是坐标，此处不是starter）</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></pre></td><td class="code"><pre><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>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.1.16<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></pre></td></tr></table></figure><p><strong>步骤二</strong>：修改配置，在数据源配置中有一个type属性，专用于指定数据源类型</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></pre></td><td class="code"><pre><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/ssm_db?serverTimezone=UTC</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">root</span></span><br><span class="line">    <span class="attr">type:</span> <span class="string">com.alibaba.druid.pool.DruidDataSource</span></span><br></pre></td></tr></table></figure><div class="note info no-icon flat"><p>这里又出现了新的问题：目前数据源配置格式是一个通用格式，不管使用什么数据源都能这么配置，若对数据源进行个性化配置，如配置数据源对应的连接数量，每个数据源技术对应的配置名称不可能完全一样，这时候该如何解决？</p><ul><li>SpringBoot给出的解决方法是导入对应的starter，使用对应配置。</li></ul></div><p><strong>步骤一</strong>：导入对应的starter</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></pre></td><td class="code"><pre><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>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-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>1.2.6<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></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></pre></td><td class="code"><pre><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">druid:</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/ssm_db?serverTimezone=UTC</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">root</span></span><br></pre></td></tr></table></figure><p>可以发现相较于之前的配置，在datasource下面多了一个druid节点，这就是druid专用的配置节点，用来配置druid数据源的相关属性。<br><div class="img-wrap"><div class="img-bg"><img class="img" src="https://raw.githubusercontent.com/silvan2077/markdown_pic/main/Picgo/image-20211129112610729.png"/></div></div></p><p>​与druid相关的配置超过200条以上，可以对druid数据源进行各种个性化配置</p><h2 id="SSMP整合综合案例"><a href="#SSMP整合综合案例" class="headerlink" title="SSMP整合综合案例"></a>SSMP整合综合案例</h2><p>可以通过SpringBoot快速整合各种技术，接下来通过一个小案例来回顾复习前面学到的知识。</p><p><img src="img\image-20211129113447844.png" alt="image-20211129113447844"></p><p><strong>添加</strong></p><p><img src="img\image-20211129113522459.png" alt="image-20211129113522459"></p><p><strong>删除</strong></p><p><img src="img\image-20211129113550829.png" alt="image-20211129113550829"></p><p><strong>修改</strong></p><p><img src="C:\Users\itcast\AppData\Roaming\Typora\typora-user-images\image-20211129113610966.png" alt="image-20211129113610966"></p><p><strong>分页</strong></p><p><img src="img\image-20211129113628969.png" alt="image-20211129113628969"></p><p><strong>条件查询</strong></p><p><img src="img\image-20211129113650369.png" alt="image-20211129113650369"></p><p>​    整体案例中需要采用的技术如下，先了解一下，做到哪一个说哪一个</p><ol><li>实体类开发————使用Lombok快速制作实体类</li><li>Dao开发————整合MyBatisPlus，制作数据层测试</li><li>Service开发————基于MyBatisPlus进行增量开发，制作业务层测试类</li><li>Controller开发————基于Restful开发，使用PostMan测试接口功能</li><li>Controller开发————前后端开发协议制作</li><li>页面开发————基于VUE+ElementUI制作，前后端联调，页面数据处理，页面消息处理<ul><li>列表</li><li>新增</li><li>修改</li><li>删除</li><li>分页</li><li>查询</li></ul></li><li>项目异常处理</li><li>按条件查询————页面功能调整、Controller修正功能、Service修正功能</li></ol><p>​    可以看的出来，东西还是很多的，希望通过这个案例，各位小伙伴能够完成基础开发的技能训练。整体开发过程采用做一层测一层的形式进行，过程完整，战线较长，希望各位能跟进进度，完成这个小案例的制作。</p><h3 id="模块创建"><a href="#模块创建" class="headerlink" title="模块创建"></a>模块创建</h3><p>​    对于这个案例如果按照企业开发的形式进行应该制作后台微服务，前后端分离的开发，但目前难度过高，所以简化一下只做单体服务器，一个服务器即充当后台服务调用，又负责前端页面展示，降低学习门槛。</p><p>下面创建一个新的模块，加载要使用的技术对应的starter，修改配置文件格式为yml格式，并把web访问端口先设置成80。</p><div class="tabs" id="ssm模块创建"><ul class="nav-tabs"><li class="tab active"><button type="button" data-href="#ssm模块创建-1">pom.xml</button></li><li class="tab"><button type="button" data-href="#ssm模块创建-2">application.yml</button></li></ul><div class="tab-contents"><div class="tab-item-content active" id="ssm模块创建-1"><p>这里直接在创建时导入Web，mysql驱动，Mybatis模块<br><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="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>org.mybatis.spring.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>mybatis-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>3.0.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><span class="line"></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 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 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 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.spring.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>mybatis-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">version</span>&gt;</span>3.0.5<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 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.4.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><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.1.16<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></pre></td></tr></table></figure></p><button type="button" class="tab-to-top" aria-label="scroll to top"><i class="fas fa-arrow-up"></i></button></div><div class="tab-item-content" id="ssm模块创建-2"><figure class="highlight yml"><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="attr">server:</span></span><br><span class="line">  <span class="attr">port:</span> <span class="number">80</span></span><br></pre></td></tr></table></figure><button type="button" class="tab-to-top" aria-label="scroll to top"><i class="fas fa-arrow-up"></i></button></div></div></div><h3 id="实体类开发"><a href="#实体类开发" class="headerlink" title="实体类开发"></a>实体类开发</h3><ul><li><p>新建表：tbl_book</p><div class="tabs" id="实体类开发新建表"><ul class="nav-tabs"><li class="tab active"><button type="button" data-href="#实体类开发新建表-1">SQL语句</button></li><li class="tab"><button type="button" data-href="#实体类开发新建表-2">表结构</button></li></ul><div class="tab-contents"><div class="tab-item-content active" id="实体类开发新建表-1"><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></pre></td><td class="code"><pre><span class="line"><span class="comment">-- ----------------------------</span></span><br><span class="line"><span class="comment">-- Table structure for tbl_book</span></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> `tbl_book`;</span><br><span class="line"><span class="keyword">CREATE TABLE</span> `tbl_book`  (</span><br><span class="line">  `id` <span class="type">int</span>(<span class="number">11</span>) <span class="keyword">NOT NULL</span> AUTO_INCREMENT,</span><br><span class="line">  `type` <span class="type">varchar</span>(<span class="number">20</span>) <span class="keyword">CHARACTER SET</span> utf8 <span class="keyword">COLLATE</span> utf8_general_ci <span class="keyword">NULL</span> <span class="keyword">DEFAULT</span> <span class="keyword">NULL</span>,</span><br><span class="line">  `name` <span class="type">varchar</span>(<span class="number">50</span>) <span class="keyword">CHARACTER SET</span> utf8 <span class="keyword">COLLATE</span> utf8_general_ci <span class="keyword">NULL</span> <span class="keyword">DEFAULT</span> <span class="keyword">NULL</span>,</span><br><span class="line">  `description` <span class="type">varchar</span>(<span class="number">255</span>) <span class="keyword">CHARACTER SET</span> utf8 <span class="keyword">COLLATE</span> utf8_general_ci <span class="keyword">NULL</span> <span class="keyword">DEFAULT</span> <span class="keyword">NULL</span>,</span><br><span class="line">  <span class="keyword">PRIMARY KEY</span> (`id`) <span class="keyword">USING</span> BTREE</span><br><span class="line">) ENGINE <span class="operator">=</span> InnoDB AUTO_INCREMENT <span class="operator">=</span> <span class="number">51</span> <span class="keyword">CHARACTER SET</span> <span class="operator">=</span> utf8 <span class="keyword">COLLATE</span> <span class="operator">=</span> utf8_general_ci ROW_FORMAT <span class="operator">=</span> <span class="keyword">Dynamic</span>;</span><br><span class="line"></span><br><span class="line"><span class="comment">-- ----------------------------</span></span><br><span class="line"><span class="comment">-- Records of tbl_book</span></span><br><span class="line"><span class="comment">-- ----------------------------</span></span><br><span class="line"><span class="keyword">INSERT INTO</span> `tbl_book` <span class="keyword">VALUES</span> (<span class="number">1</span>, <span class="string">&#x27;计算机理论&#x27;</span>, <span class="string">&#x27;Spring实战 第5版&#x27;</span>, <span class="string">&#x27;Spring入门经典教程，深入理解Spring原理技术内幕&#x27;</span>);</span><br><span class="line"><span class="keyword">INSERT INTO</span> `tbl_book` <span class="keyword">VALUES</span> (<span class="number">2</span>, <span class="string">&#x27;计算机理论&#x27;</span>, <span class="string">&#x27;Spring 5核心原理与30个类手写实战&#x27;</span>, <span class="string">&#x27;十年沉淀之作，手写Spring精华思想&#x27;</span>);</span><br><span class="line"><span class="keyword">INSERT INTO</span> `tbl_book` <span class="keyword">VALUES</span> (<span class="number">3</span>, <span class="string">&#x27;计算机理论&#x27;</span>, <span class="string">&#x27;Spring 5 设计模式&#x27;</span>, <span class="string">&#x27;深入Spring源码剖析Spring源码中蕴含的10大设计模式&#x27;</span>);</span><br><span class="line"><span class="keyword">INSERT INTO</span> `tbl_book` <span class="keyword">VALUES</span> (<span class="number">4</span>, <span class="string">&#x27;计算机理论&#x27;</span>, <span class="string">&#x27;Spring MVC+MyBatis开发从入门到项目实战&#x27;</span>, <span class="string">&#x27;全方位解析面向Web应用的轻量级框架，带你成为Spring MVC开发高手&#x27;</span>);</span><br><span class="line"><span class="keyword">INSERT INTO</span> `tbl_book` <span class="keyword">VALUES</span> (<span class="number">5</span>, <span class="string">&#x27;计算机理论&#x27;</span>, <span class="string">&#x27;轻量级Java Web企业应用实战&#x27;</span>, <span class="string">&#x27;源码级剖析Spring框架，适合已掌握Java基础的读者&#x27;</span>);</span><br><span class="line"><span class="keyword">INSERT INTO</span> `tbl_book` <span class="keyword">VALUES</span> (<span class="number">6</span>, <span class="string">&#x27;计算机理论&#x27;</span>, <span class="string">&#x27;Java核心技术 卷I 基础知识（原书第11版）&#x27;</span>, <span class="string">&#x27;Core Java 第11版，Jolt大奖获奖作品，针对Java SE9、10、11全面更新&#x27;</span>);</span><br><span class="line"><span class="keyword">INSERT INTO</span> `tbl_book` <span class="keyword">VALUES</span> (<span class="number">7</span>, <span class="string">&#x27;计算机理论&#x27;</span>, <span class="string">&#x27;深入理解Java虚拟机&#x27;</span>, <span class="string">&#x27;5个维度全面剖析JVM，大厂面试知识点全覆盖&#x27;</span>);</span><br><span class="line"><span class="keyword">INSERT INTO</span> `tbl_book` <span class="keyword">VALUES</span> (<span class="number">8</span>, <span class="string">&#x27;计算机理论&#x27;</span>, <span class="string">&#x27;Java编程思想（第4版）&#x27;</span>, <span class="string">&#x27;Java学习必读经典,殿堂级著作！赢得了全球程序员的广泛赞誉&#x27;</span>);</span><br><span class="line"><span class="keyword">INSERT INTO</span> `tbl_book` <span class="keyword">VALUES</span> (<span class="number">9</span>, <span class="string">&#x27;计算机理论&#x27;</span>, <span class="string">&#x27;零基础学Java（全彩版）&#x27;</span>, <span class="string">&#x27;零基础自学编程的入门图书，由浅入深，详解Java语言的编程思想和核心技术&#x27;</span>);</span><br><span class="line"><span class="keyword">INSERT INTO</span> `tbl_book` <span class="keyword">VALUES</span> (<span class="number">10</span>, <span class="string">&#x27;市场营销&#x27;</span>, <span class="string">&#x27;直播就该这么做：主播高效沟通实战指南&#x27;</span>, <span class="string">&#x27;李子柒、李佳琦、薇娅成长为网红的秘密都在书中&#x27;</span>);</span><br><span class="line"><span class="keyword">INSERT INTO</span> `tbl_book` <span class="keyword">VALUES</span> (<span class="number">11</span>, <span class="string">&#x27;市场营销&#x27;</span>, <span class="string">&#x27;直播销讲实战一本通&#x27;</span>, <span class="string">&#x27;和秋叶一起学系列网络营销书籍&#x27;</span>);</span><br><span class="line"><span class="keyword">INSERT INTO</span> `tbl_book` <span class="keyword">VALUES</span> (<span class="number">12</span>, <span class="string">&#x27;市场营销&#x27;</span>, <span class="string">&#x27;直播带货：淘宝、天猫直播从新手到高手&#x27;</span>, <span class="string">&#x27;一本教你如何玩转直播的书，10堂课轻松实现带货月入3W+&#x27;</span>);</span><br></pre></td></tr></table></figure><button type="button" class="tab-to-top" aria-label="scroll to top"><i class="fas fa-arrow-up"></i></button></div><div class="tab-item-content" id="实体类开发新建表-2"><div class="img-wrap"><div class="img-bg"><img class="img" src="https://raw.githubusercontent.com/silvan2077/markdown_pic/main/Picgo/20251025145836.png"/></div></div><button type="button" class="tab-to-top" aria-label="scroll to top"><i class="fas fa-arrow-up"></i></button></div></div></div></li><li><p>创建实体类：Book</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></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">Book</span> &#123;</span><br><span class="line">    <span class="keyword">private</span> Integer id;</span><br><span class="line">    <span class="keyword">private</span> String type;</span><br><span class="line">    <span class="keyword">private</span> String name;</span><br><span class="line">    <span class="keyword">private</span> String description;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></li></ul><h3 id="数据层开发——基础CRUD"><a href="#数据层开发——基础CRUD" class="headerlink" title="数据层开发——基础CRUD"></a>数据层开发——基础CRUD</h3><p>​    数据层开发本次使用MyBatisPlus技术，数据源使用前面学习的Druid，学都学了都用上,课程中提供的是SpringBoot2的各种版本，而我在学习中使用的是SpringBoot3，所以会有些许不同</p><p><strong>步骤一</strong>：导入MyBatisPlus与Druid对应的starter，当然mysql的驱动不能少<br><div class="tabs" id="数据层开发导入starter"><ul class="nav-tabs"><li class="tab active"><button type="button" data-href="#数据层开发导入starter-1">SpringBoot2</button></li><li class="tab"><button type="button" data-href="#数据层开发导入starter-2">SpringBoot3</button></li></ul><div class="tab-contents"><div class="tab-item-content active" id="数据层开发导入starter-1"><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="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>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.4.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><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-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>1.2.6<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>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-java<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 class="tag">&lt;/<span class="name">dependencies</span>&gt;</span></span><br></pre></td></tr></table></figure><button type="button" class="tab-to-top" aria-label="scroll to top"><i class="fas fa-arrow-up"></i></button></div><div class="tab-item-content" id="数据层开发导入starter-2"><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="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 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-spring-boot3-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.14<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>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-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>1.2.6<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><button type="button" class="tab-to-top" aria-label="scroll to top"><i class="fas fa-arrow-up"></i></button></div></div></div></p><p><strong>步骤二</strong>：配置数据库连接相关的数据源配置</p><div class="tabs" id="数据层开发配置数据源"><ul class="nav-tabs"><li class="tab"><button type="button" data-href="#数据层开发配置数据源-1">SpringBoot2</button></li><li class="tab active"><button type="button" data-href="#数据层开发配置数据源-2">SpringBoot3</button></li></ul><div class="tab-contents"><div class="tab-item-content" id="数据层开发配置数据源-1"><figure class="highlight yml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><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="attr">server:</span></span><br><span class="line">  <span class="attr">port:</span> <span class="number">80</span></span><br><span class="line"></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">druid:</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/ssm_db?serverTimezone=UTC</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">root</span></span><br><span class="line"></span><br><span class="line"><span class="attr">mybatis-plus:</span></span><br><span class="line">  <span class="attr">global-config:</span></span><br><span class="line">    <span class="attr">db-config:</span></span><br><span class="line">      <span class="attr">table-prefix:</span> <span class="string">tbl_</span></span><br></pre></td></tr></table></figure><button type="button" class="tab-to-top" aria-label="scroll to top"><i class="fas fa-arrow-up"></i></button></div><div class="tab-item-content active" id="数据层开发配置数据源-2"><p>SpringBoot3更新了数据源的配置方式，需要指定type为Druid<br><figure class="highlight yml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><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="attr">server:</span></span><br><span class="line">  <span class="attr">port:</span> <span class="number">8080</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/test</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">username:</span> <span class="string">root</span></span><br><span class="line">    <span class="attr">password:</span> <span class="string">369640sh</span></span><br><span class="line">    <span class="attr">type:</span> <span class="string">com.alibaba.druid.pool.DruidDataSource</span></span><br><span class="line"></span><br><span class="line"><span class="attr">mybatis-plus:</span></span><br><span class="line">  <span class="attr">global-config:</span></span><br><span class="line">    <span class="attr">db-config:</span></span><br><span class="line">      <span class="attr">table-prefix:</span> <span class="string">tbl_</span></span><br></pre></td></tr></table></figure></p><button type="button" class="tab-to-top" aria-label="scroll to top"><i class="fas fa-arrow-up"></i></button></div></div></div><p>接下来分别提供使用Mybatis和MybatisPlus两种方式快速开发基础CURD</p><div class="tabs" id="数据层开发两种方式"><ul class="nav-tabs"><li class="tab"><button type="button" data-href="#数据层开发两种方式-1">MyBatis</button></li><li class="tab active"><button type="button" data-href="#数据层开发两种方式-2">MyBatisPlus</button></li></ul><div class="tab-contents"><div class="tab-item-content" id="数据层开发两种方式-1"><p><strong>步骤三</strong>：创建BookDao接口<br>仿照查询操作自行添加增删改操作<br><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><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="meta">@Mapper</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">interface</span> <span class="title class_">BookDao</span> &#123;</span><br><span class="line">    <span class="meta">@Select(&quot;select * from tbl_book where id=#&#123;id&#125;&quot;)</span></span><br><span class="line">    Book <span class="title function_">getBookById</span><span class="params">(<span class="type">int</span> id)</span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><br><strong>步骤四</strong>：创建测试类<br><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><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">@SpringBootTest</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">BookDaoTest</span> &#123;</span><br><span class="line">    <span class="meta">@Autowired</span></span><br><span class="line">    <span class="keyword">private</span> BookDao bookDao;</span><br><span class="line">    <span class="meta">@Test</span></span><br><span class="line">    <span class="keyword">void</span> <span class="title function_">testGetById</span><span class="params">()</span>&#123;</span><br><span class="line">        <span class="type">Book</span> <span class="variable">book</span> <span class="operator">=</span> bookDao.getBookById(<span class="number">1</span>);</span><br><span class="line">        System.out.println(book);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></p><button type="button" class="tab-to-top" aria-label="scroll to top"><i class="fas fa-arrow-up"></i></button></div><div class="tab-item-content active" id="数据层开发两种方式-2"><p><strong>步骤三</strong>：令Book接口继承<code>BaseMapper</code><br><figure class="highlight java"><table><tr><td class="gutter"><pre><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="meta">@Mapper</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">interface</span> <span class="title class_">BookDao</span> <span class="keyword">extends</span> <span class="title class_">BaseMapper</span>&lt;Book&gt; &#123;</span><br><span class="line">    <span class="comment">// 不需要编写任何语句</span></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><br><strong>步骤四</strong>：创建测试类<br><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><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="meta">@SpringBootTest</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">BookDaoTest</span> &#123;</span><br><span class="line">    <span class="meta">@Autowired</span></span><br><span class="line">    <span class="keyword">private</span> BookDao bookDao;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Test</span></span><br><span class="line">    <span class="keyword">void</span> <span class="title function_">testSelectById</span><span class="params">()</span>&#123;</span><br><span class="line">        System.out.println(bookDao.selectById(<span class="number">1</span>));</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="meta">@Test</span></span><br><span class="line">    <span class="keyword">void</span> <span class="title function_">testSave</span><span class="params">()</span>&#123;</span><br><span class="line">        <span class="type">Book</span> <span class="variable">book</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">Book</span>();</span><br><span class="line">        book.setType(<span class="string">&quot;测试222&quot;</span>);</span><br><span class="line">        book.setName(<span class="string">&quot;测试222&quot;</span>);</span><br><span class="line">        book.setDescription(<span class="string">&quot;测试222&quot;</span>);</span><br><span class="line">        bookDao.insert(book);</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="meta">@Test</span></span><br><span class="line">    <span class="keyword">void</span> <span class="title function_">testDelete</span><span class="params">()</span>&#123;</span><br><span class="line">        bookDao.deleteById(<span class="number">51</span>);</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="meta">@Test</span></span><br><span class="line">    <span class="keyword">void</span> <span class="title function_">testGetAll</span><span class="params">()</span>&#123;</span><br><span class="line">        bookDao.selectList(<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><button type="button" class="tab-to-top" aria-label="scroll to top"><i class="fas fa-arrow-up"></i></button></div></div></div><div class="note danger no-icon flat"><p><strong>注意</strong>：<br>​    MP技术默认的主键生成策略为雪花算法，生成的主键ID长度较大，和目前的数据库设定规则不相符，在SpringBoot中执行测试会直接失败，在SpringBoot3中能够在数据库看到数据，但无法根据Id来获取数据。在application.yml中添加对应配置即可，具体如下：<br><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></pre></td><td class="code"><pre><span class="line"><span class="attr">mybatis-plus:</span></span><br><span class="line">  <span class="attr">global-config:</span></span><br><span class="line">    <span class="attr">db-config:</span></span><br><span class="line">      <span class="attr">table-prefix:</span> <span class="string">tbl_</span><span class="comment">#设置表名通用前缀</span></span><br><span class="line">      <span class="attr">id-type:</span> <span class="string">auto</span><span class="comment">#设置主键id字段的生成策略为参照数据库设定的策略，当前数据库设置id生成策略为自增</span></span><br></pre></td></tr></table></figure></p></div><details class="folding-tag" yellow><summary> 小技巧：查看MP运行信息 </summary>              <div class='content'>                            </div>            </details><p>​    在进行数据层测试的时候，因为基础的CRUD操作均由MP来提供，所以开发者不需要书写SQL语句了，但也因此对于程序中运行的SQL语句完全不知道了。这时候如果程序正常运行还好，如果报错了，这个时候就很崩溃，甚至都不知道从何下手，所以查看执行期运行的SQL语句就成为当务之急。</p><p>​    SpringBoot整合MP的时候充分考虑到了这点，通过配置的形式就可以查阅执行期SQL语句，配置如下</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></pre></td><td class="code"><pre><span class="line"><span class="attr">mybatis-plus:</span></span><br><span class="line">  <span class="attr">global-config:</span></span><br><span class="line">    <span class="attr">db-config:</span></span><br><span class="line">      <span class="attr">table-prefix:</span> <span class="string">tbl_</span></span><br><span class="line">      <span class="attr">id-type:</span> <span class="string">auto</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.stdout.StdOutImpl</span></span><br></pre></td></tr></table></figure><p>再来看运行结果，此时就显示了运行期执行SQL的情况。<br><figure class="highlight tex"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><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">Creating a new SqlSession</span><br><span class="line">SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@2c9a6717] was not registered for synchronization because synchronization is not active</span><br><span class="line">JDBC Connection [com.mysql.cj.jdbc.ConnectionImpl@6ca30b8a] will not be managed by Spring</span><br><span class="line">==&gt;  Preparing: SELECT id,type,name,description FROM tbl<span class="built_in">_</span>book</span><br><span class="line">==&gt; Parameters: </span><br><span class="line">&lt;==    Columns: id, type, name, description</span><br><span class="line">&lt;==        Row: 1, 计算机理论, Spring实战 第5版, Spring入门经典教程，深入理解Spring原理技术内幕</span><br><span class="line">&lt;==        Row: 2, 计算机理论, Spring 5核心原理与30个类手写实战, 十年沉淀之作，手写Spring精华思想</span><br><span class="line">&lt;==        Row: 3, 计算机理论, Spring 5 设计模式, 深入Spring源码剖析Spring源码中蕴含的10大设计模式</span><br><span class="line">&lt;==        Row: 4, 计算机理论, Spring MVC+MyBatis开发从入门到项目实战, 全方位解析面向Web应用的轻量级框架，带你成为Spring MVC开发高手</span><br><span class="line">&lt;==        Row: 5, 计算机理论, 轻量级Java Web企业应用实战, 源码级剖析Spring框架，适合已掌握Java基础的读者</span><br><span class="line">&lt;==        Row: 6, 计算机理论, Java核心技术 卷I 基础知识（原书第11版）, Core Java 第11版，Jolt大奖获奖作品，针对Java SE9、10、11全面更新</span><br><span class="line">&lt;==        Row: 7, 计算机理论, 深入理解Java虚拟机, 5个维度全面剖析JVM，大厂面试知识点全覆盖</span><br><span class="line">&lt;==        Row: 8, 计算机理论, Java编程思想（第4版）, Java学习必读经典,殿堂级著作！赢得了全球程序员的广泛赞誉</span><br><span class="line">&lt;==        Row: 9, 计算机理论, 零基础学Java（全彩版）, 零基础自学编程的入门图书，由浅入深，详解Java语言的编程思想和核心技术</span><br><span class="line">&lt;==        Row: 10, 市场营销, 直播就该这么做：主播高效沟通实战指南, 李子柒、李佳琦、薇娅成长为网红的秘密都在书中</span><br><span class="line">&lt;==        Row: 11, 市场营销, 直播销讲实战一本通, 和秋叶一起学系列网络营销书籍</span><br><span class="line">&lt;==        Row: 12, 市场营销, 直播带货：淘宝、天猫直播从新手到高手, 一本教你如何玩转直播的书，10堂课轻松实现带货月入3W+</span><br><span class="line">&lt;==        Row: 13, 测试类型, 测试数据, 测试描述数据</span><br><span class="line">&lt;==        Row: 14, 测试数据update, 测试数据update, 测试数据update</span><br><span class="line">&lt;==        Row: 15, -----------------, 测试数据123, 测试数据123</span><br><span class="line">&lt;==      Total: 15</span><br></pre></td></tr></table></figure></p><p>​    其中清晰的标注了当前执行的SQL语句是什么，携带了什么参数，对应的执行结果是什么，所有信息应有尽有。此处设置的是日志显示形式为控制台输出，当然还可以由更多的选择，根据需求切换即可</p><div class="img-wrap"><div class="img-bg"><img class="img" src="https://raw.githubusercontent.com/silvan2077/markdown_pic/main/Picgo/20251026163100.png"/></div></div><h3 id="数据层开发——分页功能制作"><a href="#数据层开发——分页功能制作" class="headerlink" title="数据层开发——分页功能制作"></a>数据层开发——分页功能制作</h3><p><strong>温馨提示：</strong></p><ul><li>这里如果使用的<code>Mybatis-Plus</code>版本是3.5.9+，那么想使用分页功能需要额外添加一个依赖管理和一个依赖，<a href="https://baomidou.com/getting-started/install/">官方说明</a><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="tag">&lt;<span class="name">dependencyManagement</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">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-bom<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.11<span class="tag">&lt;/<span class="name">version</span>&gt;</span></span><br><span class="line">            <span class="tag">&lt;<span class="name">type</span>&gt;</span>pom<span class="tag">&lt;/<span class="name">type</span>&gt;</span></span><br><span class="line">            <span class="tag">&lt;<span class="name">scope</span>&gt;</span>import<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 class="tag">&lt;/<span class="name">dependencies</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;/<span class="name">dependencyManagement</span>&gt;</span></span><br><span class="line"></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-jsqlparser<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></li></ul><p>​MP提供的分页操作API如下<br><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><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">@Test</span></span><br><span class="line"><span class="keyword">void</span> <span class="title function_">testGetPage</span><span class="params">()</span>&#123;</span><br><span class="line">    <span class="type">IPage</span> <span class="variable">page</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">Page</span>(<span class="number">2</span>,<span class="number">5</span>);</span><br><span class="line">    bookDao.selectPage(page, <span class="literal">null</span>);</span><br><span class="line">    System.out.println(page.getCurrent());</span><br><span class="line">    System.out.println(page.getSize());</span><br><span class="line">    System.out.println(page.getTotal());</span><br><span class="line">    System.out.println(page.getPages());</span><br><span class="line">    System.out.println(page.getRecords());</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></p><p>​    其中selectPage方法需要传入一个封装分页数据的对象，可以通过new的形式创建这个对象，当然这个对象也是MP提供的，别选错包了。创建此对象时就需要指定分页的两个基本数据</p><ul><li>当前显示第几页</li><li>每页显示几条数据</li></ul><ul><li>在创建Page对象时利用构造方法初始化这两个数据<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="type">IPage</span> <span class="variable">page</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">Page</span>(<span class="number">2</span>,<span class="number">5</span>);</span><br></pre></td></tr></table></figure></li></ul><p>将该对象传入到查询方法selectPage后，可以得到查询结果，但是我们会发现当前操作查询结果返回值仍然是一个IPage对象，这又是怎么回事？</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="type">IPage</span> <span class="variable">page</span> <span class="operator">=</span> bookDao.selectPage(page, <span class="literal">null</span>);</span><br></pre></td></tr></table></figure><p>​    原来这个IPage对象中封装了若干个数据，而查询的结果作为IPage对象封装的一个数据存在的，可以理解为查询结果得到后，又塞到了这个IPage对象中，其实还是为了高度的封装，一个IPage描述了分页所有的信息。下面5个操作就是IPage对象中封装的所有信息了<br><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><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">@Test</span></span><br><span class="line"><span class="keyword">void</span> <span class="title function_">testGetPage</span><span class="params">()</span>&#123;</span><br><span class="line">    <span class="type">IPage</span> <span class="variable">page</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">Page</span>(<span class="number">2</span>,<span class="number">5</span>);</span><br><span class="line">    bookDao.selectPage(page, <span class="literal">null</span>);</span><br><span class="line">    System.out.println(page.getCurrent());<span class="comment">//当前页码值</span></span><br><span class="line">    System.out.println(page.getSize());<span class="comment">//每页显示数</span></span><br><span class="line">    System.out.println(page.getTotal());<span class="comment">//数据总量</span></span><br><span class="line">    System.out.println(page.getPages());<span class="comment">//总页数</span></span><br><span class="line">    System.out.println(page.getRecords());<span class="comment">//详细数据</span></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><br>此时就知道这些数据如何获取了，但当我们执行后却发现MP并没有为我们执行分页。这个要源于MP的内部机制。</p><ul><li><p>对于MySQL的分页操作使用<code>limit</code>关键字进行，但不是所有的数据库都使用limit关键字实现，这个时候MP为了制作的兼容性强，将分页操作设置为基础查询操作的升级版，可以理解为iphone6与iphone6S-PLUS的关系。</p></li><li><p>基础操作中有查询全部的功能，而在这个基础上只需要升级一下（PLUS）就可以得到分页操作。所以MP将分页操作做成了一个开关，只需要在使用时去打开这个开关。配置拦截器就是打开这个开关</p></li></ul><p><strong>定义MP拦截器并将其设置为Spring管控的bean</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="meta">@Configuration</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">MPConfig</span> &#123;</span><br><span class="line">    <span class="meta">@Bean</span></span><br><span class="line">    <span class="keyword">public</span> MybatisPlusInterceptor <span class="title function_">mybatisPlusInterceptor</span><span class="params">()</span>&#123;</span><br><span class="line">        <span class="type">MybatisPlusInterceptor</span> <span class="variable">interceptor</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">MybatisPlusInterceptor</span>();</span><br><span class="line">        interceptor.addInnerInterceptor(<span class="keyword">new</span> <span class="title class_">PaginationInnerInterceptor</span>());</span><br><span class="line">        <span class="keyword">return</span> interceptor;</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>除了分页功能，MP还提供了强大的条件查询功能。以往我们写条件查询要自己动态拼写复杂的SQL语句，现在MP将这些操作制作成API接口，调用一个又一个的方法就可以实现各种套件的拼装。这里只简单普及一下基本格式</p><p>下面的操作就是执行一个模糊匹配对应的操作，由like条件书写变为了like方法的调用<br><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><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="meta">@Test</span></span><br><span class="line"><span class="keyword">void</span> <span class="title function_">testGetBy</span><span class="params">()</span>&#123;</span><br><span class="line"><span class="comment">//  select * from t_book where name like &#x27;%Spring%&#x27;</span></span><br><span class="line">    QueryWrapper&lt;Book&gt; queryWrapper = <span class="keyword">new</span> <span class="title class_">QueryWrapper</span>&lt;&gt;();</span><br><span class="line">    queryWrapper.like(<span class="string">&quot;name&quot;</span>,<span class="string">&quot;Spring&quot;</span>);</span><br><span class="line">    bookDao.selectList(queryWrapper);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></p><p>​    第一句<code>QueryWrapper对象</code>是用于封装查询条件的对象，该对象可以动态使用API调用的方法来添加条件，最终转化成对应的SQL语句。第二句就是一个条件了，需要什么条件，使用QueryWapper对象直接调用对应操作即可。比如做大于、小于关系，就可以使用lt或gt方法，等于使用eq方法，等等……</p><div class="note warning no-icon flat"><p>这组API使用方式较简单，但是属性名一旦写错，编译器将无法检查，运行时才会报错，非常不友好，所以MP针对字段进行功能升级，支持Lambda表达式，同时将<code>QueryWrapper</code>对象升级为<code>LambdaQueryWrapper</code>对象</p></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></pre></td><td class="code"><pre><span class="line"><span class="meta">@Test</span></span><br><span class="line"><span class="keyword">void</span> <span class="title function_">testGetBy2</span><span class="params">()</span>&#123;</span><br><span class="line">    <span class="type">String</span> <span class="variable">name</span> <span class="operator">=</span> <span class="string">&quot;1&quot;</span>;</span><br><span class="line">    LambdaQueryWrapper&lt;Book&gt; lqw = <span class="keyword">new</span> <span class="title class_">LambdaQueryWrapper</span>&lt;Book&gt;();</span><br><span class="line">    lqw.like(Book::getName,name);</span><br><span class="line">    bookDao.selectList(lqw);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>为了便于开发者动态拼写SQL，防止将null数据作为条件使用，MP还提供了动态拼装SQL的快捷书写方式<br><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><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">@Test</span></span><br><span class="line"><span class="keyword">void</span> <span class="title function_">testGetBy2</span><span class="params">()</span>&#123;</span><br><span class="line">    <span class="type">String</span> <span class="variable">name</span> <span class="operator">=</span> <span class="string">&quot;1&quot;</span>;</span><br><span class="line">    LambdaQueryWrapper&lt;Book&gt; lqw = <span class="keyword">new</span> <span class="title class_">LambdaQueryWrapper</span>&lt;Book&gt;();</span><br><span class="line">    <span class="comment">//if(name != null) lqw.like(Book::getName,name);//方式一：JAVA代码控制</span></span><br><span class="line"><span class="comment">// 拥有三个参数，第一个参数控制条件判断，第二个参数是字段名，第三个参数是字段值</span></span><br><span class="line">    lqw.like(name != <span class="literal">null</span>,Book::getName,name);<span class="comment">//方式二：API接口提供控制开关</span></span><br><span class="line">    bookDao.selectList(lqw);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></p><div class="note warning no-icon flat"><p>其实第三种只是个格式，没有本质上的区别，根据需求选择使用</p></div><h3 id="业务层开发"><a href="#业务层开发" class="headerlink" title="业务层开发"></a>业务层开发</h3><p>​    数据层开发告一段落，下面进行业务层开发，其实标准业务层开发很多初学者认为就是调用数据层，这个理解是没有大问题的，但更精准的说法应该是<font color="#ff0000"><b><strong>组织业务逻辑功能，并根据业务需求，对数据持久层发起调用</strong></b></font>。有什么差别呢？目标是为了组织出符合需求的业务逻辑功能，至于调不调用数据层根据需求来决定，有需求就调用，没有需求就不调用。</p><p>​一个常识性的知识：</p><ul><li><p>业务层的方法名定义一定要与业务有关，例如登录操作</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">login(String username,String password);</span><br></pre></td></tr></table></figure></li><li><p>数据层的方法名定义一定与业务无关，是一定，不是可能，也不是有可能，例如根据用户名密码查询</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">selectByUserNameAndPassword(String username,String password);</span><br></pre></td></tr></table></figure></li></ul><p>在开发的时候是可以根据完成的工作不同划分成不同职能的开发团队的。比如一个人制作数据层，他就可以不知道业务是什么样子，拿到的需求文档要求可能是这样的</p><figure class="highlight tex"><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><br><span class="line">接口：传入ID字段，查询出对应结果，结果是单条数据</span><br><span class="line">接口：传入离职字段，查询出对应结果，结果是多条数据</span><br></pre></td></tr></table></figure><p>但是进行业务功能开发的人，拿到的需求文档要求差别就很大<br><figure class="highlight tex"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">接口：传入用户名与密码字段，对用户名字段做长度校验，4-15位，对密码字段做长度校验，8到24位，对喵喵喵字段做特殊字符校验，不允许存在空格，查询结果为对象。如果为null，返回BusinessException，封装消息码INFO<span class="built_in">_</span>LOGON<span class="built_in">_</span>USERNAME<span class="built_in">_</span>PASSWORD<span class="built_in">_</span>ERROR</span><br></pre></td></tr></table></figure></p><p>业务层接口定义如下：<br><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><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">interface</span> <span class="title class_">BookService</span> &#123;</span><br><span class="line">    Boolean <span class="title function_">save</span><span class="params">(Book book)</span>;</span><br><span class="line">    Boolean <span class="title function_">update</span><span class="params">(Book book)</span>;</span><br><span class="line">    Boolean <span class="title function_">delete</span><span class="params">(Integer id)</span>;</span><br><span class="line">    Book <span class="title function_">getById</span><span class="params">(Integer id)</span>;</span><br><span class="line">    List&lt;Book&gt; <span class="title function_">getAll</span><span class="params">()</span>;</span><br><span class="line">    IPage&lt;Book&gt; <span class="title function_">getPage</span><span class="params">(<span class="type">int</span> currentPage,<span class="type">int</span> pageSize)</span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></p><p>业务层实现类如下，转调数据层即可<br><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><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">@Service</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">BookServiceImpl</span> <span class="keyword">implements</span> <span class="title class_">BookService</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> BookDao bookDao;</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> Boolean <span class="title function_">save</span><span class="params">(Book book)</span> &#123;</span><br><span class="line">        <span class="keyword">return</span> bookDao.insert(book) &gt; <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> Boolean <span class="title function_">update</span><span class="params">(Book book)</span> &#123;</span><br><span class="line">        <span class="keyword">return</span> bookDao.updateById(book) &gt; <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> Boolean <span class="title function_">delete</span><span class="params">(Integer id)</span> &#123;</span><br><span class="line">        <span class="keyword">return</span> bookDao.deleteById(id) &gt; <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> Book <span class="title function_">getById</span><span class="params">(Integer id)</span> &#123;</span><br><span class="line">        <span class="keyword">return</span> bookDao.selectById(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">public</span> List&lt;Book&gt; <span class="title function_">getAll</span><span class="params">()</span> &#123;</span><br><span class="line">        <span class="keyword">return</span> bookDao.selectList(<span class="literal">null</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> IPage&lt;Book&gt; <span class="title function_">getPage</span><span class="params">(<span class="type">int</span> currentPage, <span class="type">int</span> pageSize)</span> &#123;</span><br><span class="line">        <span class="type">IPage</span> <span class="variable">page</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">Page</span>(currentPage,pageSize);</span><br><span class="line">        bookDao.selectPage(page,<span class="literal">null</span>);</span><br><span class="line">        <span class="keyword">return</span> page;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></p><p>对业务层接口进行测试，测试类如下<br><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><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="meta">@SpringBootTest</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">BookServiceTest</span> &#123;</span><br><span class="line">    <span class="meta">@Autowired</span></span><br><span class="line">    <span class="keyword">private</span> BookService bookService;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Test</span></span><br><span class="line">    <span class="keyword">void</span> <span class="title function_">testGetById</span><span class="params">()</span>&#123;</span><br><span class="line">        System.out.println(bookService.getById(<span class="number">4</span>));</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="meta">@Test</span></span><br><span class="line">    <span class="keyword">void</span> <span class="title function_">testSave</span><span class="params">()</span>&#123;</span><br><span class="line">        <span class="type">Book</span> <span class="variable">book</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">Book</span>();</span><br><span class="line">        book.setType(<span class="string">&quot;测试数据123&quot;</span>);</span><br><span class="line">        book.setName(<span class="string">&quot;测试数据123&quot;</span>);</span><br><span class="line">        book.setDescription(<span class="string">&quot;测试数据123&quot;</span>);</span><br><span class="line">        bookService.save(book);</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="meta">@Test</span></span><br><span class="line">    <span class="keyword">void</span> <span class="title function_">testUpdate</span><span class="params">()</span>&#123;</span><br><span class="line">        <span class="type">Book</span> <span class="variable">book</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">Book</span>();</span><br><span class="line">        book.setId(<span class="number">17</span>);</span><br><span class="line">        book.setType(<span class="string">&quot;-----------------&quot;</span>);</span><br><span class="line">        book.setName(<span class="string">&quot;测试数据123&quot;</span>);</span><br><span class="line">        book.setDescription(<span class="string">&quot;测试数据123&quot;</span>);</span><br><span class="line">        bookService.update(book);</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="meta">@Test</span></span><br><span class="line">    <span class="keyword">void</span> <span class="title function_">testDelete</span><span class="params">()</span>&#123;</span><br><span class="line">        bookService.delete(<span class="number">18</span>);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Test</span></span><br><span class="line">    <span class="keyword">void</span> <span class="title function_">testGetAll</span><span class="params">()</span>&#123;</span><br><span class="line">        bookService.getAll();</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></p><h4 id="业务层快速开发"><a href="#业务层快速开发" class="headerlink" title="业务层快速开发"></a>业务层快速开发</h4><div class="note info no-icon flat"><p>​MP不仅提供了数据层快速开发方案，业务层MP也给了一个通用接口，其实就是一个封装+继承的思想，代码给出，<strong>实际开发需要慎用</strong></p></div><div class="note warning no-icon flat"><p>接口前面带上I，表示这是一个接口，而不是一个类，这是大多数公司的规范，不强求</p></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></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">interface</span> <span class="title class_">IBookService</span> <span class="keyword">extends</span> <span class="title class_">IService</span>&lt;Book&gt; &#123;</span><br><span class="line">    <span class="comment">//添加非通用操作API接口</span></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><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></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_">BookServiceImpl</span> <span class="keyword">extends</span> <span class="title class_">ServiceImpl</span>&lt;BookDao, Book&gt; <span class="keyword">implements</span> <span class="title class_">IBookService</span> &#123;</span><br><span class="line">    <span class="meta">@Autowired</span></span><br><span class="line">    <span class="keyword">private</span> BookDao bookDao;</span><br><span class="line"><span class="comment">//添加非通用操作API</span></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>测试类：<br><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><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="meta">@SpringBootTest</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">BookServiceTest</span> &#123;</span><br><span class="line">    <span class="meta">@Autowired</span></span><br><span class="line">    <span class="keyword">private</span> IBookService bookService;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Test</span></span><br><span class="line">    <span class="keyword">void</span> <span class="title function_">testGetById</span><span class="params">()</span>&#123;</span><br><span class="line">        System.out.println(bookService.getById(<span class="number">4</span>));</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="meta">@Test</span></span><br><span class="line">    <span class="keyword">void</span> <span class="title function_">testSave</span><span class="params">()</span>&#123;</span><br><span class="line">        <span class="type">Book</span> <span class="variable">book</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">Book</span>();</span><br><span class="line">        book.setType(<span class="string">&quot;测试数据123&quot;</span>);</span><br><span class="line">        book.setName(<span class="string">&quot;测试数据123&quot;</span>);</span><br><span class="line">        book.setDescription(<span class="string">&quot;测试数据123&quot;</span>);</span><br><span class="line">        bookService.save(book);</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="meta">@Test</span></span><br><span class="line">    <span class="keyword">void</span> <span class="title function_">testUpdate</span><span class="params">()</span>&#123;</span><br><span class="line">        <span class="type">Book</span> <span class="variable">book</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">Book</span>();</span><br><span class="line">        book.setId(<span class="number">17</span>);</span><br><span class="line">        book.setType(<span class="string">&quot;-----------------&quot;</span>);</span><br><span class="line">        book.setName(<span class="string">&quot;测试数据123&quot;</span>);</span><br><span class="line">        book.setDescription(<span class="string">&quot;测试数据123&quot;</span>);</span><br><span class="line">        bookService.updateById(book);</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="meta">@Test</span></span><br><span class="line">    <span class="keyword">void</span> <span class="title function_">testDelete</span><span class="params">()</span>&#123;</span><br><span class="line">        bookService.removeById(<span class="number">18</span>);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Test</span></span><br><span class="line">    <span class="keyword">void</span> <span class="title function_">testGetAll</span><span class="params">()</span>&#123;</span><br><span class="line">        bookService.list();</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Test</span></span><br><span class="line">    <span class="keyword">void</span> <span class="title function_">testGetPage</span><span class="params">()</span>&#123;</span><br><span class="line">        IPage&lt;Book&gt; page = <span class="keyword">new</span> <span class="title class_">Page</span>&lt;Book&gt;(<span class="number">2</span>,<span class="number">5</span>);</span><br><span class="line">        bookService.page(page);</span><br><span class="line">        System.out.println(page.getCurrent());</span><br><span class="line">        System.out.println(page.getSize());</span><br><span class="line">        System.out.println(page.getTotal());</span><br><span class="line">        System.out.println(page.getPages());</span><br><span class="line">        System.out.println(page.getRecords());</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><br>​    如果感觉MP提供的功能不足以支撑你的使用需要，只需要在原始基础上定义新的方法或者对旧的方法进行重载即可，但是不要和已有的API接口名冲突。</p><h3 id="表现层开发"><a href="#表现层开发" class="headerlink" title="表现层开发"></a>表现层开发</h3><ul><li>前面搞的都是基础工作，现在开始表现层的处理，表现层的开发基于<code>Restful</code>风格,功能测试使用<code>PostMan</code>工具。</li></ul><p>表现层<br><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><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="meta">@RestController</span></span><br><span class="line"><span class="meta">@RequestMapping(&quot;/books&quot;)</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">BookController2</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> IBookService bookService;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@GetMapping</span></span><br><span class="line">    <span class="keyword">public</span> List&lt;Book&gt; <span class="title function_">getAll</span><span class="params">()</span>&#123;</span><br><span class="line">        <span class="keyword">return</span> bookService.list();</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@PostMapping</span></span><br><span class="line">    <span class="keyword">public</span> Boolean <span class="title function_">save</span><span class="params">(<span class="meta">@RequestBody</span> Book book)</span>&#123;</span><br><span class="line">        <span class="keyword">return</span> bookService.save(book);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@PutMapping</span></span><br><span class="line">    <span class="keyword">public</span> Boolean <span class="title function_">update</span><span class="params">(<span class="meta">@RequestBody</span> Book book)</span>&#123;</span><br><span class="line">        <span class="keyword">return</span> bookService.modify(book);</span><br><span class="line">    &#125;</span><br><span class="line"></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> Boolean <span class="title function_">delete</span><span class="params">(<span class="meta">@PathVariable</span> Integer id)</span>&#123;</span><br><span class="line">        <span class="keyword">return</span> bookService.delete(id);</span><br><span class="line">    &#125;</span><br><span class="line"></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> Book <span class="title function_">getById</span><span class="params">(<span class="meta">@PathVariable</span> Integer id)</span>&#123;</span><br><span class="line">        <span class="keyword">return</span> bookService.getById(id);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@GetMapping(&quot;&#123;currentPage&#125;/&#123;pageSize&#125;&quot;)</span></span><br><span class="line">    <span class="keyword">public</span> IPage&lt;Book&gt; <span class="title function_">getPage</span><span class="params">(<span class="meta">@PathVariable</span> <span class="type">int</span> currentPage,<span class="meta">@PathVariable</span> <span class="type">int</span> pageSize)</span>&#123;</span><br><span class="line">        <span class="keyword">return</span> bookService.getPage(currentPage,pageSize);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></p><p>​在使用Postman测试时关注提交类型，对应上即可，不然就会报405的错误码</p><p><strong>普通GET请求</strong></p><div class="img-wrap"><div class="img-bg"><img class="img" src="https://raw.githubusercontent.com/silvan2077/markdown_pic/main/Picgo/20251030150930.png"/></div></div><p><strong>POST请求传递json数据，后台实用@RequestBody接收数据</strong></p><div class="img-wrap"><div class="img-bg"><img class="img" src="https://raw.githubusercontent.com/silvan2077/markdown_pic/main/Picgo/20251030151439.png"/></div></div><p><strong>GET请求传递路径变量，后台实用@PathVariable接收数据</strong></p><div class="img-wrap"><div class="img-bg"><img class="img" src="https://raw.githubusercontent.com/silvan2077/markdown_pic/main/Picgo/20251030151546.png"/></div></div><h4 id="表现层消息一致性处理"><a href="#表现层消息一致性处理" class="headerlink" title="表现层消息一致性处理"></a>表现层消息一致性处理</h4><p>​    目前我们通过Postman测试后业务层接口功能时通的，但是这样的结果给到前端开发者会出现一个小问题。不同的操作结果所展示的数据格式差异化严重<br><div class="note info no-icon flat"><p>需求：通过Postman测试时会发现，如果调用不同的方法，此时返回的结果类型是不同的，这样的结果会导致前端在处理时数据格式差异化严重，所以需要统一处理。</p></div><br><div class="tabs" id="统一格式数据"><ul class="nav-tabs"><li class="tab active"><button type="button" data-href="#统一格式数据-1">增删改操作结果</button></li><li class="tab"><button type="button" data-href="#统一格式数据-2">查询单个数据操作结果</button></li></ul><div class="tab-contents"><div class="tab-item-content active" id="统一格式数据-1"><figure class="highlight txt"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">true</span><br></pre></td></tr></table></figure><button type="button" class="tab-to-top" aria-label="scroll to top"><i class="fas fa-arrow-up"></i></button></div><div class="tab-item-content" id="统一格式数据-2"><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></pre></td><td class="code"><pre><span class="line"><span class="punctuation">&#123;</span></span><br><span class="line">    <span class="attr">&quot;id&quot;</span><span class="punctuation">:</span> <span class="number">1</span><span class="punctuation">,</span></span><br><span class="line">    <span class="attr">&quot;type&quot;</span><span class="punctuation">:</span> <span class="string">&quot;计算机理论&quot;</span><span class="punctuation">,</span></span><br><span class="line">    <span class="attr">&quot;name&quot;</span><span class="punctuation">:</span> <span class="string">&quot;Spring实战 第5版&quot;</span><span class="punctuation">,</span></span><br><span class="line">    <span class="attr">&quot;description&quot;</span><span class="punctuation">:</span> <span class="string">&quot;Spring入门经典教程&quot;</span></span><br><span class="line"><span class="punctuation">&#125;</span></span><br></pre></td></tr></table></figure><!-- tab 查询全部数据操作结果 --><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><span class="line">10</span><br><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="punctuation">[</span></span><br><span class="line">    <span class="punctuation">&#123;</span></span><br><span class="line">        <span class="attr">&quot;id&quot;</span><span class="punctuation">:</span> <span class="number">1</span><span class="punctuation">,</span></span><br><span class="line">        <span class="attr">&quot;type&quot;</span><span class="punctuation">:</span> <span class="string">&quot;计算机理论&quot;</span><span class="punctuation">,</span></span><br><span class="line">        <span class="attr">&quot;name&quot;</span><span class="punctuation">:</span> <span class="string">&quot;Spring实战 第5版&quot;</span><span class="punctuation">,</span></span><br><span class="line">        <span class="attr">&quot;description&quot;</span><span class="punctuation">:</span> <span class="string">&quot;Spring入门经典教程&quot;</span></span><br><span class="line">    <span class="punctuation">&#125;</span><span class="punctuation">,</span></span><br><span class="line">    <span class="punctuation">&#123;</span></span><br><span class="line">        <span class="attr">&quot;id&quot;</span><span class="punctuation">:</span> <span class="number">2</span><span class="punctuation">,</span></span><br><span class="line">        <span class="attr">&quot;type&quot;</span><span class="punctuation">:</span> <span class="string">&quot;计算机理论&quot;</span><span class="punctuation">,</span></span><br><span class="line">        <span class="attr">&quot;name&quot;</span><span class="punctuation">:</span> <span class="string">&quot;Spring 5核心原理与30个类手写实战&quot;</span><span class="punctuation">,</span></span><br><span class="line">        <span class="attr">&quot;description&quot;</span><span class="punctuation">:</span> <span class="string">&quot;十年沉淀之作&quot;</span></span><br><span class="line">    <span class="punctuation">&#125;</span></span><br><span class="line"><span class="punctuation">]</span></span><br></pre></td></tr></table></figure><button type="button" class="tab-to-top" aria-label="scroll to top"><i class="fas fa-arrow-up"></i></button></div></div></div></p><ul><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></pre></td><td class="code"><pre><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> &#123;</span><br><span class="line">    <span class="keyword">private</span> Boolean flag;</span><br><span class="line">    <span class="keyword">private</span> Object data;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>​    其中<code>flag</code>用于标识操作是否成功，<code>data</code>用于封装操作数据，现在的数据格式就变了</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;flag&quot;</span><span class="punctuation">:</span> <span class="literal"><span class="keyword">true</span></span><span class="punctuation">,</span></span><br><span class="line">    <span class="attr">&quot;data&quot;</span><span class="punctuation">:</span><span class="punctuation">&#123;</span></span><br><span class="line">        <span class="attr">&quot;id&quot;</span><span class="punctuation">:</span> <span class="number">1</span><span class="punctuation">,</span></span><br><span class="line">        <span class="attr">&quot;type&quot;</span><span class="punctuation">:</span> <span class="string">&quot;计算机理论&quot;</span><span class="punctuation">,</span></span><br><span class="line">        <span class="attr">&quot;name&quot;</span><span class="punctuation">:</span> <span class="string">&quot;Spring实战 第5版&quot;</span><span class="punctuation">,</span></span><br><span class="line">        <span class="attr">&quot;description&quot;</span><span class="punctuation">:</span> <span class="string">&quot;Spring入门经典教程&quot;</span></span><br><span class="line">    <span class="punctuation">&#125;</span></span><br><span class="line"><span class="punctuation">&#125;</span></span><br></pre></td></tr></table></figure><p>​表现层开发格式也需要转换一下<br><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><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="meta">@RestController</span></span><br><span class="line"><span class="meta">@RequestMapping(&quot;/books&quot;)</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">BookController</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> IBookService bookService;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@GetMapping</span></span><br><span class="line">    <span class="keyword">public</span> Result <span class="title function_">getAll</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_">Result</span>(<span class="literal">true</span>,bookService.list());</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@PostMapping</span></span><br><span class="line">    <span class="keyword">public</span> Result <span class="title function_">save</span><span class="params">(<span class="meta">@RequestBody</span> Book book)</span>&#123;</span><br><span class="line">        <span class="keyword">return</span> <span class="keyword">new</span> <span class="title class_">Result</span>(bookService.save(book));</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@PutMapping</span></span><br><span class="line">    <span class="keyword">public</span> Result <span class="title function_">update</span><span class="params">(<span class="meta">@RequestBody</span> Book book)</span>&#123;</span><br><span class="line">        <span class="keyword">return</span> <span class="keyword">new</span> <span class="title class_">Result</span>(bookService.modify(book));</span><br><span class="line">    &#125;</span><br><span class="line"></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 <span class="title function_">delete</span><span class="params">(<span class="meta">@PathVariable</span> Integer id)</span>&#123;</span><br><span class="line">        <span class="keyword">return</span> <span class="keyword">new</span> <span class="title class_">Result</span>(bookService.delete(id));</span><br><span class="line">    &#125;</span><br><span class="line"></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> Result <span class="title function_">getById</span><span class="params">(<span class="meta">@PathVariable</span> Integer id)</span>&#123;</span><br><span class="line">        <span class="keyword">return</span> <span class="keyword">new</span> <span class="title class_">Result</span>(<span class="literal">true</span>,bookService.getById(id));</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@GetMapping(&quot;&#123;currentPage&#125;/&#123;pageSize&#125;&quot;)</span></span><br><span class="line">    <span class="keyword">public</span> Result <span class="title function_">getPage</span><span class="params">(<span class="meta">@PathVariable</span> <span class="type">int</span> currentPage, <span class="meta">@PathVariable</span> <span class="type">int</span> pageSize)</span>&#123;</span><br><span class="line">        <span class="keyword">return</span> <span class="keyword">new</span> <span class="title class_">Result</span>(<span class="literal">true</span>,bookService.getPage(currentPage,pageSize));</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></p><p>此时不管执行哪种操作，返回的数据格式都一样了</p><div class="img-wrap"><div class="img-bg"><img class="img" src="https://raw.githubusercontent.com/silvan2077/markdown_pic/main/Picgo/20251030162256.png"/></div></div><h4 id="前后端联通性测试"><a href="#前后端联通性测试" class="headerlink" title="前后端联通性测试"></a>前后端联通性测试</h4><ul><li>此时后端的表现层接口开发完毕，就可以进行前端的开发了，将前端人员开发的页面保存到lresources目录下的static目录中，建议执行maven的clean生命周期，避免缓存的问题出现。</li><li><p>导入前端文件后，重新启动项目就可以在浏览器中搜索<a href="localhost:8080/pages/books.html">localhost:8080/pages/books.html</a>来查看前端页面了</p><div class="img-wrap"><div class="img-bg"><img class="img" src="https://raw.githubusercontent.com/silvan2077/markdown_pic/main/Picgo/20251030162821.png" style="width:1000px;"/></div></div></li><li><p>在进行具体的功能开发之前，先做联通性的测试，通过页面发送<code>异步提交</code>（axios），这一步调试通过后再进行进一步的功能开发,先打开<code>pages/books.html</code>页面，编辑钩子函数和getAll方法</p><div class="tabs" id="连通性测试"><ul class="nav-tabs"><li class="tab active"><button type="button" data-href="#连通性测试-1">created()</button></li><li class="tab"><button type="button" data-href="#连通性测试-2">getAll()</button></li></ul><div class="tab-contents"><div class="tab-item-content active" id="连通性测试-1"><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><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">//钩子函数，VUE对象初始化完成后自动执行</span></span><br><span class="line">        <span class="title function_">created</span>(<span class="params"></span>) &#123;</span><br><span class="line">        <span class="comment">//  调用查询结构数据的函数</span></span><br><span class="line">            <span class="variable language_">this</span>.<span class="title function_">getAll</span>();</span><br><span class="line">        &#125;</span><br></pre></td></tr></table></figure><button type="button" class="tab-to-top" aria-label="scroll to top"><i class="fas fa-arrow-up"></i></button></div><div class="tab-item-content" id="连通性测试-2"><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><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="title function_">getAll</span>(<span class="params"></span>) &#123;</span><br><span class="line">axios.<span class="title function_">get</span>(<span class="string">&quot;/books&quot;</span>).<span class="title function_">then</span>(<span class="function">(<span class="params">res</span>)=&gt;</span>&#123;</span><br><span class="line"><span class="variable language_">console</span>.<span class="title function_">log</span>(res.<span class="property">data</span>);</span><br><span class="line">&#125;);</span><br><span class="line">&#125;,</span><br></pre></td></tr></table></figure><button type="button" class="tab-to-top" aria-label="scroll to top"><i class="fas fa-arrow-up"></i></button></div></div></div><p>此时刷新页面，在控制台中就可以看到数据了</p><div class="img-wrap"><div class="img-bg"><img class="img" src="https://raw.githubusercontent.com/silvan2077/markdown_pic/main/Picgo/20251030163932.png" style="width:1000px;"/></div></div></li></ul><div class="note info no-icon flat"><p>只要后台代码能够正常工作，前端能够在日志中接收到数据，就证明前后端是通的，也就可以进行下一步的功能开发了</p></div><h4 id="页面基础功能开发"><a href="#页面基础功能开发" class="headerlink" title="页面基础功能开发"></a>页面基础功能开发</h4><h5 id="列表功能（非分页版）"><a href="#列表功能（非分页版）" class="headerlink" title="列表功能（非分页版）"></a>列表功能（非分页版）</h5><div class="note info no-icon flat"><p>需求：列表功能主要操作就是加载完数据，将数据展示到页面上</p></div><p>此处利用VUE的数据模型绑定，发送请求得到数据，然后页面上读取指定数据即可<br>​        <strong>页面数据模型定义</strong><br><div class="tabs" id="基础功能开发-列表功能（非分页版）"><ul class="nav-tabs"><li class="tab active"><button type="button" data-href="#基础功能开发-列表功能（非分页版）-1">页面数据模型定义</button></li><li class="tab"><button type="button" data-href="#基础功能开发-列表功能（非分页版）-2">异步请求获取数据</button></li></ul><div class="tab-contents"><div class="tab-item-content active" id="基础功能开发-列表功能（非分页版）-1"><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></pre></td><td class="code"><pre><span class="line">data:&#123;</span><br><span class="line">dataList: [],//当前页要展示的列表数据</span><br><span class="line">...</span><br><span class="line">&#125;,</span><br></pre></td></tr></table></figure><button type="button" class="tab-to-top" aria-label="scroll to top"><i class="fas fa-arrow-up"></i></button></div><div class="tab-item-content" id="基础功能开发-列表功能（非分页版）-2"><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><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="title function_">getAll</span>(<span class="params"></span>) &#123;</span><br><span class="line">    axios.<span class="title function_">get</span>(<span class="string">&quot;/books&quot;</span>).<span class="title function_">then</span>(<span class="function">(<span class="params">res</span>)=&gt;</span>&#123;</span><br><span class="line">        <span class="variable language_">this</span>.<span class="property">dataList</span> = res.<span class="property">data</span>.<span class="property">data</span>;</span><br><span class="line">    &#125;);</span><br><span class="line">&#125;,</span><br></pre></td></tr></table></figure><button type="button" class="tab-to-top" aria-label="scroll to top"><i class="fas fa-arrow-up"></i></button></div></div></div></p><p>此时页面加载时就可以获取到数据，并且由VUE将数据展示到页面上了<br><div class="img-wrap"><div class="img-bg"><img class="img" src="https://raw.githubusercontent.com/silvan2077/markdown_pic/main/Picgo/20251030165226.png"/></div></div></p><h5 id="添加功能"><a href="#添加功能" class="headerlink" title="添加功能"></a>添加功能</h5><div class="note info no-icon flat"><p>需求：添加功能主要操作是打开一个弹窗，将数据录入到弹窗中，点击弹窗的保存按钮，将数据保存到数据库中，并关闭弹窗，重新加载数据列表</p></div><ul><li><p>此处以切换弹窗状态为隐藏或显示来实现弹窗的打开和关闭，页面加载时，设置弹窗为不可显示</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></pre></td><td class="code"><pre><span class="line">data:&#123;</span><br><span class="line">dialogFormVisible: false,//添加表单是否可见</span><br><span class="line">...</span><br><span class="line">&#125;,</span><br></pre></td></tr></table></figure></li><li><p>可以看到页面的新建按钮关联了handleCreate()方法，点击按钮时，会调用该方法，因此只要在该方法中设置弹窗可见即可</p><div class="tabs" id="设置弹窗可见"><ul class="nav-tabs"><li class="tab active"><button type="button" data-href="#设置弹窗可见-1">新建按钮</button></li><li class="tab"><button type="button" data-href="#设置弹窗可见-2">handleCreate()</button></li></ul><div class="tab-contents"><div class="tab-item-content active" id="设置弹窗可见-1"><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></pre></td><td class="code"><pre><span class="line"><span class="tag">&lt;<span class="name">div</span> <span class="attr">class</span>=<span class="string">&quot;filter-container&quot;</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">el-input</span> <span class="attr">placeholder</span>=<span class="string">&quot;图书类别&quot;</span> <span class="attr">style</span>=<span class="string">&quot;width: 200px;&quot;</span> <span class="attr">class</span>=<span class="string">&quot;filter-item&quot;</span>&gt;</span><span class="tag">&lt;/<span class="name">el-input</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">el-input</span> <span class="attr">placeholder</span>=<span class="string">&quot;图书名称&quot;</span> <span class="attr">style</span>=<span class="string">&quot;width: 200px;&quot;</span> <span class="attr">class</span>=<span class="string">&quot;filter-item&quot;</span>&gt;</span><span class="tag">&lt;/<span class="name">el-input</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">el-input</span> <span class="attr">placeholder</span>=<span class="string">&quot;图书描述&quot;</span> <span class="attr">style</span>=<span class="string">&quot;width: 200px;&quot;</span> <span class="attr">class</span>=<span class="string">&quot;filter-item&quot;</span>&gt;</span><span class="tag">&lt;/<span class="name">el-input</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">el-button</span> @<span class="attr">click</span>=<span class="string">&quot;getAll()&quot;</span> <span class="attr">class</span>=<span class="string">&quot;dalfBut&quot;</span>&gt;</span>查询<span class="tag">&lt;/<span class="name">el-button</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">el-button</span> <span class="attr">type</span>=<span class="string">&quot;primary&quot;</span> <span class="attr">class</span>=<span class="string">&quot;butT&quot;</span> @<span class="attr">click</span>=<span class="string">&quot;handleCreate()&quot;</span>&gt;</span>新建<span class="tag">&lt;/<span class="name">el-button</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;/<span class="name">div</span>&gt;</span></span><br></pre></td></tr></table></figure><button type="button" class="tab-to-top" aria-label="scroll to top"><i class="fas fa-arrow-up"></i></button></div><div class="tab-item-content" id="设置弹窗可见-2"><figure class="highlight js"><table><tr><td class="gutter"><pre><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">//弹出添加窗口</span></span><br><span class="line"><span class="title function_">handleCreate</span>(<span class="params"></span>) &#123;</span><br><span class="line"><span class="variable language_">this</span>.<span class="property">dialogFormVisible</span> = <span class="literal">true</span>;</span><br><span class="line">&#125;,</span><br></pre></td></tr></table></figure><button type="button" class="tab-to-top" aria-label="scroll to top"><i class="fas fa-arrow-up"></i></button></div></div></div></li></ul><p>由于每次添加数据都是使用同一个弹窗录入数据，需要在每次操作之前需要先清理掉上次弹窗中的操作痕迹</p><div class="tabs" id="充值弹窗"><ul class="nav-tabs"><li class="tab active"><button type="button" data-href="#充值弹窗-1">重置弹窗方法</button></li><li class="tab"><button type="button" data-href="#充值弹窗-2">handleCreate()</button></li></ul><div class="tab-contents"><div class="tab-item-content active" id="充值弹窗-1"><figure class="highlight js"><table><tr><td class="gutter"><pre><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">//重置表单</span></span><br><span class="line"><span class="title function_">resetForm</span>(<span class="params"></span>) &#123;</span><br><span class="line">    <span class="variable language_">this</span>.<span class="property">formData</span> = &#123;&#125;;</span><br><span class="line">&#125;,</span><br></pre></td></tr></table></figure><button type="button" class="tab-to-top" aria-label="scroll to top"><i class="fas fa-arrow-up"></i></button></div><div class="tab-item-content" id="充值弹窗-2"><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><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="title function_">handleCreate</span>(<span class="params"></span>) &#123;</span><br><span class="line">    <span class="variable language_">this</span>.<span class="property">dialogFormVisible</span> = <span class="literal">true</span>;</span><br><span class="line">    <span class="variable language_">this</span>.<span class="title function_">resetForm</span>();</span><br><span class="line">&#125;,</span><br></pre></td></tr></table></figure><button type="button" class="tab-to-top" aria-label="scroll to top"><i class="fas fa-arrow-up"></i></button></div></div></div><div class="note info no-icon flat"><p>此时准备工作已经完成，可以调用后台来完成添加操作了</p></div><ul><li>通过查看代码，发现确定和取消都绑定了相应的方法，针对不同按钮编辑不同方法<div class="tabs" id="确定和取消"><ul class="nav-tabs"><li class="tab active"><button type="button" data-href="#确定和取消-1">handleAdd()</button></li><li class="tab"><button type="button" data-href="#确定和取消-2">cancel</button></li></ul><div class="tab-contents"><div class="tab-item-content active" id="确定和取消-1"><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><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="title function_">handleAdd</span> () &#123;</span><br><span class="line">    <span class="comment">//发送异步请求</span></span><br><span class="line">    axios.<span class="title function_">post</span>(<span class="string">&quot;/books&quot;</span>,<span class="variable language_">this</span>.<span class="property">formData</span>).<span class="title function_">then</span>(<span class="function">(<span class="params">res</span>)=&gt;</span>&#123;</span><br><span class="line">        <span class="comment">//如果操作成功，关闭弹层，显示数据</span></span><br><span class="line">        <span class="keyword">if</span>(res.<span class="property">data</span>.<span class="property">flag</span>)&#123;</span><br><span class="line">            <span class="variable language_">this</span>.<span class="property">dialogFormVisible</span> = <span class="literal">false</span>;</span><br><span class="line">            <span class="variable language_">this</span>.<span class="property">$message</span>.<span class="title function_">success</span>(<span class="string">&quot;添加成功&quot;</span>);</span><br><span class="line">        &#125;<span class="keyword">else</span> &#123;</span><br><span class="line">            <span class="variable language_">this</span>.<span class="property">$message</span>.<span class="title function_">error</span>(<span class="string">&quot;添加失败&quot;</span>);</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;).<span class="title function_">finally</span>(<span class="function">()=&gt;</span>&#123;</span><br><span class="line">        <span class="variable language_">this</span>.<span class="title function_">getAll</span>();</span><br><span class="line">    &#125;);</span><br><span class="line">&#125;,</span><br></pre></td></tr></table></figure><button type="button" class="tab-to-top" aria-label="scroll to top"><i class="fas fa-arrow-up"></i></button></div><div class="tab-item-content" id="确定和取消-2"><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><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="title function_">cancel</span>(<span class="params"></span>)&#123;</span><br><span class="line">    <span class="variable language_">this</span>.<span class="property">dialogFormVisible</span> = <span class="literal">false</span>;</span><br><span class="line">    <span class="variable language_">this</span>.<span class="property">$message</span>.<span class="title function_">info</span>(<span class="string">&quot;操作取消&quot;</span>);</span><br><span class="line">&#125;,</span><br></pre></td></tr></table></figure><button type="button" class="tab-to-top" aria-label="scroll to top"><i class="fas fa-arrow-up"></i></button></div></div></div></li></ul><h5 id="删除功能"><a href="#删除功能" class="headerlink" title="删除功能"></a>删除功能</h5><div class="note info no-icon flat"><p>需求：模仿添加操作来制作删除功能</p></div><p>​</p><p>​        <strong>删除操作</strong></p><ul><li><p>发现删除操作绑定的方法是<code>handleDelete(row)</code>，这里row是封装了当前行的数据</p><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><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="title function_">handleDelete</span>(<span class="params">row</span>) &#123;</span><br><span class="line">    axios.<span class="title function_">delete</span>(<span class="string">&quot;/books/&quot;</span>+row.<span class="property">id</span>).<span class="title function_">then</span>(<span class="function">(<span class="params">res</span>)=&gt;</span>&#123;</span><br><span class="line">        <span class="keyword">if</span>(res.<span class="property">data</span>.<span class="property">flag</span>)&#123;</span><br><span class="line">            <span class="variable language_">this</span>.<span class="property">$message</span>.<span class="title function_">success</span>(<span class="string">&quot;删除成功&quot;</span>);</span><br><span class="line">        &#125;<span class="keyword">else</span>&#123;</span><br><span class="line">            <span class="variable language_">this</span>.<span class="property">$message</span>.<span class="title function_">error</span>(<span class="string">&quot;删除失败&quot;</span>);</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;).<span class="title function_">finally</span>(<span class="function">()=&gt;</span>&#123;</span><br><span class="line">        <span class="variable language_">this</span>.<span class="title function_">getAll</span>();</span><br><span class="line">    &#125;);</span><br><span class="line">&#125;,</span><br></pre></td></tr></table></figure></li><li><p>给删除操作添加上提示信息，以免用户误操作</p><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><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">// row是传递的这一整行的数据</span></span><br><span class="line"><span class="title function_">handleDelete</span>(<span class="params">row</span>) &#123;</span><br><span class="line">    <span class="comment">//1.弹出提示框</span></span><br><span class="line">    <span class="variable language_">this</span>.$confirm(<span class="string">&quot;此操作永久删除当前数据，是否继续？&quot;</span>,<span class="string">&quot;提示&quot;</span>,&#123;</span><br><span class="line">        <span class="attr">type</span>:<span class="string">&#x27;info&#x27;</span></span><br><span class="line">    &#125;).<span class="title function_">then</span>(<span class="function">()=&gt;</span>&#123;</span><br><span class="line">        <span class="comment">//2.做删除业务</span></span><br><span class="line">        axios.<span class="title function_">delete</span>(<span class="string">&quot;/books/&quot;</span>+row.<span class="property">id</span>).<span class="title function_">then</span>(<span class="function">(<span class="params">res</span>)=&gt;</span>&#123;</span><br><span class="line">       <span class="keyword">if</span>(res.<span class="property">data</span>.<span class="property">flag</span>)&#123;</span><br><span class="line">            <span class="variable language_">this</span>.<span class="property">$message</span>.<span class="title function_">success</span>(<span class="string">&quot;删除成功&quot;</span>);</span><br><span class="line">        &#125;<span class="keyword">else</span>&#123;</span><br><span class="line">            <span class="variable language_">this</span>.<span class="property">$message</span>.<span class="title function_">error</span>(<span class="string">&quot;删除失败&quot;</span>);</span><br><span class="line">        &#125;</span><br><span class="line">        &#125;).<span class="title function_">finally</span>(<span class="function">()=&gt;</span>&#123;</span><br><span class="line">            <span class="variable language_">this</span>.<span class="title function_">getAll</span>();</span><br><span class="line">        &#125;);</span><br><span class="line">    &#125;).<span class="title function_">catch</span>(<span class="function">()=&gt;</span>&#123;</span><br><span class="line">        <span class="comment">//3.取消删除</span></span><br><span class="line">        <span class="variable language_">this</span>.<span class="property">$message</span>.<span class="title function_">info</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></li></ul><h5 id="修改功能"><a href="#修改功能" class="headerlink" title="修改功能"></a>修改功能</h5><div class="note info no-icon flat"><p>​修改功能可以说是列表功能、删除功能与添加功能的合体。几个相似点如下：</p><ol><li>页面也需要有一个弹窗用来加载修改的数据，这一点与添加相同，都要有弹窗</li><li>弹出的窗口中需要加载待修改的数据，而数据需要通过查询得到，这一点与查询相同，都要查数据</li><li>查询操作需要将要修改的数据id发送到后台，这一点与删除相同，都是传递id到后台</li><li>修改数据时需要将被修改的数据传递到后台，这一点与添加相同，都要传递数据</li></ol></div><ul><li>首先为修改操作弹出一个带有数据的弹窗<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><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="title function_">handleUpdate</span>(<span class="params">row</span>) &#123;</span><br><span class="line">    axios.<span class="title function_">get</span>(<span class="string">&quot;/books/&quot;</span>+row.<span class="property">id</span>).<span class="title function_">then</span>(<span class="function">(<span class="params">res</span>)=&gt;</span>&#123;</span><br><span class="line">        <span class="keyword">if</span>(res.<span class="property">data</span>.<span class="property">flag</span>&amp;&amp;res.<span class="property">data</span>.<span class="property">data</span>)&#123;</span><br><span class="line">            <span class="comment">//展示弹层，加载数据</span></span><br><span class="line">            <span class="variable language_">this</span>.<span class="property">formData</span> = res.<span class="property">data</span>.<span class="property">data</span>;</span><br><span class="line">            <span class="variable language_">this</span>.<span class="property">dialogFormVisible4Edit</span> = <span class="literal">true</span>;</span><br><span class="line">        &#125;<span class="keyword">else</span>&#123;</span><br><span class="line">            <span class="variable language_">this</span>.<span class="property">$message</span>.<span class="title function_">error</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></li><li>当我们修改弹窗中的数据后，点击确定按钮，将数据发送到后台进行修改<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><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="title function_">handleEdit</span>(<span class="params"></span>) &#123;</span><br><span class="line">    axios.<span class="title function_">put</span>(<span class="string">&quot;/books&quot;</span>,<span class="variable language_">this</span>.<span class="property">formData</span>).<span class="title function_">then</span>(<span class="function">(<span class="params">res</span>)=&gt;</span>&#123;</span><br><span class="line">        <span class="comment">//如果操作成功，关闭弹层并刷新页面</span></span><br><span class="line">        <span class="keyword">if</span>(res.<span class="property">data</span>.<span class="property">flag</span>)&#123;</span><br><span class="line">            <span class="variable language_">this</span>.<span class="property">dialogFormVisible4Edit</span> = <span class="literal">false</span>;</span><br><span class="line">            <span class="variable language_">this</span>.<span class="property">$message</span>.<span class="title function_">success</span>(<span class="string">&quot;修改成功&quot;</span>);</span><br><span class="line">        &#125;<span class="keyword">else</span> &#123;</span><br><span class="line">            <span class="variable language_">this</span>.<span class="property">$message</span>.<span class="title function_">error</span>(<span class="string">&quot;修改失败，请重试&quot;</span>);</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;).<span class="title function_">finally</span>(<span class="function">()=&gt;</span>&#123;</span><br><span class="line">        <span class="variable language_">this</span>.<span class="title function_">getAll</span>();</span><br><span class="line">    &#125;);</span><br><span class="line">&#125;,</span><br></pre></td></tr></table></figure>​    </li></ul><h4 id="异常处理"><a href="#异常处理" class="headerlink" title="异常处理"></a>异常处理</h4><p>目前的基本功能制作大致完成了正常使用的情况，但是如果这个程序出现Bug导致系统抛出一个异常，此时就会出现一些问题，这里我们在后台模拟抛出一个异常然后查看前端接收到的数据。<br><div class="tabs" id="基础开发处理异常"><ul class="nav-tabs"><li class="tab active"><button type="button" data-href="#基础开发处理异常-1">BookController</button></li><li class="tab"><button type="button" data-href="#基础开发处理异常-2">Result</button></li></ul><div class="tab-contents"><div class="tab-item-content active" id="基础开发处理异常-1"><p>手动在程序中抛出一个异常，查看前端接收到的数据<br><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><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="meta">@GetMapping</span></span><br><span class="line">    <span class="keyword">public</span> Result <span class="title function_">getAll</span><span class="params">()</span>&#123;</span><br><span class="line">        <span class="keyword">if</span>(<span class="literal">true</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 class="keyword">return</span> <span class="keyword">new</span> <span class="title class_">Result</span>(<span class="literal">true</span>,bookService.list());</span><br><span class="line">    &#125;</span><br></pre></td></tr></table></figure></p><button type="button" class="tab-to-top" aria-label="scroll to top"><i class="fas fa-arrow-up"></i></button></div><div class="tab-item-content" id="基础开发处理异常-2"><ul><li>此时会发现这又是一个数据格式，与之前的格式不同。<div class="img-wrap"><div class="img-bg"><img class="img" src="https://raw.githubusercontent.com/silvan2077/markdown_pic/main/Picgo/20251030180226.png"/></div></div></li></ul><button type="button" class="tab-to-top" aria-label="scroll to top"><i class="fas fa-arrow-up"></i></button></div></div></div></p><p>​因此我们不仅要对正确的操作数据格式做处理，还要对错误的操作数据格式做同样的格式处理，在返回错误数据格式的时候，添加一个字段，用来返回错误消息<br><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><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="meta">@Data</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">Result</span>&#123;</span><br><span class="line">    <span class="keyword">private</span> Boolean flag;</span><br><span class="line">    <span class="keyword">private</span> Object data;</span><br><span class="line">    <span class="keyword">private</span> String msg;<span class="comment">//用于封装消息</span></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></p><p>同样要添加好对应的构造函数，因为需要封装错误信息时一般都是操作失败，此时数据也没有意义了，所以只需要添加一个包含错误信息字段的构造函数即可<br><figure class="highlight java"><table><tr><td class="gutter"><pre><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="keyword">public</span> <span class="title function_">Result</span><span class="params">(String errorMessage)</span> &#123;</span><br><span class="line">        <span class="built_in">this</span>.flag = <span class="literal">false</span>;</span><br><span class="line">        <span class="built_in">this</span>.errorMessage = errorMessage;</span><br><span class="line">    &#125;</span><br></pre></td></tr></table></figure></p><p>​然后在表现层做统一的异常处理，使用SpringMVC提供的<code>@RestControllerAdvice</code>和<code>@ExceptionHandler</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></pre></td><td class="code"><pre><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_">ProjectExceptionAdvice</span> &#123;</span><br><span class="line">    <span class="meta">@ExceptionHandler(Exception.class)</span></span><br><span class="line">    <span class="keyword">public</span> R <span class="title function_">doOtherException</span><span class="params">(Exception ex)</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">        <span class="comment">//发送邮件给开发人员,ex对象发送给开发人员</span></span><br><span class="line">        ex.printStackTrace();</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_">R</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><p>此时测试后端程序可以看到返回的内容，格式已经符合要求<br><div class="img-wrap"><div class="img-bg"><img class="img" src="https://raw.githubusercontent.com/silvan2077/markdown_pic/main/Picgo/20251030181736.png"/></div></div></p><p>​当页面上得到数据后，先判定是否有后台传递过来的消息，标志就是当前操作是否成功，如果返回操作结果false，就读取后台传递的消息</p><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><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="title function_">handleAdd</span> () &#123;</span><br><span class="line"><span class="comment">//发送ajax请求</span></span><br><span class="line">    axios.<span class="title function_">post</span>(<span class="string">&quot;/books&quot;</span>,<span class="variable language_">this</span>.<span class="property">formData</span>).<span class="title function_">then</span>(<span class="function">(<span class="params">res</span>)=&gt;</span>&#123;</span><br><span class="line">        <span class="comment">//如果操作成功，关闭弹层，显示数据</span></span><br><span class="line">        <span class="keyword">if</span>(res.<span class="property">data</span>.<span class="property">flag</span>)&#123;</span><br><span class="line">            <span class="variable language_">this</span>.<span class="property">dialogFormVisible</span> = <span class="literal">false</span>;</span><br><span class="line">            <span class="variable language_">this</span>.<span class="property">$message</span>.<span class="title function_">success</span>(<span class="string">&quot;添加成功&quot;</span>);</span><br><span class="line">        &#125;<span class="keyword">else</span> &#123;</span><br><span class="line">            <span class="variable language_">this</span>.<span class="property">$message</span>.<span class="title function_">error</span>(res.<span class="property">data</span>.<span class="property">msg</span>);<span class="comment">//消息来自于后台传递过来，而非固定内容</span></span><br><span class="line">        &#125;</span><br><span class="line">    &#125;).<span class="title function_">finally</span>(<span class="function">()=&gt;</span>&#123;</span><br><span class="line">        <span class="variable language_">this</span>.<span class="title function_">getAll</span>();</span><br><span class="line">    &#125;);</span><br><span class="line">&#125;,</span><br></pre></td></tr></table></figure><p>​    </p><h4 id="页面功能开发"><a href="#页面功能开发" class="headerlink" title="页面功能开发"></a>页面功能开发</h4><h5 id="分页功能"><a href="#分页功能" class="headerlink" title="分页功能"></a>分页功能</h5><div class="note info no-icon flat"><p>需求：实现页面数据分页展示</p></div><ul><li><p>使用el分页组件来完成分页展示</p><div class="tabs" id="分页功能"><ul class="nav-tabs"><li class="tab active"><button type="button" data-href="#分页功能-1">分页组件</button></li><li class="tab"><button type="button" data-href="#分页功能-2">封装数据类型</button></li></ul><div class="tab-contents"><div class="tab-item-content active" id="分页功能-1"><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><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">&lt;!--分页组件--&gt;</span><br><span class="line"><span class="language-xml"><span class="tag">&lt;<span class="name">div</span> <span class="attr">class</span>=<span class="string">&quot;pagination-container&quot;</span>&gt;</span></span></span><br><span class="line"><span class="language-xml">    <span class="tag">&lt;<span class="name">el-pagination</span></span></span></span><br><span class="line"><span class="tag"><span class="language-xml"><span class="attr">class</span>=<span class="string">&quot;pagiantion&quot;</span></span></span></span><br><span class="line"><span class="tag"><span class="language-xml">@<span class="attr">current-change</span>=<span class="string">&quot;handleCurrentChange&quot;</span></span></span></span><br><span class="line"><span class="tag"><span class="language-xml"><span class="attr">:current-page</span>=<span class="string">&quot;pagination.currentPage&quot;</span></span></span></span><br><span class="line"><span class="tag"><span class="language-xml"><span class="attr">:page-size</span>=<span class="string">&quot;pagination.pageSize&quot;</span></span></span></span><br><span class="line"><span class="tag"><span class="language-xml"><span class="attr">layout</span>=<span class="string">&quot;total, prev, pager, next, jumper&quot;</span></span></span></span><br><span class="line"><span class="tag"><span class="language-xml"><span class="attr">:total</span>=<span class="string">&quot;pagination.total&quot;</span>&gt;</span></span></span><br><span class="line"><span class="language-xml">    <span class="tag">&lt;/<span class="name">el-pagination</span>&gt;</span></span></span><br><span class="line"><span class="language-xml"><span class="tag">&lt;/<span class="name">div</span>&gt;</span></span></span><br></pre></td></tr></table></figure><button type="button" class="tab-to-top" aria-label="scroll to top"><i class="fas fa-arrow-up"></i></button></div><div class="tab-item-content" id="分页功能-2"><p>为了配合分页组件，封装分页对应的数据模型<br><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></pre></td><td class="code"><pre><span class="line">data:&#123;</span><br><span class="line">pagination: &#123;</span><br><span class="line">//分页相关模型数据</span><br><span class="line">currentPage: 1,//当前页码</span><br><span class="line">pageSize:10,//每页显示的记录数</span><br><span class="line">total:0,//总记录数</span><br><span class="line">&#125;</span><br><span class="line">&#125;,</span><br></pre></td></tr></table></figure></p><button type="button" class="tab-to-top" aria-label="scroll to top"><i class="fas fa-arrow-up"></i></button></div></div></div></li><li><p>修改查询全部功能为分页查询，通过路径变量传递页码信息参数</p></li></ul><figure class="highlight js"><table><tr><td class="gutter"><pre><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="title function_">getAll</span>(<span class="params"></span>) &#123;</span><br><span class="line">    axios.<span class="title function_">get</span>(<span class="string">&quot;/books/&quot;</span>+<span class="variable language_">this</span>.<span class="property">pagination</span>.<span class="property">currentPage</span>+<span class="string">&quot;/&quot;</span>+<span class="variable language_">this</span>.<span class="property">pagination</span>.<span class="property">pageSize</span>).<span class="title function_">then</span>(<span class="function">(<span class="params">res</span>) =&gt;</span> &#123;</span><br><span class="line">    &#125;);</span><br><span class="line">&#125;,</span><br></pre></td></tr></table></figure><ul><li>此时不着急显示数据，可以在网页上查看请求的是否为设置的分页参数<div class="img-wrap"><div class="img-bg"><img class="img" src="https://raw.githubusercontent.com/silvan2077/markdown_pic/main/Picgo/20251030184007.png"/></div></div></li></ul><ul><li>现在来进行页面数据的显示以及分页组件数据的绑定，​页面根据分页操作结果读取对应数据，并进行数据模型绑定</li></ul><div class="tabs" id="分页组件绑定"><ul class="nav-tabs"><li class="tab active"><button type="button" data-href="#分页组件绑定-1">分页组件绑定</button></li><li class="tab"><button type="button" data-href="#分页组件绑定-2">效果展示</button></li></ul><div class="tab-contents"><div class="tab-item-content active" id="分页组件绑定-1"><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><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="title function_">getAll</span>(<span class="params"></span>) &#123;</span><br><span class="line">    axios.<span class="title function_">get</span>(<span class="string">&quot;/books/&quot;</span>+<span class="variable language_">this</span>.<span class="property">pagination</span>.<span class="property">currentPage</span>+<span class="string">&quot;/&quot;</span>+<span class="variable language_">this</span>.<span class="property">pagination</span>.<span class="property">pageSize</span>).<span class="title function_">then</span>(<span class="function">(<span class="params">res</span>) =&gt;</span> &#123;</span><br><span class="line">        <span class="variable language_">this</span>.<span class="property">pagination</span>.<span class="property">total</span> = res.<span class="property">data</span>.<span class="property">data</span>.<span class="property">total</span>;</span><br><span class="line">        <span class="variable language_">this</span>.<span class="property">pagination</span>.<span class="property">currentPage</span> = res.<span class="property">data</span>.<span class="property">data</span>.<span class="property">current</span>;</span><br><span class="line">        <span class="variable language_">this</span>.<span class="property">pagination</span>.<span class="property">pagesize</span> = res.<span class="property">data</span>.<span class="property">data</span>.<span class="property">size</span>;</span><br><span class="line">        <span class="variable language_">this</span>.<span class="property">dataList</span> = res.<span class="property">data</span>.<span class="property">data</span>.<span class="property">records</span>;</span><br><span class="line">    &#125;);</span><br><span class="line">&#125;,</span><br></pre></td></tr></table></figure><button type="button" class="tab-to-top" aria-label="scroll to top"><i class="fas fa-arrow-up"></i></button></div><div class="tab-item-content" id="分页组件绑定-2"><div class="img-wrap"><div class="img-bg"><img class="img" src="https://raw.githubusercontent.com/silvan2077/markdown_pic/main/Picgo/20251030184738.png"/></div></div><button type="button" class="tab-to-top" aria-label="scroll to top"><i class="fas fa-arrow-up"></i></button></div></div></div><p>​对切换页码操作设置调用当前分页操作</p><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><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="title function_">handleCurrentChange</span>(<span class="params">currentPage</span>) &#123;</span><br><span class="line">    <span class="variable language_">this</span>.<span class="property">pagination</span>.<span class="property">currentPage</span> = currentPage;</span><br><span class="line">    <span class="variable language_">this</span>.<span class="title function_">getAll</span>();</span><br><span class="line">&#125;,</span><br></pre></td></tr></table></figure><h5 id="删除功能维护"><a href="#删除功能维护" class="headerlink" title="删除功能维护"></a>删除功能维护</h5><p>​由于使用了分页功能，当最后一页只有一条数据时，执行删除操作就会出现BUG，最后一页无数据但是独立展示，所以要对分页查询功能进行后台功能维护，如果当前页码值大于最大页码值，重新执行查询。其实这个问题解决方案很多，这里给出比较简单的一种处理方案<br><div class="img-wrap"><div class="img-bg"><img class="img" src="https://raw.githubusercontent.com/silvan2077/markdown_pic/main/Picgo/20251031120215.png"/></div></div></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">@GetMapping(&quot;&#123;currentPage&#125;/&#123;pageSize&#125;&quot;)</span></span><br><span class="line"><span class="keyword">public</span> R <span class="title function_">getPage</span><span class="params">(<span class="meta">@PathVariable</span> <span class="type">int</span> currentPage,<span class="meta">@PathVariable</span> <span class="type">int</span> pageSize)</span>&#123;</span><br><span class="line">    IPage&lt;Book&gt; page = bookService.getPage(currentPage, pageSize);</span><br><span class="line">    <span class="comment">//如果当前页码值大于了总页码值，那么重新执行查询操作，使用最大页码值作为当前页码值</span></span><br><span class="line">    <span class="keyword">if</span>( currentPage &gt; page.getPages())&#123;</span><br><span class="line">        page = bookService.getPage((<span class="type">int</span>)page.getPages(), pageSize);</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">return</span> <span class="keyword">new</span> <span class="title class_">R</span>(<span class="literal">true</span>, page);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><div class="note info no-icon flat"><p>这样做只是一种简单的补救性方案，如果某一时刻同时删除大量数据，依旧会出现问题，应当根据不同情况来做相应处理</p></div><h5 id="条件查询功能"><a href="#条件查询功能" class="headerlink" title="条件查询功能"></a>条件查询功能</h5><ul><li>此时页面的大部分功能已经完成，接下来来完成分页查询操作，目前查询条件还无法添加，当输入数据时会直接显示<code>undefined</code>。<div class="img-wrap"><div class="img-bg"><img class="img" src="https://raw.githubusercontent.com/silvan2077/markdown_pic/main/Picgo/20251031120823.png"/></div></div></li></ul><p>条件查询分析：条件查询可以理解为分页查询的时候除了携带分页数据再多带几个查询条件的查询。比较一下不带条件的分页查询与带条件的分页查询差别之处</p><ul><li>页面封装的数据：带不带条件影响的仅仅是一次性传递到后台的数据总量，由传递2个分页相关的数据转换成2个分页数据加若干个条件</li><li>后台查询功能：查询时由不带条件，转换成带条件，反正不带条件的时候查询条件对象使用的是null，现在换成具体条件，差别不大</li><li><p>查询结果：不管带不带条件，出来的数据只是有数量上的差别，其他都差别，这个可以忽略</p><p>经过上述分析，看来需要在页面发送请求的格式方面做一定的修改，后台的调用数据层操作时发送修改，其他没有区别</p><p>页面发送请求时，两个分页数据仍然使用路径变量，其他条件采用动态拼装url参数的形式传递</p><p><strong>页面封装查询条件字段</strong></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><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line">pagination: &#123;</span><br><span class="line">//分页相关模型数据</span><br><span class="line">currentPage: 1,//当前页码</span><br><span class="line">pageSize:10,//每页显示的记录数</span><br><span class="line">total:0,//总记录数</span><br><span class="line">name: &quot;&quot;,</span><br><span class="line">type: &quot;&quot;,</span><br><span class="line">description: &quot;&quot;</span><br><span class="line">&#125;,</span><br></pre></td></tr></table></figure><p>页面添加查询条件字段对应的数据模型绑定名称</p><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></pre></td><td class="code"><pre><span class="line"><span class="tag">&lt;<span class="name">div</span> <span class="attr">class</span>=<span class="string">&quot;filter-container&quot;</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">el-input</span> <span class="attr">placeholder</span>=<span class="string">&quot;图书类别&quot;</span> <span class="attr">v-model</span>=<span class="string">&quot;pagination.type&quot;</span> <span class="attr">class</span>=<span class="string">&quot;filter-item&quot;</span>/&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">el-input</span> <span class="attr">placeholder</span>=<span class="string">&quot;图书名称&quot;</span> <span class="attr">v-model</span>=<span class="string">&quot;pagination.name&quot;</span> <span class="attr">class</span>=<span class="string">&quot;filter-item&quot;</span>/&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">el-input</span> <span class="attr">placeholder</span>=<span class="string">&quot;图书描述&quot;</span> <span class="attr">v-model</span>=<span class="string">&quot;pagination.description&quot;</span> <span class="attr">class</span>=<span class="string">&quot;filter-item&quot;</span>/&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">el-button</span> @<span class="attr">click</span>=<span class="string">&quot;getAll()&quot;</span> <span class="attr">class</span>=<span class="string">&quot;dalfBut&quot;</span>&gt;</span>查询<span class="tag">&lt;/<span class="name">el-button</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">el-button</span> <span class="attr">type</span>=<span class="string">&quot;primary&quot;</span> <span class="attr">class</span>=<span class="string">&quot;butT&quot;</span> @<span class="attr">click</span>=<span class="string">&quot;handleCreate()&quot;</span>&gt;</span>新建<span class="tag">&lt;/<span class="name">el-button</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;/<span class="name">div</span>&gt;</span></span><br></pre></td></tr></table></figure><p>将查询条件组织成url参数，添加到请求url地址中，这里可以借助其他类库快速开发，当前使用手工形式拼接，降低学习要求</p><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><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="title function_">getAll</span>(<span class="params"></span>) &#123;</span><br><span class="line">    <span class="comment">//1.获取查询条件,拼接查询条件</span></span><br><span class="line">    param = <span class="string">&quot;?name=&quot;</span>+<span class="variable language_">this</span>.<span class="property">pagination</span>.<span class="property">name</span>;</span><br><span class="line">    param += <span class="string">&quot;&amp;type=&quot;</span>+<span class="variable language_">this</span>.<span class="property">pagination</span>.<span class="property">type</span>;</span><br><span class="line">    param += <span class="string">&quot;&amp;description=&quot;</span>+<span class="variable language_">this</span>.<span class="property">pagination</span>.<span class="property">description</span>;</span><br><span class="line">    <span class="variable language_">console</span>.<span class="title function_">log</span>(<span class="string">&quot;-----------------&quot;</span>+ param);</span><br><span class="line">    axios.<span class="title function_">get</span>(<span class="string">&quot;/books/&quot;</span>+<span class="variable language_">this</span>.<span class="property">pagination</span>.<span class="property">currentPage</span>+<span class="string">&quot;/&quot;</span>+<span class="variable language_">this</span>.<span class="property">pagination</span>.<span class="property">pageSize</span>+param).<span class="title function_">then</span>(<span class="function">(<span class="params">res</span>) =&gt;</span> &#123;</span><br><span class="line">        <span class="variable language_">this</span>.<span class="property">dataList</span> = res.<span class="property">data</span>.<span class="property">data</span>.<span class="property">records</span>;</span><br><span class="line">    &#125;);</span><br><span class="line">&#125;,</span><br></pre></td></tr></table></figure><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></pre></td><td class="code"><pre><span class="line"><span class="meta">@GetMapping(&quot;&#123;currentPage&#125;/&#123;pageSize&#125;&quot;)</span></span><br><span class="line"><span class="keyword">public</span> R <span class="title function_">getAll</span><span class="params">(<span class="meta">@PathVariable</span> <span class="type">int</span> currentPage,<span class="meta">@PathVariable</span> <span class="type">int</span> pageSize,Book book)</span> &#123;</span><br><span class="line">    System.out.println(<span class="string">&quot;参数=====&gt;&quot;</span>+book);</span><br><span class="line">    IPage&lt;Book&gt; pageBook = bookService.getPage(currentPage,pageSize);</span><br><span class="line">    <span class="keyword">return</span> <span class="keyword">new</span> <span class="title class_">R</span>(<span class="literal">null</span> != pageBook ,pageBook);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><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></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">interface</span> <span class="title class_">IBookService</span> <span class="keyword">extends</span> <span class="title class_">IService</span>&lt;Book&gt; &#123;</span><br><span class="line">    IPage&lt;Book&gt; <span class="title function_">getPage</span><span class="params">(Integer currentPage,Integer pageSize,Book queryBook)</span>;</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></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_">BookServiceImpl2</span> <span class="keyword">extends</span> <span class="title class_">ServiceImpl</span>&lt;BookDao,Book&gt; <span class="keyword">implements</span> <span class="title class_">IBookService</span> &#123;</span><br><span class="line">    <span class="keyword">public</span> IPage&lt;Book&gt; <span class="title function_">getPage</span><span class="params">(Integer currentPage,Integer pageSize,Book queryBook)</span>&#123;</span><br><span class="line">        <span class="type">IPage</span> <span class="variable">page</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">Page</span>(currentPage,pageSize);</span><br><span class="line">        LambdaQueryWrapper&lt;Book&gt; lqw = <span class="keyword">new</span> <span class="title class_">LambdaQueryWrapper</span>&lt;Book&gt;();</span><br><span class="line">        lqw.like(Strings.isNotEmpty(queryBook.getName()),Book::getName,queryBook.getName());</span><br><span class="line">        lqw.like(Strings.isNotEmpty(queryBook.getType()),Book::getType,queryBook.getType());</span><br><span class="line">        lqw.like(Strings.isNotEmpty(queryBook.getDescription()),Book::getDescription,queryBook.getDescription());</span><br><span class="line">        <span class="keyword">return</span> bookDao.selectPage(page,lqw);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>页面回显数据</p></li></ul><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><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="title function_">getAll</span>(<span class="params"></span>) &#123;</span><br><span class="line">    <span class="comment">//1.获取查询条件,拼接查询条件</span></span><br><span class="line">    param = <span class="string">&quot;?name=&quot;</span>+<span class="variable language_">this</span>.<span class="property">pagination</span>.<span class="property">name</span>;</span><br><span class="line">    param += <span class="string">&quot;&amp;type=&quot;</span>+<span class="variable language_">this</span>.<span class="property">pagination</span>.<span class="property">type</span>;</span><br><span class="line">    param += <span class="string">&quot;&amp;description=&quot;</span>+<span class="variable language_">this</span>.<span class="property">pagination</span>.<span class="property">description</span>;</span><br><span class="line">    <span class="variable language_">console</span>.<span class="title function_">log</span>(<span class="string">&quot;-----------------&quot;</span>+ param);</span><br><span class="line">    axios.<span class="title function_">get</span>(<span class="string">&quot;/books/&quot;</span>+<span class="variable language_">this</span>.<span class="property">pagination</span>.<span class="property">currentPage</span>+<span class="string">&quot;/&quot;</span>+<span class="variable language_">this</span>.<span class="property">pagination</span>.<span class="property">pageSize</span>+param).<span class="title function_">then</span>(<span class="function">(<span class="params">res</span>) =&gt;</span> &#123;</span><br><span class="line">        <span class="variable language_">this</span>.<span class="property">pagination</span>.<span class="property">total</span> = res.<span class="property">data</span>.<span class="property">data</span>.<span class="property">total</span>;</span><br><span class="line">        <span class="variable language_">this</span>.<span class="property">pagination</span>.<span class="property">currentPage</span> = res.<span class="property">data</span>.<span class="property">data</span>.<span class="property">current</span>;</span><br><span class="line">        <span class="variable language_">this</span>.<span class="property">pagination</span>.<span class="property">pagesize</span> = res.<span class="property">data</span>.<span class="property">data</span>.<span class="property">size</span>;</span><br><span class="line">        <span class="variable language_">this</span>.<span class="property">dataList</span> = res.<span class="property">data</span>.<span class="property">data</span>.<span class="property">records</span>;</span><br><span class="line">    &#125;);</span><br><span class="line">&#125;,</span><br></pre></td></tr></table></figure><h2 id="基础篇总结"><a href="#基础篇总结" class="headerlink" title="基础篇总结"></a>基础篇总结</h2><p><strong>创建SpringBoot项目</strong></p><ul><li>SpringBoot项目拥有四种创建方式<ol><li>IDEA中直接勾选创建SpringBoot项目</li><li>在官网创建好后下载到本地</li><li>阿里提供的工程创建地址</li><li>手动创建工程并导入相关SpringBoot依赖</li></ol></li></ul><div class="note info no-icon flat"><p>前三种创建方式都需要联网进行，而第四种不需要联网，但前提是在maven仓库中有需要的依赖，即之前联网下载过该依赖</p></div><p><strong>SpringBoot简化操作</strong></p><ul><li><code>parent</code>：通过继承一个SpringBoot父类，来统一管理各种坐标的版本，避免不同坐标之间的版本冲突</li><li><code>starter</code>：SpringBoot提供的起步器，在该起步器中已经定义了各种需要的依赖。</li><li><code>引导类</code>：这是SpringBoot项目的入口，在该类运行后会直接创建一个Spring容器，并启动SpringBoot项目</li><li><code>内嵌Tomcat</code>：无需配置，只需要添加web的起步器，便可以在运行引导类时启动Tomcat，除Tomcat以外，SpringBoot还提供了两款内置服务器，可以通过排除<code>Tomcat</code>依赖，然后引入 <code>jetty</code>或<code>undertow</code>依赖，来启动内嵌服务器<ul><li>Tomcat(默认)：apache出品，粉丝多，应用面广，负载了若干较重的组件</li><li>jetty：更轻量级，负载性能远不及tomcat</li><li>undertow：负载性能勉强跑赢tomcat</li></ul></li></ul><p><strong>SpringBoot基础配置</strong></p><ul><li><code>属性配置</code>：SpringBoot提供了各种默认配置，这些默认配置可以通过配置文件来修改，比如数据库连接信息，端口号，日志级别等等，可以在<a href="https://docs.spring.io/spring-boot/appendix/application-properties/index.html">SpringBoot官方文档</a>中查看所有配置项</li><li><code>配置文件</code>：SpringBoot提供了三种格式的配置文件分别是：<ul><li>properties</li><li>yml</li><li>yaml</li></ul></li></ul><div class="note warning no-icon flat"><p><strong>其中yml和yaml格式完全相同，只是后缀不同，在配置SpringBoot时使用yml格式较多，因为该格式更简单直接</strong></p></div><ul><li><code>配置优先级</code>：即文件加载的优先级别，相同配置下优先加载高优先级的配置，优先级顺序：<code>application.properties &gt; application.yml &gt; application.yaml</code></li></ul><div class="note warning no-icon flat"><p><strong>如果配置多种格式的配置文件，那么相同的配置项会根据加载优先级进行覆盖，不同配置文件中的不同配置会全部保留</strong></p></div><ul><li><code>读取配置文件中的数据</code>：<ul><li><strong>读取少量数据</strong>:直接通过<code>@Value</code>配合<code>$&#123;一级属性名.二级属性名……&#125;</code>读取</li><li><strong>读取大量数据</strong>:SpringBoot提供了一个对象，可以通过自动装配将所有数据封装到该对象中<ul><li>通过<code>Autowired</code>注解来将数据装配到一个<code>Environment</code>类中</li><li>通过Environment接口提供的<code>getProperties(String)</code>方法获取数据，参数填写属性名</li></ul></li><li><strong>读取对象数据</strong>:上面两种读取方式的获取范围都不合适，SpringBoot提供了<code>@ConfigurationProperties</code>注解来快速读取,只需要在该注解的prefix属性中填写数据前缀即可</li></ul></li></ul><div class="note warning no-icon flat"><ul><li>自定义的属性可能IDEA不会自动提示</li><li>直接获取对象数据的前提是该类在Spring容器中</li></ul></div><ul><li><code>配置文件中的数据引用</code>:在书写配置文件中如果多个文件中都有相同的文件前缀，则可以将相同的前缀提取出来，下面再对其进行引用，如果属性中有特殊字符则需要使用双引号包裹起来。<h1 id="SpringBoot运维实用篇"><a href="#SpringBoot运维实用篇" class="headerlink" title="SpringBoot运维实用篇"></a>SpringBoot运维实用篇</h1></li></ul><p>实用篇是在基础篇的基础上，补全SpringBoot的<code>知识图谱</code>。比如在基础篇中学习了yaml的语法格式，但是具体写yaml文件的时候还有很多实用开发过程中的坑，这些在实用篇中都要进行学习。</p><p>实用篇共分为两块内容，分别是<code>运维实用篇</code>和<code>开发实用篇</code>。两个阶段的划分是为了更好的将同类知识点进行归类，帮助学习者找到知识之间的关联性，这样有助于知识的记忆存储转换，经过一系列的知识反复出现与强化练习，将临时记忆转换成永久性记忆。做课程嘛，不能仅以讲完为目标，要以学习者的学习收获为目标，这也是我这么多年教学秉承的基本理念。</p><p>运维使用pianzhong</p><ul><li>SpringBoot程序的打包与运行</li><li>配置高级</li><li>多环境开发</li><li>日志</li></ul><h2 id="SpringBoot程序的打包与运行"><a href="#SpringBoot程序的打包与运行" class="headerlink" title="SpringBoot程序的打包与运行"></a>SpringBoot程序的打包与运行</h2><p>我们现在都是在IDEA中对代码进行书写并运行，但在实际开发完成后，制作的程序并不是直接在自己电脑的IDEA中运行，而是运行在专用的服务器上，<br><div class="img-wrap"><div class="img-bg"><img class="img" src="https://raw.githubusercontent.com/silvan2077/markdown_pic/main/Picgo/image-20211201091341645.png"/></div></div><br><div class="img-wrap"><div class="img-bg"><img class="img" src="https://raw.githubusercontent.com/silvan2077/markdown_pic/main/Picgo/image-20211201091502040.png"/></div></div></p><p>​想要将我们准备好的程序运行在服务器上，这就需要将准备好的程序先组织成一个文件，然后把文件传输到服务器上。<strong>这个操作包含两个过程，一个是打包的过程，另一个是运行的过程。</strong>打包指的是将程序转换成一个可执行的文件，运行指的是不依赖开发环境执行打包产生的文件</p><p>​<div class="note info no-icon flat"><p>企业项目上线为了<code>保障环境适配性</code>会采用下面流程发布项目，这里不讨论此过程。</p><ol><li>开发部门使用<code>Git</code>、<code>SVN</code>等版本控制工具上传工程到版本服务器</li><li>服务器使用版本控制工具下载工程</li><li>服务器上使用Maven工具在当前真机环境下重新构建项目</li><li>启动服务器</li></ol></div></p><h3 id="程序打包"><a href="#程序打包" class="headerlink" title="程序打包"></a>程序打包</h3><p>SpringBoot程序是基于<code>Maven</code>创建的，在Maven中提供有打包的指令，叫做package。本操作可以在Idea环境下执行。</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">mvn <span class="keyword">package</span></span><br></pre></td></tr></table></figure><p>打包后会产生一个与工程名类似的jar文件，其名称由<code>模块名+版本号+.jar</code>组成。<br><div class="note warning no-icon flat"><p>注意：</p><ul><li>SpringBoot工程下执行<code>package</code>指令生成的jar包会将所有使用到的依赖库打包到jar文件中，所以可以直接运行</li><li>Maven自身的<code>package</code>指令生成的jar包，需要先将依赖库添加到classpath中，才能运行</li></ul></div></p><h3 id="程序运行"><a href="#程序运行" class="headerlink" title="程序运行"></a>程序运行</h3><div class="note warning no-icon flat"><p>运行jar包的前提是计算机安装了java的JDK环境，因为程序执行的是java指令。</p></div><p>程序包打好以后，就可以直接执行了。在程序包所在路径下，执行指令。</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">java -jar 工程包名.jar</span><br></pre></td></tr></table></figure><p>执行程序打包指令后，程序正常运行，与在Idea下执行程序没有区别。</p><p>这段配置在SpringBoot工程的<code>pom.xml</code>文件中，它在普通Maven项目的基础上扩展了功能，所以在SpringBoot环境下打包的项目可以直接运行。<br><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></pre></td><td class="code"><pre><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></pre></td></tr></table></figure></p><h3 id="打包问题"><a href="#打包问题" class="headerlink" title="打包问题"></a>打包问题</h3><div class="note info no-icon flat"><p>前面提到SpringBoot环境下打的包和普通Maven打的包不一样，下面来具体分析一下哪里不同。</p></div><ul><li><p>直接运行Maven下载的jar包，会出现以下错误</p><div class="img-wrap"><div class="img-bg"><img class="img" src="https://raw.githubusercontent.com/silvan2077/markdown_pic/main/Picgo/image-20211201094223991.png"/></div></div></li><li><p>之前在项目中我们也经常导入各种jar包，如Mysql的驱动jar包，使用前面学习的指令<code>java -jar xxx.jar</code>运行，也会出现上面的错误，但使用SpringBoot打包的jar包，就能正常运行。</p></li></ul><ul><li>前面就提到了，这里的配置是SpringBoot打包的关键<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></pre></td><td class="code"><pre><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></pre></td></tr></table></figure></li></ul><p>开启这段配置和注释掉这段配置分别执行两次打包，然后观察两次打包后的程序包的差别，共有3处比较明显的特征</p><ul><li>打包后文件的大小不同</li><li>打包后所包含的内容不同</li><li>打包程序中个别文件内容不同</li></ul><ol><li>文件大小不同。带有配置时打包生成的程序包大小如下：<div class="img-wrap"><div class="img-bg"><img class="img" src="https://raw.githubusercontent.com/silvan2077/markdown_pic/main/Picgo/image-20211201095610270.png"/></div></div></li></ol><p>​这里带有配置的程序包体积比不带配置的大了30倍，接下来看看里面内容的差异。<br><div class="tabs" id="打包问题1"><ul class="nav-tabs"><li class="tab active"><button type="button" data-href="#打包问题1-1">带配置</button></li><li class="tab"><button type="button" data-href="#打包问题1-2">不带配置</button></li></ul><div class="tab-contents"><div class="tab-item-content active" id="打包问题1-1"><div class="img-wrap"><div class="img-bg"><img class="img" src="https://raw.githubusercontent.com/silvan2077/markdown_pic/main/Picgo/image-20211201101541267.png"/></div></div><button type="button" class="tab-to-top" aria-label="scroll to top"><i class="fas fa-arrow-up"></i></button></div><div class="tab-item-content" id="打包问题1-2"><div class="img-wrap"><div class="img-bg"><img class="img" src="https://raw.githubusercontent.com/silvan2077/markdown_pic/main/Picgo/image-20211201101652868.png"/></div></div><button type="button" class="tab-to-top" aria-label="scroll to top"><i class="fas fa-arrow-up"></i></button></div></div></div></p><p>可以看到两个文件内容也不一样，打开带配置的文件中的BOOT-INF目录下的classes目录，可以看到这个目录下的内容和不带配置的程序包的内容一模一样，这说明带配置的程序包中包含了不带配置的程序包，除此之外还多了许多东西。</p><p>回到BOOT-INF目录下，打开lib目录，发现里面有着各种jar包</p><div class="img-wrap"><div class="img-bg"><img class="img" src="https://raw.githubusercontent.com/silvan2077/markdown_pic/main/Picgo/image-20211201102025791.png"/></div></div><p>这些jar文件都是制作这个工程时导入的坐标对应的文件。这就是SpringBoot生成的JAR包能够独立运行的原因，这样可以不依赖程序包外部的任何资源来独立运行当前程序。</p><p>除此以外，在带配置的程序包的最外层目录包含一个<code>org目录</code>，进入此目录，目录名是<code>org\springframework\boot\loader</code>，在里面可以找到一个<code>JarLauncher.class</code>文件，先记得这个文件。而这套目录名明显是一个Spring的目录名，为什么要把Spring框架的东西打包到这个程序包中呢？不清楚。</p><p>​        回到两个程序包的最外层目录，查看名称相同的文件夹<code>META-INF</code>下都有一个叫做<code>MANIFEST.MF</code>的文件，但是大小不同，打开文件，比较内容区别</p><div class="tabs" id="打包问题2"><ul class="nav-tabs"><li class="tab active"><button type="button" data-href="#打包问题2-1">小容量文件的MANIFEST.MF</button></li><li class="tab"><button type="button" data-href="#打包问题2-2">大容量文件的MANIFEST.MF</button></li></ul><div class="tab-contents"><div class="tab-item-content active" id="打包问题2-1"><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><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">Manifest-Version: <span class="number">1.0</span></span><br><span class="line">Implementation-Title: springboot_08_ssmp</span><br><span class="line">Implementation-Version: <span class="number">0.0</span><span class="number">.1</span>-SNAPSHOT</span><br><span class="line">Build-Jdk-Spec: <span class="number">1.8</span></span><br><span class="line">Created-By: Maven Jar Plugin <span class="number">3.2</span><span class="number">.0</span></span><br></pre></td></tr></table></figure><button type="button" class="tab-to-top" aria-label="scroll to top"><i class="fas fa-arrow-up"></i></button></div><div class="tab-item-content" id="打包问题2-2"><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><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">Manifest-Version: <span class="number">1.0</span></span><br><span class="line">Spring-Boot-Classpath-Index: BOOT-INF/classpath.idx</span><br><span class="line">Implementation-Title: springboot_08_ssmp</span><br><span class="line">Implementation-Version: <span class="number">0.0</span><span class="number">.1</span>-SNAPSHOT</span><br><span class="line">Spring-Boot-Layers-Index: BOOT-INF/layers.idx</span><br><span class="line">Start-Class: com.itheima.SSMPApplication</span><br><span class="line">Spring-Boot-Classes: BOOT-INF/classes/</span><br><span class="line">Spring-Boot-Lib: BOOT-INF/lib/</span><br><span class="line">Build-Jdk-Spec: <span class="number">1.8</span></span><br><span class="line">Spring-Boot-Version: <span class="number">2.5</span><span class="number">.4</span></span><br><span class="line">Created-By: Maven Jar Plugin <span class="number">3.2</span><span class="number">.0</span></span><br><span class="line">Main-Class: org.springframework.boot.loader.JarLauncher</span><br></pre></td></tr></table></figure><button type="button" class="tab-to-top" aria-label="scroll to top"><i class="fas fa-arrow-up"></i></button></div></div></div><p>​        大文件中明显比小文件中多了几行信息，其中最后一行信息是<code>Main-Class: org.springframework.boot.loader.JarLauncher</code>。这句话的意思是如果使用java -jar执行此程序包，将执行Main-Class属性配置的类，而这个类恰巧就是前面看到的那个文件。原来SpringBoot打包程序中出现Spring框架的东西是为这里服务的。而这个<code>org.springframework.boot.loader.JarLauncher</code>类内部要查找<code>Start-Class</code>属性中配置的类，并执行对应的类。这个属性在当前配置中也存在，对应的就是程序的引导类类名。</p><div class="note danger no-icon flat"><p>SpringBoot制作的jar包能够直接运行的原因：</p><ol><li>SpringBoot程序添加配置后会打出一个特殊的包，包含Spring框架部分功能，原始工程内容，原始工程依赖的jar包</li><li>首先读取<code>MANIFEST.MF</code>文件中的<code>Main-Class</code>属性，用来标记执行<code>java -jar</code>命令后运行的类</li><li><code>JarLauncher</code>类执行时会找到<code>Start-Class</code>属性，也就是启动类类名</li><li>运行启动类时会运行当前工程的内容</li><li>运行当前工程时会使用依赖的jar包，从lib目录中查找</li></ol></div><div class="img-wrap"><div class="img-bg"><img class="img" src="https://raw.githubusercontent.com/silvan2077/markdown_pic/main/Picgo/image-20211201094223991.png"/></div></div><p>之前的报错就是由于打包时没有使用那段配置，打了一个普通的jar包，在<code>MANIFEST.MF</code>文件中也就没有了<code>Main-Class</code>对应的属性了，所以运行时提示找不到主清单属性。</p><ul><li>这一节对实际编程意义不大，但能更好的理解SpringBoot整体的实现过程。</li></ul><h3 id="命令行启动常见问题及解决方案"><a href="#命令行启动常见问题及解决方案" class="headerlink" title="命令行启动常见问题及解决方案"></a>命令行启动常见问题及解决方案</h3><p>在DOS环境下启动SpringBoot工程时，可能会遇到端口占用的问题。下面这组命令可以用来解决这个问题。</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></pre></td><td class="code"><pre><span class="line"><span class="comment"># 查询端口</span></span><br><span class="line">netstat -ano</span><br><span class="line"><span class="comment"># 查询指定端口</span></span><br><span class="line">netstat -ano |findstr <span class="string">&quot;端口号&quot;</span></span><br><span class="line"><span class="comment"># 根据进程PID查询进程名称</span></span><br><span class="line">tasklist |findstr <span class="string">&quot;进程PID号&quot;</span></span><br><span class="line"><span class="comment"># 根据PID杀死任务</span></span><br><span class="line">taskkill /F /PID <span class="string">&quot;进程PID号&quot;</span></span><br><span class="line"><span class="comment"># 根据进程名称杀死任务</span></span><br><span class="line">taskkill -f -t -im <span class="string">&quot;进程名称&quot;</span></span><br></pre></td></tr></table></figure><div class="note info no-icon flat"><p>关于打包与运行程序其实还有一系列的配置和参数，这里只是最简单的默认打包运行方式，之后再继续深入学习。</p></div><h2 id="配置-高级"><a href="#配置-高级" class="headerlink" title="配置(高级)"></a>配置(高级)</h2><ul><li>在基础篇中简单学习了配置文件的分类以及如何读取配置文件的内容等基本使用，在实用篇则重点学习配置的使用。</li></ul><h3 id="临时属性设置"><a href="#临时属性设置" class="headerlink" title="临时属性设置"></a>临时属性设置</h3><p>假设此时我们的程序包打好了，已经准备发布了。但是程序包打好以后，里面的配置就固定了，比如配置了服务器的端口是8080。如果在启动项目时，发现当前服务器上已经有应用启动起来并且占用了8080端口，这个时候难道要重新把打包好的程序修改一下吗？比如把打包好的程序启动端口改成80。</p><div class="img-wrap"><div class="img-bg"><img class="img" src="https://raw.githubusercontent.com/silvan2077/markdown_pic/main/Picgo/image-20211206095113771.png"/></div></div><div class="img-wrap"><div class="img-bg"><img class="img" src="https://raw.githubusercontent.com/silvan2077/markdown_pic/main/Picgo/image-20211206095101581.png"/></div></div><ul><li>SpringBoot为此提供了灵活的配置方式，我们可以在启动时使用临时属性的方式快速修改某些配置，方法非常简单，只需要在启动时添加对应参数即可<figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">java –jar springboot.jar –-server.port=80</span><br></pre></td></tr></table></figure></li><li><p>在命令输入完毕后，空一格，然后输入两个-号。下面按照属性名=属性值的形式添加对应参数就可以了。这里的格式不是yaml中的书写格式，当属性存在多级名称时，中间使用点分隔，和properties文件中的属性格式完全相同。</p></li><li><p>如果要修改的属性不止一个，可以按照上述格式继续写，属性与属性之间使用空格分隔。</p></li></ul><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">java –jar springboot.jar –-server.port=80 --logging.level.root=debug</span><br></pre></td></tr></table></figure><h4 id="属性加载优先级"><a href="#属性加载优先级" class="headerlink" title="属性加载优先级"></a>属性加载优先级</h4><p>现在我们可以通过<code>配置文件</code>和<code>临时属性</code>来控制程序配置。并且临时属性的加载优先级要高于配置文件。那是否还有其他的配置方式呢？其实是有的，而且还不少，打开官方文档中对应的内容，就可以查看配置读取的优先顺序。<br>地址：<a href="https://docs.spring.io/spring-boot/docs/current/reference/html/spring-boot-features.html">https://docs.spring.io/spring-boot/docs/current/reference/html/spring-boot-features.html#boot-features-external-config</a></p><div class="img-wrap"><div class="img-bg"><img class="img" src="https://raw.githubusercontent.com/silvan2077/markdown_pic/main/Picgo/image-20211206100859236.png"/></div></div><ul><li>SpringBoot提供了14种配置方式，第三条指的是配置文件，第十一条说的就是使用命令行参数来临时配置。</li><li>这14条配置的顺序就是SpringBoot配置的加载顺序，越在下面优先级越高。</li><li>不需要死记硬背，只需要在配置的优先级出错时对照该表排查即可，例如在基础篇提到的user属性值系统属性的加载优先级高于配置文件的加载优先级，那么在配置文件配置的user属性值就会失效。</li></ul><h4 id="开发环境中使用临时属性"><a href="#开发环境中使用临时属性" class="headerlink" title="开发环境中使用临时属性"></a>开发环境中使用临时属性</h4><ul><li>前面使用临时属性是通过命令行来指定，但是上线的时候需要确保通过命令行输入的临时属性必须是正确的，所以这些属性配置值必须在开发环境中测试好（不通过直接修改配置文件来测试因为要用与上线相同的启动方式（命令行参数或环境变量）进行验证，确保这些动态配置的参数名、值、格式都正确，而不是仅仅依赖修改配置文件。）</li></ul><p>打开SpringBoot引导类的运行界面，在里面找到配置项。其中Program arguments对应的位置就是添加临时属性的，可以加几个试试效果。</p><div class="img-wrap"><div class="img-bg"><img class="img" src="https://raw.githubusercontent.com/silvan2077/markdown_pic/main/Picgo/image-20211206101947622.png"/></div></div><p>其实这里就是我们运行main方法时main方法的<code>args</code>参数</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="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">&#125;</span><br></pre></td></tr></table></figure><p>原来通过这个args就可以获取到参数。这个参数在程序中会被传递给<code>run</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></pre></td><td class="code"><pre><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(SSMPApplication.class,args);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><ul><li>args参数通过以上方法传递到程序中，言外之意，如果我们不传递这个参数，那么外部传递临时属性的入口就断开了，下面的调用方式，就无法使用外部临时属性了<figure class="highlight java"><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="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(SSMPApplication.class);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></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></pre></td><td class="code"><pre><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">    String[] arg = <span class="keyword">new</span> <span class="title class_">String</span>[<span class="number">1</span>];</span><br><span class="line">    arg[<span class="number">0</span>] = <span class="string">&quot;--server.port=8082&quot;</span>;</span><br><span class="line">    SpringApplication.run(SSMPApplication.class, arg);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p><strong>思考</strong><br>现在可以通过临时属性在启动项目前临时更改配置了，但是新的问题又出来了。如果需要配置多个临时属性，假如有需求要一下配置20个，那么配置起来就非常麻烦了，这该如何解决？</p><h3 id="配置文件分类-1"><a href="#配置文件分类-1" class="headerlink" title="配置文件分类"></a>配置文件分类</h3><ul><li><p>SpringBoot提供了<code>配置文件</code>和<code>临时属性</code>的方式来对程序进行配置。这里再详细说说配置文件。我们前面使用的配置文件其实只是SpringBoot提供的4级配置文件中的其中一个级别。4个级别分别是：</p><ul><li>类路径下配置文件（一直使用的是这个，也就是resources目录中的application.yml文件）</li><li>类路径下config目录下配置文件</li><li>程序包所在目录中配置文件</li><li>程序包所在目录中config目录下配置文件</li></ul></li><li><p>分为四种但说到底都是配置文件，功能都是配置属性，他们之前的差异就是文件存放的位置导致的优先级不同，这四个文件的加载优先顺序为：</p></li></ul><ol><li>file ：config/application.yml <strong>【最高】</strong></li><li>file ：application.yml</li><li>classpath：config/application.yml</li><li>classpath：application.yml（一直使用的就是这个）  <strong>【最低】</strong></li></ol><ul><li>设计这么多种配置文件的原因：<ul><li>场景A：作为一个开发者，做程序的时候为了方便自己写代码，配置的数据库肯定是自己本机的，咱们使用4这个级别，也就是之前一直用的application.yml。</li><li>场景B：现在项目开发到了一个阶段，要联调测试了，连接的数据库是测试服务器的数据库，肯定要换一组配置吧。你可以选择把你之前的文件中的内容都改了，目前还不麻烦。</li><li>场景C：测试完了，一切OK。可以继续往下写代码了，但这时就会发现原先的配置文件都被更改了，需要重新改回来，改来改去就非常繁琐。</li></ul></li></ul><p>​而使用上面3这个级别的配置文件就可以快速解决该问题。两个配置文件共存，因为config目录中的配置加载优先级更高，所以配置项如果和级别4里面的内容相同就覆盖了。</p><p><strong>总结</strong></p><ol><li>配置文件分为4种<ul><li>工程路径config目录中配置文件：服务于运维经理整体调控</li><li>工程路径配置文件：服务于运维人员配置涉密线上环境</li><li>项目类路径config目录中配置文件：服务于项目经理整体调控</li><li>项目类路径配置文件：服务于开发人员本机开发与测试</li></ul></li><li>多层级配置文件间的属性采用<code>叠加并覆盖</code>的形式作用于程序</li></ol><h3 id="自定义配置文件"><a href="#自定义配置文件" class="headerlink" title="自定义配置文件"></a>自定义配置文件</h3><p>之前使用的配置文件都是application.yml，其实可以定义多个配置文件，使用其他名称的配置文件。比如2020年4月1日搞活动，走了一组配置，2020年5月1日活动取消，恢复原始配置，这个时候只需要重新更换一下配置文件就可以了。但是你总不能在原始配置文件上修改吧，不然搞完活动以后，活动的配置就留不下来了，不利于维护。</p><p>​自定义配置文件方式有如下两种：</p><p><strong>方式一：使用临时属性设置配置文件名，注意仅仅是名称，不要带扩展名</strong></p><div class="img-wrap"><div class="img-bg"><img class="img" src="https://raw.githubusercontent.com/silvan2077/markdown_pic/main/Picgo/image-20211206105548238.png"/></div></div><p><strong>方式二：使用临时属性设置配置文件路径，这个是全路径名</strong></p><div class="img-wrap"><div class="img-bg"><img class="img" src="https://raw.githubusercontent.com/silvan2077/markdown_pic/main/Picgo/image-20211206105716450.png"/></div></div><ul><li>也可以设置加载多个配置文件<div class="img-wrap"><div class="img-bg"><img class="img" src="https://raw.githubusercontent.com/silvan2077/markdown_pic/main/Picgo/image-20211206105750285.png"/></div></div></li></ul><div class="note warning no-icon flat"><p>方式一使用的属性是<code>spring.config.name</code>,方式二使用的是<code>spring.config.location</code>，这个一定要区别清楚。</p></div><font color="#f0f"><b>温馨提示</b></font><div class="note danger no-icon flat"><p>现在研究的都是SpringBoot单体项目，就是单服务器版本。其实企业开发现在更多的是使用基于<code>SpringCloud</code>技术的多服务器项目。这种配置方式和现在学习的完全不一样，所有的服务器将不再设置自己的配置文件，而是<strong>通过配置中心获取配置，动态加载配置信息</strong>。为什么这样做？方便集中管理，之后再详细学习。</p></div><h2 id="多环境开发"><a href="#多环境开发" class="headerlink" title="多环境开发"></a>多环境开发</h2><p>多环境：我们书写的程序大多需要放到服务器上运行，而每个电脑环境都不一样，这就是<code>多环境</code>。常见的多环境开发主要兼顾3种环境设置，<code>开发环境</code>——自己用的，<code>测试环境</code>——自己公司用的，<code>生产环境</code>——甲方用的。因为这是绝对不同的三台电脑，所以环境肯定有所不同，比如连接的数据库不一样，设置的访问端口不一样等等。</p><div class="img-wrap"><div class="img-bg"><img class="img" src="https://raw.githubusercontent.com/silvan2077/markdown_pic/main/Picgo/image-20211206110958819.png"/></div></div><h3 id="多环境开发（yaml单文件版）"><a href="#多环境开发（yaml单文件版）" class="headerlink" title="多环境开发（yaml单文件版）"></a>多环境开发（yaml单文件版）</h3><ul><li><code>多环境开发</code>就是针对不同的环境设置不同的配置属性。比如自己开发时，配置的端口如下：</li></ul><figure class="highlight yaml"><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="attr">server:</span></span><br><span class="line">  <span class="attr">port:</span> <span class="number">80</span></span><br></pre></td></tr></table></figure><ul><li>​在yaml中可以使用三个减号来分隔开不同环境的配置<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></pre></td><td class="code"><pre><span class="line"><span class="attr">server:</span></span><br><span class="line">  <span class="attr">port:</span> <span class="number">80</span></span><br><span class="line"><span class="meta">---</span></span><br><span class="line"><span class="attr">server:</span></span><br><span class="line">  <span class="attr">port:</span> <span class="number">81</span></span><br></pre></td></tr></table></figure></li><li>通过为不同环境设置不同的profile名称，就可以在启动时指定使用哪一个环境的配置。<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></pre></td><td class="code"><pre><span class="line"><span class="attr">spring:</span></span><br><span class="line"><span class="attr">profiles:</span> <span class="string">pro</span></span><br><span class="line"><span class="attr">server:</span></span><br><span class="line"><span class="attr">port:</span> <span class="number">80</span></span><br><span class="line"><span class="meta">---</span></span><br><span class="line"><span class="attr">spring:</span></span><br><span class="line"><span class="attr">profiles:</span> <span class="string">dev</span></span><br><span class="line"><span class="attr">server:</span></span><br><span class="line"><span class="attr">port:</span> <span class="number">81</span></span><br></pre></td></tr></table></figure></li><li><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></pre></td><td class="code"><pre><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">pro</span><span class="comment"># 默认启动pro</span></span><br><span class="line"><span class="meta">---</span></span><br><span class="line"><span class="attr">spring:</span></span><br><span class="line"><span class="attr">profiles:</span> <span class="string">pro</span></span><br><span class="line"><span class="attr">server:</span></span><br><span class="line"><span class="attr">port:</span> <span class="number">80</span></span><br><span class="line"><span class="meta">---</span></span><br><span class="line"><span class="attr">spring:</span></span><br><span class="line"><span class="attr">profiles:</span> <span class="string">dev</span></span><br><span class="line"><span class="attr">server:</span></span><br><span class="line"><span class="attr">port:</span> <span class="number">81</span></span><br></pre></td></tr></table></figure></li><li><p>可以区分各种环境</p></li></ul><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></pre></td><td class="code"><pre><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">pro</span><span class="comment"># 启动pro</span></span><br><span class="line"><span class="meta">---</span></span><br><span class="line"><span class="attr">spring:</span></span><br><span class="line"><span class="attr">profiles:</span> <span class="string">pro</span></span><br><span class="line"><span class="attr">server:</span></span><br><span class="line"><span class="attr">port:</span> <span class="number">80</span></span><br><span class="line"><span class="meta">---</span></span><br><span class="line"><span class="attr">spring:</span></span><br><span class="line"><span class="attr">profiles:</span> <span class="string">dev</span></span><br><span class="line"><span class="attr">server:</span></span><br><span class="line"><span class="attr">port:</span> <span class="number">81</span></span><br><span class="line"><span class="meta">---</span></span><br><span class="line"><span class="attr">spring:</span></span><br><span class="line"><span class="attr">profiles:</span> <span class="string">test</span></span><br><span class="line"><span class="attr">server:</span></span><br><span class="line"><span class="attr">port:</span> <span class="number">82</span></span><br></pre></td></tr></table></figure><div class="note info no-icon flat"><p><strong>总结</strong></p><ol><li>多环境开发需要设置若干种常用环境，例如开发、生产、测试环境</li><li>yaml格式中设置多环境使用—-区分环境设置边界</li><li>每种环境的区别在于加载的配置属性不同</li><li>启用某种环境时需要指定启动时使用该环境</li></ol></div><h3 id="多环境开发（yaml多文件版）"><a href="#多环境开发（yaml多文件版）" class="headerlink" title="多环境开发（yaml多文件版）"></a>多环境开发（yaml多文件版）</h3><ul><li>前面单文件开发是将所有配置都放在一个配置文件中，使用<code>---</code>来分隔，但当配置文件中有很多项配置时，明显将不同环境的配置项写在一个文件中是不合理的。于是就有了将一个配置文件拆分成多个配置文件的想法。拆分后，每个配置文件中写自己的配置，主配置文件中写清楚用哪一个配置文件就好了。</li></ul><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></pre></td><td class="code"><pre><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">pro</span><span class="comment"># 启动pro</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></pre></td><td class="code"><pre><span class="line"><span class="attr">server:</span></span><br><span class="line"><span class="attr">port:</span> <span class="number">80</span></span><br></pre></td></tr></table></figure><p>环境配置文件因为每一个都是配置自己的项，所以连名字都不用写在配置文件里面了，直接使用文件名区分即可。</p><div class="tabs" id="配置文件多文件版"><ul class="nav-tabs"><li class="tab active"><button type="button" data-href="#配置文件多文件版-1">application-pro.yaml</button></li><li class="tab"><button type="button" data-href="#配置文件多文件版-2">application-dev.yaml</button></li></ul><div class="tab-contents"><div class="tab-item-content active" id="配置文件多文件版-1"><figure class="highlight yaml"><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="attr">server:</span></span><br><span class="line"><span class="attr">port:</span> <span class="number">80</span></span><br></pre></td></tr></table></figure><button type="button" class="tab-to-top" aria-label="scroll to top"><i class="fas fa-arrow-up"></i></button></div><div class="tab-item-content" id="配置文件多文件版-2"><figure class="highlight yaml"><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="attr">server:</span></span><br><span class="line"><span class="attr">port:</span> <span class="number">81</span></span><br></pre></td></tr></table></figure><button type="button" class="tab-to-top" aria-label="scroll to top"><i class="fas fa-arrow-up"></i></button></div></div></div><div class="note info no-icon flat"><p>总结：</p><ul><li>配置文件的命名规则为：application-环境名.yml。</li><li>主配置文件中设置公共配置（全局）</li><li>环境分类配置文件中常用于设置冲突属性（局部）</li></ul></div><h3 id="多环境开发（properties多文件版）"><a href="#多环境开发（properties多文件版）" class="headerlink" title="多环境开发（properties多文件版）"></a>多环境开发（properties多文件版）</h3><ul><li>SpringBoot最早期提供的配置文件格式是properties格式的，properties不支持单文件，这种格式的多环境配置作为了解即可</li></ul><p><strong>主配置文件</strong><br><div class="tabs" id="主配置文件1"><ul class="nav-tabs"><li class="tab active"><button type="button" data-href="#主配置文件1-1">主配置文件</button></li><li class="tab"><button type="button" data-href="#主配置文件1-2">环境配置文件</button></li></ul><div class="tab-contents"><div class="tab-item-content active" id="主配置文件1-1"><figure class="highlight properties"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="attr">spring.profiles.active</span>=<span class="string">pro</span></span><br></pre></td></tr></table></figure><button type="button" class="tab-to-top" aria-label="scroll to top"><i class="fas fa-arrow-up"></i></button></div><div class="tab-item-content" id="主配置文件1-2"><p><strong>application-pro.properties</strong></p><figure class="highlight properties"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="attr">server.port</span>=<span class="string">80</span></span><br></pre></td></tr></table></figure><p><strong>application-dev.properties</strong></p><figure class="highlight properties"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="attr">server.port</span>=<span class="string">81</span></span><br></pre></td></tr></table></figure><button type="button" class="tab-to-top" aria-label="scroll to top"><i class="fas fa-arrow-up"></i></button></div></div></div></p><ul><li><strong>文件的命名规则为：application-环境名.properties</strong>。</li></ul><h3 id="多环境开发配置文件书写技巧"><a href="#多环境开发配置文件书写技巧" class="headerlink" title="多环境开发配置文件书写技巧"></a>多环境开发配置文件书写技巧</h3><p>作为程序员在搞配置的时候往往处于一种分久必合合久必分的局面。开始先写一起，后来为了方便维护就拆分。对于多环境开发也是如此，下面给大家说一下如何基于多环境开发做配置独立管理，务必掌握。</p><p><strong>准备工作</strong></p><p>将所有的配置根据功能对配置文件中的信息进行拆分，并制作成独立的配置文件，命名规则如下</p><ul><li>application-devDB.yml</li><li>application-devRedis.yml</li><li>application-devMVC.yml</li></ul><p><strong>使用</strong><br>使用include属性在激活指定环境的情况下，同时对多个环境进行加载使其生效，多个环境间使用逗号分隔，这样更加清晰直接，除了加载dev配置还加载了哪些环境的配置文件。</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="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><br><span class="line">        <span class="attr">include:</span> <span class="string">devDB,devRedis,devMVC</span></span><br></pre></td></tr></table></figure><div class="note warning no-icon flat"><p><strong>​当主环境dev与其他环境有相同属性时，主环境属性生效；其他环境中有相同属性时，最后加载的环境属性生效</strong></p></div><p><strong>改良</strong><br>但是上面的设置也有一个问题，比如我要切换dev环境为pro时，include也要修改。因为include属性只能使用一次，这就比较麻烦了。SpringBoot从2.4版开始使用group属性替代include属性，降低了配置书写量。简单说就是我先写好，你爱用哪个用哪个。</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></pre></td><td class="code"><pre><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><br><span class="line">        <span class="attr">group:</span></span><br><span class="line">        <span class="attr">&quot;dev&quot;:</span> <span class="string">devDB,devRedis,devMVC</span></span><br><span class="line">      <span class="attr">&quot;pro&quot;:</span> <span class="string">proDB,proRedis,proMVC</span></span><br><span class="line">      <span class="attr">&quot;test&quot;:</span> <span class="string">testDB,testRedis,testMVC</span></span><br></pre></td></tr></table></figure><h3 id="多环境开发控制"><a href="#多环境开发控制" class="headerlink" title="多环境开发控制"></a>多环境开发控制</h3><ul><li><p>多环境开发在配置方面的内容基本就这些，最后一个冲突就是maven和SpringBoot同时设置多环境的话怎么搞。处理这个冲突问题前，需要先明确一个关系，究竟谁在多环境开发中其主导地位。也就是说如果现在都设置了多环境，谁的应该是保留下来的，另一个应该遵从相同的设置。</p></li><li><p>maven是做什么的？</p><ul><li>项目构建管理的，最终生成代码包的，</li></ul></li><li>SpringBoot是干什么的？简化开发的。</li></ul><div class="note info no-icon flat"><ul><li>简化说明SpringBoot并不起主导作用，最终还是靠Maven管理整个工程，所以最终肯定是保留Maven的配置。大体思路：<ul><li>在Maven中设置好具体用哪些环境</li><li>在SpringBoot中读取Maven设置的环境</li></ul></li></ul></div><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><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><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="tag">&lt;<span class="name">profiles</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">profile</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">id</span>&gt;</span>env_dev<span class="tag">&lt;/<span class="name">id</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 class="tag">&lt;<span class="name">profile.active</span>&gt;</span>dev<span class="tag">&lt;/<span class="name">profile.active</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 class="tag">&lt;<span class="name">activation</span>&gt;</span></span><br><span class="line">            <span class="tag">&lt;<span class="name">activeByDefault</span>&gt;</span>true<span class="tag">&lt;/<span class="name">activeByDefault</span>&gt;</span><span class="comment">&lt;!--默认启动环境--&gt;</span></span><br><span class="line">        <span class="tag">&lt;/<span class="name">activation</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;/<span class="name">profile</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">profile</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">id</span>&gt;</span>env_pro<span class="tag">&lt;/<span class="name">id</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 class="tag">&lt;<span class="name">profile.active</span>&gt;</span>pro<span class="tag">&lt;/<span class="name">profile.active</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 class="tag">&lt;/<span class="name">profile</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;/<span class="name">profiles</span>&gt;</span></span><br></pre></td></tr></table></figure><p><strong>SpringBoot中读取maven设置值</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></pre></td><td class="code"><pre><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">@profile.active@</span></span><br></pre></td></tr></table></figure><p>此处的<code>@属性名@</code>就是读取maven中配置的属性值的语法格式。</p><p><strong>总结</strong></p><ol><li>当Maven与SpringBoot同时对多环境进行控制时，以Mavn为主，SpringBoot使用@..@占位符读取Maven对应的配置属性值</li><li>基于SpringBoot读取Maven配置属性的前提下，如果在Idea下测试工程时pom.xml每次更新需要手动compile方可生效</li></ol><h2 id="日志"><a href="#日志" class="headerlink" title="日志"></a>日志</h2><ul><li>日志都不陌生，简单介绍一下。日志其实就是记录程序日常运行的信息，主要作用如下：</li></ul><ul><li>编程期调试代码</li><li>运营期记录信息</li><li>记录日常运营重要信息（峰值流量、平均响应时长……）</li><li>记录应用报错信息（错误堆栈）</li><li>记录运维过程数据（扩容、宕机、报警……）</li></ul><h3 id="代码中使用日志工具记录日志"><a href="#代码中使用日志工具记录日志" class="headerlink" title="代码中使用日志工具记录日志"></a>代码中使用日志工具记录日志</h3><p>日志的使用格式非常固定，直接上操作步骤：</p><p><strong>步骤一</strong>：添加日志记录操作<br>直接在类中定义日志对象并调用方法<br><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><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="meta">@RestController</span></span><br><span class="line"><span class="meta">@RequestMapping(&quot;/books&quot;)</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">BookController</span> <span class="keyword">extends</span> <span class="title class_">BaseClass</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">Logger</span> <span class="variable">log</span> <span class="operator">=</span> LoggerFactory.getLogger(BookController.class);</span><br><span class="line">    <span class="meta">@GetMapping</span></span><br><span class="line">    <span class="keyword">public</span> String <span class="title function_">getById</span><span class="params">()</span>&#123;</span><br><span class="line">        log.debug(<span class="string">&quot;debug...&quot;</span>);</span><br><span class="line">        log.info(<span class="string">&quot;info...&quot;</span>);</span><br><span class="line">        log.warn(<span class="string">&quot;warn...&quot;</span>);</span><br><span class="line">        log.error(<span class="string">&quot;error...&quot;</span>);</span><br><span class="line">        <span class="keyword">return</span> <span class="string">&quot;springboot is running...2&quot;</span>;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><br>上述代码中log对象就是用来记录日志的对象，下面的log.debug，log.info这些操作就是写日志的API了。</p><p><strong>步骤二</strong>：设置日志输出级别</p><p>日志设置好以后可以根据设置选择哪些参与记录。这里是根据日志的级别来设置的。日志的级别分为6种，分别是：</p><ul><li>TRACE：运行堆栈信息，使用率低</li><li>DEBUG：程序员调试代码使用</li><li>INFO：记录运维过程数据</li><li>WARN：记录运维过程报警数据</li><li>ERROR：记录错误堆栈信息</li><li>FATAL：灾难信息，合并计入ERROR</li></ul><div class="note info no-icon flat"><p>​一般情况下，开发时候使用<code>DEBUG</code>，上线后使用<code>INFO</code>，运维信息记录使用<code>WARN</code>即可。</p></div><p>下面就设置一下日志级别：</p><figure class="highlight yaml"><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"># 开启debug模式，输出调试信息，常用于检查系统运行状况</span></span><br><span class="line"><span class="attr">debug:</span> <span class="literal">true</span></span><br></pre></td></tr></table></figure><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></pre></td><td class="code"><pre><span class="line"><span class="comment"># 开启debug模式，输出调试信息，常用于检查系统运行状况</span></span><br><span class="line"><span class="attr">debug:</span> <span class="literal">true</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># 设置日志级别，root表示根节点，即整体应用日志级别</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">debug</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></pre></td><td class="code"><pre><span class="line"><span class="attr">logging:</span></span><br><span class="line"><span class="comment"># 设置日志组</span></span><br><span class="line">    <span class="attr">group:</span></span><br><span class="line">    <span class="comment"># 自定义组名，设置当前组中所包含的包</span></span><br><span class="line">        <span class="attr">ebank:</span> <span class="string">com.itheima.controller</span></span><br><span class="line">    <span class="attr">level:</span></span><br><span class="line">    <span class="attr">root:</span> <span class="string">warn</span></span><br><span class="line">        <span class="comment"># 为对应组设置日志级别</span></span><br><span class="line">        <span class="attr">ebank:</span> <span class="string">debug</span></span><br><span class="line">    <span class="comment"># 为对包设置日志级别</span></span><br><span class="line">        <span class="attr">com.itheima.controller:</span> <span class="string">debug</span></span><br></pre></td></tr></table></figure><p>说白了就是总体设置一下，每个包设置一下，如果感觉设置的麻烦，就先把包分个组，对组设置就行了</p><h3 id="小技巧：优化日志对象创建代码"><a href="#小技巧：优化日志对象创建代码" class="headerlink" title="小技巧：优化日志对象创建代码"></a>小技巧：优化日志对象创建代码</h3><p>写代码的时候每个类都要写创建日志记录对象，这个可以优化一下，使用<code>lombok</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></pre></td><td class="code"><pre><span class="line"><span class="meta">@RestController</span></span><br><span class="line"><span class="meta">@RequestMapping(&quot;/books&quot;)</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">BookController</span> <span class="keyword">extends</span> <span class="title class_">BaseClass</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">Logger</span> <span class="variable">log</span> <span class="operator">=</span> LoggerFactory.getLogger(BookController.class);<span class="comment">//这一句可以不写了</span></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>导入lombok后使用注解搞定，日志对象名为<code>log</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></pre></td><td class="code"><pre><span class="line"><span class="meta">@Slf4j</span><span class="comment">//这个注解替代了下面那一行</span></span><br><span class="line"><span class="meta">@RestController</span></span><br><span class="line"><span class="meta">@RequestMapping(&quot;/books&quot;)</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">BookController</span> <span class="keyword">extends</span> <span class="title class_">BaseClass</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">Logger</span> <span class="variable">log</span> <span class="operator">=</span> LoggerFactory.getLogger(BookController.class);<span class="comment">//这一句可以不写了</span></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h3 id="日志输出格式控制"><a href="#日志输出格式控制" class="headerlink" title="日志输出格式控制"></a>日志输出格式控制</h3><ul><li>日志已经能够记录了，但是目前记录的格式是SpringBoot给我们提供的，如果想自定义控制就需要自己设置了。先分析一下当前日志的记录格式。</li></ul><div class="img-wrap"><div class="img-bg"><img class="img" src="https://raw.githubusercontent.com/silvan2077/markdown_pic/main/Picgo/image-20211206123431222.png"/></div></div><ul><li>对于单条日志信息来说，日期，触发位置，记录信息是最核心的信息。级别用于做筛选过滤，PID与线程名用于做精准分析。了解这些信息后就可以DIY日志格式了。这里不做详细的研究。下面给出课程中模拟的官方日志模板</li></ul><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></pre></td><td class="code"><pre><span class="line"><span class="attr">logging:</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 %clr(%p) --- [%16t] %clr(%-40.40c)&#123;cyan&#125; : %m %n&quot;</span></span><br></pre></td></tr></table></figure><h3 id="日志文件"><a href="#日志文件" class="headerlink" title="日志文件"></a>日志文件</h3><ul><li>日志不仅能显示在控制台，还可以将日志记录到文件中，以便后期维护查阅。</li><li>对于日志文件的使用存在各种各样的策略，例如每日记录，分类记录，报警后记录等。这里主要研究日志文件如何记录。</li></ul><p>​记录日志到文件中格式非常简单，设置日志文件名即可。<br><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></pre></td><td class="code"><pre><span class="line"><span class="attr">logging:</span></span><br><span class="line"><span class="attr">file:</span></span><br><span class="line">    <span class="attr">name:</span> <span class="string">server.log</span></span><br></pre></td></tr></table></figure></p><p>​<br>虽然使用上述格式可以将日志记录下来了，但是面对线上的复杂情况，一个文件记录肯定是不能够满足运维要求的，通常会每天记录日志文件，同时为了便于维护，还要限制每个日志文件的大小。下面给出日志文件的常用配置方式：</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></pre></td><td class="code"><pre><span class="line"><span class="attr">logging:</span></span><br><span class="line"><span class="attr">logback:</span></span><br><span class="line">    <span class="attr">rollingpolicy:</span></span><br><span class="line">        <span class="attr">max-file-size:</span> <span class="string">3KB</span></span><br><span class="line">            <span class="attr">file-name-pattern:</span> <span class="string">server.%d&#123;yyyy-MM-dd&#125;.%i.log</span></span><br></pre></td></tr></table></figure><p>​<div class="note info no-icon flat"><p>以上格式是基于logback日志技术设置每日日志文件的设置格式，要求容量到达3KB以后就转存信息到第二个文件中。文件命名规则中的%d标识日期，%i是一个递增变量，用于区分日志文件。</p></div></p><p><strong>总结</strong></p><h2 id="运维实用篇完结"><a href="#运维实用篇完结" class="headerlink" title="运维实用篇完结"></a>运维实用篇完结</h2><p>​在整体运维实用篇中带着大家学习了4块内容，首先学习了如何运行SpringBoot程序，也就是程序的打包与运行，接下来对配置进行了升级学习，不再局限在配置文件中进行设置，通过临时属性，外部配置文件对项目的配置进行管控。在多环境开发中给大家介绍了多种多环境开发的格式，其实掌握一种即可，此外还给大家讲了多环境开发的一些技巧以及与maven的冲突解决方案。最后给大家介绍了日志系统，老实说日志这里讲的相当的潦草，因为大部分日志相关的知识都不应该在这门课中学习，这里只是告诉大家如何整合实用而已。</p><p>​        看了各位小伙伴的评论，知道你们再催更，我也在加油，一起努力吧，实用开发篇再会。实用开发篇会提高更新频度，不全部做完给大家更新了，我先把做好的一部分开放出来，随后做完一点就更新一点，额，好吧，就说到这里吧。</p><h1 id="SpringBoot开发实用篇"><a href="#SpringBoot开发实用篇" class="headerlink" title="SpringBoot开发实用篇"></a>SpringBoot开发实用篇</h1><p>​开发实用篇中主要内容是SpringBoot整合各种各样的技术，具体包含的内容如下：</p><ul><li>热部署</li><li>配置高级</li><li>测试</li><li>数据层解决方案</li><li>整合第三方技术</li><li>监控</li></ul><h2 id="热部署"><a href="#热部署" class="headerlink" title="热部署"></a>热部署</h2><p><code>热部署</code>：在应用程序运行的过程中，不需要停止或重启整个系统，就能<code>更新、替换或加载代码、配置或资源文件</code>的技术。简单说就是程序修改了，不需要重新启动服务器，服务器会自己悄悄的把更新后的程序给重新加载一遍，这就是热部署。</p><p>热部署的功能是如何实现的呢？这就要分两种情况来说了，非springboot工程和springboot工程的热部署实现方式完全不一样。先说一下原始的非springboot项目是如何实现热部署的。</p><details class="folding-tag" ><summary> 非SpringBoot项目启动热部署实现原理 </summary>              <div class='content'>              <ul><li>开发非springboot项目时，我们要制作一个web工程并通过tomcat启动，通常需要先安装tomcat服务器到磁盘中，开发的程序配置发布到安装的tomcat服务器上。</li><li><p>实现热部署的效果有两种做法：</p><ul><li>一种是在tomcat服务器的配置文件中进行配置，这种做法与使用什么IDE工具无关，不管你使用eclipse还是idea都行。</li><li>另一种是通过IDE工具进行配置，比如在idea工具中进行设置，这种形式需要依赖IDE工具，每款IDE工具不同，对应的配置也不太一样。但是核心思想是一样的，就是使用服务器去监控其中加载的应用，发现产生了变化就重新加载一次。</li></ul></li><li><p>看起来非springboot项目实现热部署是一个非常简单的过程，几乎每个小伙伴都能自己写出来。最简单的想法就是启动一个定时任务，任务启动时记录每个文件的大小，以后每5秒比对一下每个文件的大小是否有改变，或者是否有新文件。如果没有改变，放行，如果有改变，刷新当前记录的文件信息，然后重新启动服务器，这就可以实现热部署了。当然，这个过程肯定不能这么做，比如把一个打印输出的字符串”abc”改成”cba”，比对大小是没有变化的，但是内容缺实变了，所以这么做肯定不行，只是这么打个比方，而且重启服务器这就是冷启动了，不能算热部署。</p></li><li>看上去这个过程也没多复杂，在springboot项目中难道还有其他的弯弯绕吗？还真有。</li></ul>              </div>            </details><details class="folding-tag" ><summary> SpringBoot项目启动热部署实现原理 </summary>              <div class='content'>              <ul><li><p>基于springboot开发的web工程有一个显著的特征，就是tomcat服务器内嵌了。基础篇学过，服务器以一个对象的形式在spring容器中运行。本来我们期望于tomcat服务器加载程序后由tomcat服务器盯着程序，你变化后我就重新启动重新加载，但是现在tomcat和我们的程序是平级的，都是spring容器中的组件，这下Tomcat缺乏了一个直接的管理权，那该怎么做呢？简单，再搞一个程序X在spring容器中盯着你原始开发的程序A不就行了吗？确实，搞一个盯着程序A的程序X就行了，如果你自己开发的程序A变化了，那么程序X就命令tomcat容器重新加载程序A就OK了。并且这样做有一个好处，spring容器中东西不用全部重新加载一遍，只需要重新加载你开发的程序那一部分就可以了，这下效率又高了，挺好。</p></li><li><p>程序X肯定不需要我们自己手写，springboot早就做好了，搞一个坐标导入进去就行了。</p></li></ul>              </div>            </details><h3 id="手动启动热部署"><a href="#手动启动热部署" class="headerlink" title="手动启动热部署"></a>手动启动热部署</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>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">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></pre></td></tr></table></figure><p><strong>步骤二</strong>：构建项目，可以使用快捷键<code>CTRL + F9</code>激活此功能</p><div class="img-wrap"><div class="img-bg"><img class="img" src="https://raw.githubusercontent.com/silvan2077/markdown_pic/main/Picgo/image-20220222121257218.png"/></div></div><div class="note danger no-icon flat"><p>这就实现了springboot工程的热部署，使用起来非常的简单，接下来普及一下底层的工作工程</p></div><p><strong>重启与重载</strong></p><p>一个springboot项目在运行时实际上是分两个过程进行的，根据加载的东西不同，分成<code>base类加载器</code>与<code>restart类加载器</code>。</p><ul><li><code>base类加载器</code>：用来加载jar包中的类，jar包中的类和配置文件由于不会发生变化，因此不管加载多少次，加载的内容不会发生变化</li><li><code>restart类加载器</code>：用来加载开发者自己开发的类、配置文件、页面等信息，这一类文件受开发者影响</li></ul><p>当springboot项目启动时，base类加载器执行，加载jar包中的信息后，restart类加载器执行，加载开发者制作的内容。当执行构建项目后，由于jar中的信息不会变化，因此base类加载器无需再次执行，所以仅仅运行restart类加载即可，也就是将开发者自己制作的内容重新加载就行了，这就完成了一次热部署的过程，也可以说热部署的过程实际上是重新加载restart类加载器中的信息。</p><h3 id="自动启动热部署"><a href="#自动启动热部署" class="headerlink" title="自动启动热部署"></a>自动启动热部署</h3><ul><li>前面学习的启动热部署无论是点击按钮还是使用快捷键都需要开发者自己执行，我们还可以设置<code>自动热部署</code>实现代码修改后程序自己执行热部署功能，减少开发者操作。</li><li>自动热部署其实就是设计一个开关，打开这个开关后，IDE工具就可以自动热部署。因此这个操作和IDE工具有关，以下设置idea中启动热部署</li></ul><p><strong>步骤一</strong>：设置自动构建项目<br>打开【File】，选择【settings…】,在面板左侧的菜单中找到【Compile】选项，然后勾选【Build project automatically】，意思是自动构建项目</p><div class="img-wrap"><div class="img-bg"><img class="img" src="https://raw.githubusercontent.com/silvan2077/markdown_pic/main/Picgo/image-20220222123543551.png"/></div></div><p><strong>步骤二</strong>：允许在程序运行时进行自动构建<br>使用快捷键【Ctrl】+【Alt】+【Shit】+【/】打开维护面板，选择第1项【Registry…】</p><div class="img-wrap"><div class="img-bg"><img class="img" src="https://raw.githubusercontent.com/silvan2077/markdown_pic/main/Picgo/image-20220222124006910.png"/></div></div><p>在选项中搜索comple，然后勾选对应项即可<br><div class="img-wrap"><div class="img-bg"><img class="img" src="https://raw.githubusercontent.com/silvan2077/markdown_pic/main/Picgo/image-20220222124240069.png"/></div></div></p><p>​此时程序在运行的时候就可以进行自动构建了，实现了热部署的效果。</p><div class="note warning no-icon flat"><p>注意：</p><ul><li>如果每输入一个字母服务器都重构一次未免过于频繁，所以IDEA设置当IDEA工具失去焦点五秒后执行热部署操作。其实就是从idea工具中切换到其他工具时进行热部署，比如改完程序需要到浏览器上去调试，这个时候idea就自动进行热部署操作。</li></ul></div><p><strong>思考</strong></p><ul><li>现在已经实现了热部署了，但是到企业开发的时候会发现，为了便于管理，在你的程序目录中除了有代码，还有可能有文档，如果你修改了一下文档，这个时候会进行热部署吗？不管是否进行热部署，这个过程我们需要自己控制才比较合理</li></ul><h3 id="参与热部署监控的文件范围配置"><a href="#参与热部署监控的文件范围配置" class="headerlink" title="参与热部署监控的文件范围配置"></a>参与热部署监控的文件范围配置</h3><ul><li>前面实现了自动热部署，但是在企业开发中，程序目录中除了有代码还可能有文档，如果文档被修改了，IDEA会进行热部署吗？不管是否进行，这个过程我们自己控制才比较合理，接下来就来对热部署监控文件范围进行配置</li><li>通过修改项目中的文件，可以发现其实并不是所有的文件修改都会激活热部署的，原因在于在开发者工具中有一组配置，当满足了配置中的条件后，才会启动热部署。</li></ul><blockquote><ul><li>/META-INF/maven</li><li>/META-INF/resources</li><li>/resources</li><li>/static</li><li>/public</li><li>/templates<br>以上目录中的文件如果发生变化，是不参与热部署的。如果想修改配置，可以通过application.yml文件进行设定哪些文件不参与热部署操作</li></ul></blockquote><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></pre></td><td class="code"><pre><span class="line"><span class="attr">spring:</span></span><br><span class="line">  <span class="attr">devtools:</span></span><br><span class="line">    <span class="attr">restart:</span></span><br><span class="line">      <span class="comment"># 设置不参与热部署的文件或文件夹</span></span><br><span class="line">      <span class="attr">exclude:</span> <span class="string">static/**,public/**,config/application.yml</span></span><br></pre></td></tr></table></figure><h3 id="关闭热部署"><a href="#关闭热部署" class="headerlink" title="关闭热部署"></a>关闭热部署</h3><ul><li>热部署功能显然是一个在开发阶段使用的功能，到了线上环境运行时是不可能使用热部署功能的，所以需要强制关闭此功能，通过配置可以关闭此功能。</li></ul><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="attr">spring:</span></span><br><span class="line">  <span class="attr">devtools:</span></span><br><span class="line">    <span class="attr">restart:</span></span><br><span class="line">      <span class="attr">enabled:</span> <span class="literal">false</span></span><br></pre></td></tr></table></figure><ul><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></pre></td><td class="code"><pre><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_">SSMPApplication</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.setProperty(<span class="string">&quot;spring.devtools.restart.enabled&quot;</span>,<span class="string">&quot;false&quot;</span>);</span><br><span class="line">        SpringApplication.run(SSMPApplication.class);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><div class="note info no-icon flat"><p>其实上述担心略微有点多余，因为线上环境的维护是不可能出现修改代码的操作的，这么做唯一的作用是降低资源消耗，毕竟那双盯着你项目是不是产生变化的眼睛只要闭上了，就不具有热部署功能了，这个开关的作用就是禁用对应功能。</p></div><h2 id="配置高级"><a href="#配置高级" class="headerlink" title="配置高级"></a>配置高级</h2><ul><li>在前面的基础篇和运维使用篇都学习了一部分的开发配置，这里再详细介绍</li></ul><h3 id="ConfigurationProperties"><a href="#ConfigurationProperties" class="headerlink" title="@ConfigurationProperties"></a>@ConfigurationProperties</h3><p>在基础篇学习了<code>@ConfigurationProperties</code>注解,使用此注解后可以加载配置文件的信息为Bean绑定属性。</p><figure class="highlight yml"><table><tr><td class="gutter"><pre><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="attr">servers:</span></span><br><span class="line">  <span class="attr">ip-address:</span> <span class="number">192.168</span><span class="number">.0</span><span class="number">.1</span> </span><br><span class="line">  <span class="attr">port:</span> <span class="number">2345</span></span><br><span class="line">  <span class="attr">timeout:</span> <span class="number">-1</span></span><br></pre></td></tr></table></figure><p>开发一个用来封装数据的实体类，注意要提供属性对应的setter方法，因为默认是set注入<br><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><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="meta">@Component</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_">ServerConfig</span> &#123;</span><br><span class="line">    <span class="keyword">private</span> String ipAddress;</span><br><span class="line">    <span class="keyword">private</span> <span class="type">int</span> port;</span><br><span class="line">    <span class="keyword">private</span> <span class="type">long</span> timeout;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></p><p>使用<code>@ConfigurationProperties</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="meta">@Component</span></span><br><span class="line"><span class="meta">@Data</span></span><br><span class="line"><span class="meta">@ConfigurationProperties(prefix = &quot;servers&quot;)</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">ServerConfig</span> &#123;</span><br><span class="line">    <span class="keyword">private</span> String ipAddress;</span><br><span class="line">    <span class="keyword">private</span> <span class="type">int</span> port;</span><br><span class="line">    <span class="keyword">private</span> <span class="type">long</span> timeout;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>使用该方法能轻松为我们自己定义的Bean加载配置属性，但对第三方类的属性赋值时无法使用这种方法，因为无法在第三方类的源代码上添加<code>@ConfigurationProperties</code>注解，该如何解决这个问题呢？</p><p>使用@ConfigurationProperties注解其实可以为第三方bean加载属性，格式特殊一点而已。</p><p><strong>步骤一</strong>：使用@Bean注解定义第三方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></pre></td><td class="code"><pre><span class="line"><span class="meta">@Bean</span></span><br><span class="line"><span class="keyword">public</span> DruidDataSource <span class="title function_">datasource</span><span class="params">()</span>&#123;</span><br><span class="line">    <span class="type">DruidDataSource</span> <span class="variable">ds</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">DruidDataSource</span>();</span><br><span class="line">    <span class="keyword">return</span> ds;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p><strong>步骤二</strong>：在yml中定义要绑定的属性，注意datasource此时全小写</p><figure class="highlight yaml"><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="attr">datasource:</span></span><br><span class="line">  <span class="attr">driverClassName:</span> <span class="string">com.mysql.jdbc.Driver</span></span><br></pre></td></tr></table></figure><p><strong>步骤三</strong>：使用@ConfigurationProperties注解为第三方bean进行属性绑定，<strong>注意前缀是全小写的datasource</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></pre></td><td class="code"><pre><span class="line"><span class="meta">@Bean</span></span><br><span class="line"><span class="meta">@ConfigurationProperties(prefix = &quot;datasource&quot;)</span></span><br><span class="line"><span class="keyword">public</span> DruidDataSource <span class="title function_">datasource</span><span class="params">()</span>&#123;</span><br><span class="line">    <span class="type">DruidDataSource</span> <span class="variable">ds</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">DruidDataSource</span>();</span><br><span class="line">    <span class="keyword">return</span> ds;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><div class="note info no-icon flat"><p>与之前的操作方式完全一样，只不过<code>@ConfigurationProperties</code>注解不仅能添加到类上，还可以添加到方法上，<strong>添加到类上是为spring容器管理的当前类的对象绑定属性，添加到方法上是为spring容器管理的当前方法的返回值对象绑定属性</strong></p></div><div class="note warning no-icon flat"><ul><li><code>@ConfigurationProperties</code>即可以放在方法上也可以放在类上，相应的问题是这样找起来就比较麻烦了，Spring提供了一个全新的注解，专门标注使用<code>@ConfigurationProperties</code>注解绑定属性的bean是哪些。这个注解叫做<code>@EnableConfigurationProperties</code>。</li></ul></div><p><strong>步骤一</strong>：在主配置类上开启<code>@EnableConfigurationProperties</code>注解，并标注要使用<code>@ConfigurationProperties</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></pre></td><td class="code"><pre><span class="line"><span class="meta">@SpringBootApplication</span></span><br><span class="line"><span class="meta">@EnableConfigurationProperties(ServerConfig.class)</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">Springboot13ConfigurationApplication</span> &#123;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p><strong>步骤二</strong>：在对应的类上直接使用<code>@ConfigurationProperties</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></pre></td><td class="code"><pre><span class="line"><span class="meta">@Data</span></span><br><span class="line"><span class="meta">@ConfigurationProperties(prefix = &quot;servers&quot;)</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">ServerConfig</span> &#123;</span><br><span class="line">    <span class="keyword">private</span> String ipAddress;</span><br><span class="line">    <span class="keyword">private</span> <span class="type">int</span> port;</span><br><span class="line">    <span class="keyword">private</span> <span class="type">long</span> timeout;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><div class="note info no-icon flat"><p>区别：</p><ul><li>绑定属性的ServerConfig类不需要声明<code>@Component</code>注解。当使用<code>@EnableConfigurationProperties</code>注解时，spring会默认将其标注的类定义为bean，因此无需再次声明@Component注解了。</li><li>自己定义的类只需要在主配置文件中添加<code>@EnableConfigurationProperties</code>,第三方Bean使用<code>@Bean</code>+<code>@ConfigurationProperties</code></li></ul></div><p>最后再说一个小技巧，使用<code>@ConfigurationProperties</code>注解时，会出现一个提示信息</p><div class="img-wrap"><div class="img-bg"><img class="img" src="https://raw.githubusercontent.com/silvan2077/markdown_pic/main/Picgo/image-20220222145535749.png"/></div></div><p>出现这个提示后只需要添加一个坐标此提醒就消失了<br><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.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-configuration-processor<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><p><strong>总结</strong></p><ol><li>使用@ConfigurationProperties可以为使用@Bean声明的第三方bean绑定属性</li><li>当使用@EnableConfigurationProperties声明进行属性绑定的bean后，无需使用@Component注解再次进行bean声明</li></ol><div class="note warning no-icon flat"><p>总结：</p><ul><li>为第三方Bean进行属性绑定时，使用<code>@Bean</code>+<code>@ConfigurationProperties</code></li><li>自己的Bean进行属性绑定时，使用<code>@EnableConfigurationProperties</code>，不需要再添加<code>@Component</code>注解,系统会自动将bean进行声明</li></ul></div><h3 id="松散绑定"><a href="#松散绑定" class="headerlink" title="松散绑定"></a>松散绑定</h3><details class="folding-tag" yellow><summary> 引入 </summary>              <div class='content'>              <p>在进行属性绑定时，可能会遇到如下情况，为了进行标准命名，开发者会将原本全部为小写的属性名严格按照驼峰命名法书写，在yml配置文件中将datasource修改为dataSource，如下：<br><figure class="highlight yaml"><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="attr">dataSource:</span></span><br><span class="line">  <span class="attr">driverClassName:</span> <span class="string">com.mysql.jdbc.Driver</span></span><br></pre></td></tr></table></figure><br>此时直接启动程序，却发现只修改了配置文件中的名称，未更改程序中的名称，程序却可以正常运行，然后又将代码中的前缀datasource修改为dataSource，如下：<br><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><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="meta">@Bean</span></span><br><span class="line"><span class="meta">@ConfigurationProperties(prefix = &quot;dataSource&quot;)</span></span><br><span class="line"><span class="keyword">public</span> DruidDataSource <span class="title function_">datasource</span><span class="params">()</span>&#123;</span><br><span class="line">    <span class="type">DruidDataSource</span> <span class="variable">ds</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">DruidDataSource</span>();</span><br><span class="line">    <span class="keyword">return</span> ds;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><br>但此时却会出现错误，提示我们配置属性名无效，这就是SpringBoot为开发者提供的<code>松散绑定</code>功能。<br><figure class="highlight cmd"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><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">Configuration property name &#x27;dataSource&#x27; is <span class="keyword">not</span> valid:</span><br><span class="line"></span><br><span class="line">    Invalid characters: &#x27;S&#x27;</span><br><span class="line"><span class="function">    Bean: <span class="title">datasource</span></span></span><br><span class="line"><span class="function">    <span class="title">Reason</span>: <span class="title">Canonical</span> <span class="title">names</span> <span class="title">should</span> <span class="title">be</span> <span class="title">kebab</span>-<span class="title">case</span> (&#x27;-&#x27; <span class="title">separated</span>), <span class="title">lowercase</span> <span class="title">alpha</span>-<span class="title">numeric</span> <span class="title">characters</span> <span class="title">and</span> <span class="title">must</span> <span class="title">start</span> <span class="title">with</span> <span class="title">a</span> <span class="title">letter</span></span></span><br><span class="line"><span class="function"></span></span><br><span class="line"><span class="function"><span class="title">Action</span>:</span></span><br><span class="line"><span class="function"><span class="title">Modify</span> &#x27;<span class="title">dataSource</span>&#x27; <span class="title">so</span> <span class="title">that</span> <span class="title">it</span> <span class="title">conforms</span> <span class="title">to</span> <span class="title">the</span> <span class="title">canonical</span> <span class="title">names</span> <span class="title">requirements</span>.</span></span><br></pre></td></tr></table></figure></p>              </div>            </details><p><code>松散绑定</code>：配置文件中的命名格式与变量名的命名格式可以进行格式上的最大化兼容。这是SpringBoot编程时人性化设计的一个体现，而且不仅是大小写兼容，几乎所有的命名格式都支持。例如：<br><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><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="meta">@Component</span></span><br><span class="line"><span class="meta">@Data</span></span><br><span class="line"><span class="meta">@ConfigurationProperties(prefix = &quot;servers&quot;)</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">ServerConfig</span> &#123;</span><br><span class="line">    <span class="keyword">private</span> String ipAddress;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><br><strong>可以与下面的配置属性名规则全兼容</strong></p><figure class="highlight yml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><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="attr">servers:</span></span><br><span class="line">  <span class="attr">ipAddress:</span> <span class="number">192.168</span><span class="number">.0</span><span class="number">.2</span>       <span class="comment"># 驼峰模式</span></span><br><span class="line">  <span class="attr">ip_address:</span> <span class="number">192.168</span><span class="number">.0</span><span class="number">.2</span>      <span class="comment"># 下划线模式</span></span><br><span class="line">  <span class="attr">ip-address:</span> <span class="number">192.168</span><span class="number">.0</span><span class="number">.2</span>      <span class="comment"># 烤肉串模式</span></span><br><span class="line">  <span class="attr">IP_ADDRESS:</span> <span class="number">192.168</span><span class="number">.0</span><span class="number">.2</span>      <span class="comment"># 常量模式</span></span><br></pre></td></tr></table></figure><p>为什么这四种模式都可以匹配到servers的属性名呢？因为SpringBoot在进行匹配时，会将配置中的名称去掉中划线和下划线后，忽略大小写的情况下与java代码中的属性名进行忽略大小写的等值匹配，以上4种命名去掉下划线中划线忽略大小写后都是一个词ipaddress，java代码中的属性名忽略大小写后也是ipaddress，这样就可以进行等值匹配了。</p><div class="note warning no-icon flat"><p>虽然这四种方式都能匹配，不过springboot官方推荐使用烤肉串模式，也就是<code>中划线模式</code>。</p></div><ul><li><p>接下来再了解一下命名规范问题，观察前面的报错信息</p><figure class="highlight cmd"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><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">Configuration property name &#x27;dataSource&#x27; is <span class="keyword">not</span> valid:</span><br><span class="line"></span><br><span class="line">    Invalid characters: &#x27;S&#x27;</span><br><span class="line"><span class="function">    Bean: <span class="title">datasource</span></span></span><br><span class="line"><span class="function">    <span class="title">Reason</span>: <span class="title">Canonical</span> <span class="title">names</span> <span class="title">should</span> <span class="title">be</span> <span class="title">kebab</span>-<span class="title">case</span> (&#x27;-&#x27; <span class="title">separated</span>), <span class="title">lowercase</span> <span class="title">alpha</span>-<span class="title">numeric</span> <span class="title">characters</span> <span class="title">and</span> <span class="title">must</span> <span class="title">start</span> <span class="title">with</span> <span class="title">a</span> <span class="title">letter</span></span></span><br><span class="line"><span class="function"></span></span><br><span class="line"><span class="function"><span class="title">Action</span>:</span></span><br><span class="line"><span class="function"><span class="title">Modify</span> &#x27;<span class="title">dataSource</span>&#x27; <span class="title">so</span> <span class="title">that</span> <span class="title">it</span> <span class="title">conforms</span> <span class="title">to</span> <span class="title">the</span> <span class="title">canonical</span> <span class="title">names</span> <span class="title">requirements</span>.</span></span><br></pre></td></tr></table></figure></li><li><p>Reason描述了报错的原因，规范的名称应该是<code>烤肉串模式(kebabcase)</code>，即使用-分隔，使用小写字母数字作为标准字符，且必须以字母开头。名称dataSource就不满足上述要求。</p></li></ul><div class="note warning no-icon flat"><p>注意：</p><ul><li>以上规则仅针对springboot中<code>@ConfigurationProperties</code>注解进行属性绑定时有效，对<code>@Value</code>注解进行属性映射无效。</li></ul></div><p><strong>总结</strong></p><ol><li>@ConfigurationProperties绑定属性时支持属性名宽松绑定，这个宽松体现在属性名的命名规则上</li><li>@Value注解不支持松散绑定规则</li><li>绑定前缀名推荐采用烤肉串命名规则，即使用中划线做分隔符</li></ol><h3 id="常用计量单位绑定"><a href="#常用计量单位绑定" class="headerlink" title="常用计量单位绑定"></a>常用计量单位绑定</h3><ul><li>在前面的配置中，我们书写了如下配置值，其中第三项超时时间timeout描述了服务器操作超时时间，当前值是-1表示永不超时。</li></ul><figure class="highlight yml"><table><tr><td class="gutter"><pre><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="attr">servers:</span></span><br><span class="line">  <span class="attr">ip-address:</span> <span class="number">192.168</span><span class="number">.0</span><span class="number">.1</span> </span><br><span class="line">  <span class="attr">port:</span> <span class="number">2345</span></span><br><span class="line">  <span class="attr">timeout:</span> <span class="number">-1</span></span><br></pre></td></tr></table></figure><ul><li>-1引发的歧义可能会比较小，但如果此时timeout的值为240，这时候可能就会出现较大的歧义了，比如线上服务器完成一次主从备份，配置超时时间240，这个240如果单位是秒，就是4分钟，如果单位是分钟就是4小时。面对一次线上服务器的主从备份，设置4分钟，简直是开玩笑，别说拷贝过程，备份之前的压缩过程4分钟也搞不定，这可能会引发许多问题。</li><li>springboot利用JDK8中提供的全新的用来表示计量单位的新数据类型，从根本上解决这个问题。以下模型类中添加了两个JDK8中新增的类，分别是<code>Duration</code>和<code>DataSize</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><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">@Component</span></span><br><span class="line"><span class="meta">@Data</span></span><br><span class="line"><span class="meta">@ConfigurationProperties(prefix = &quot;servers&quot;)</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">ServerConfig</span> &#123;</span><br><span class="line">    <span class="comment">// 时间单位为小时</span></span><br><span class="line">    <span class="meta">@DurationUnit(ChronoUnit.HOURS)</span></span><br><span class="line">    <span class="keyword">private</span> Duration serverTimeOut;</span><br><span class="line">    <span class="comment">// 大小单位为MB</span></span><br><span class="line">    <span class="meta">@DataSizeUnit(DataUnit.MEGABYTES)</span></span><br><span class="line">    <span class="keyword">private</span> DataSize dataSize;</span><br><span class="line">&#125;</span><br><span class="line"></span><br></pre></td></tr></table></figure><ul><li><p><strong>Duration</strong>：表示时间间隔，可以通过<code>@DurationUnit</code>注解描述时间单位，例如上例中描述的单位为小时（ChronoUnit.HOURS）</p></li><li><p><strong>DataSize</strong>：表示存储空间，可以通过<code>@DataSizeUnit</code>注解描述存储空间单位，例如上例中描述的单位为MB（DataUnit.MEGABYTES）</p></li></ul><div class="tabs" id="常用计量单位"><ul class="nav-tabs"><li class="tab active"><button type="button" data-href="#常用计量单位-1">Druation</button></li><li class="tab"><button type="button" data-href="#常用计量单位-2">DataSize</button></li></ul><div class="tab-contents"><div class="tab-item-content active" id="常用计量单位-1"><div class="img-wrap"><div class="img-bg"><img class="img" src="https://raw.githubusercontent.com/silvan2077/markdown_pic/main/Picgo/image-20220222173911102.png"/></div></div><button type="button" class="tab-to-top" aria-label="scroll to top"><i class="fas fa-arrow-up"></i></button></div><div class="tab-item-content" id="常用计量单位-2"><div class="img-wrap"><div class="img-bg"><img class="img" src="https://raw.githubusercontent.com/silvan2077/markdown_pic/main/Picgo/image-20220222174130102.png"/></div></div><button type="button" class="tab-to-top" aria-label="scroll to top"><i class="fas fa-arrow-up"></i></button></div></div></div><h3 id="校验"><a href="#校验" class="headerlink" title="校验"></a>校验</h3><ul><li>因为SpringBoot提供的宽松绑定机制，在属性名称的书写上我们可以放飞自我了，但如果在书写时一时忘记了模型类中的数据类型，就会出现类型不匹配的问题，如属性需要int类型，但在配置中给了一个字符串，这肯定无法完成有效的属性绑定，还会引发一些错误。</li><li>SpringBoot为我们<code>集成</code>了强大的<code>数据校验</code>功能来避免此类问题的发生。</li><li>在JAVAEE的JSR303规范中给出了具体的数据校验标准，开发者可以根据自己的需要选择对应的校验框架，此处使用<code>Hibernate</code>提供的校验框架来作为实现进行数据校验。书写应用格式非常固定</li></ul><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;!--1.导入JSR303规范--&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>javax.validation<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>validation-api<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="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.validator<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-validator<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>步骤二</strong>：在需要开启校验功能的类上使用注解<code>@Validated</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></pre></td><td class="code"><pre><span class="line"><span class="meta">@Component</span></span><br><span class="line"><span class="meta">@Data</span></span><br><span class="line"><span class="meta">@ConfigurationProperties(prefix = &quot;servers&quot;)</span></span><br><span class="line"><span class="comment">//开启对当前bean的属性注入校验</span></span><br><span class="line"><span class="meta">@Validated</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">ServerConfig</span> &#123;</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></pre></td><td class="code"><pre><span class="line"><span class="meta">@Component</span></span><br><span class="line"><span class="meta">@Data</span></span><br><span class="line"><span class="meta">@ConfigurationProperties(prefix = &quot;servers&quot;)</span></span><br><span class="line"><span class="comment">//开启对当前bean的属性注入校验</span></span><br><span class="line"><span class="meta">@Validated</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">ServerConfig</span> &#123;</span><br><span class="line">    <span class="comment">//设置具体的规则</span></span><br><span class="line">    <span class="meta">@Max(value = 8888,message = &quot;最大值不能超过8888&quot;)</span></span><br><span class="line">    <span class="meta">@Min(value = 202,message = &quot;最小值不能低于202&quot;)</span></span><br><span class="line">    <span class="keyword">private</span> <span class="type">int</span> port;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><div class="note warning no-icon flat"><p>通过设置数据格式校验可以有效避免非法数据加载，而且格式非常固定，使用起来非常简单</p></div><h3 id="数据类型转换"><a href="#数据类型转换" class="headerlink" title="数据类型转换"></a>数据类型转换</h3><ul><li>关于SpringBoot属性注入的问题基本结束，在学习过程中遇到的问题往往复杂度较低，但线上开发时遇到的问题往往要复杂很多，比如数据库连接失败，如下报错信息，显示数据库的密码错误。</li></ul><figure class="highlight cmd"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="function">java.sql.SQLException: <span class="title">Access</span> <span class="title">denied</span> <span class="title">for</span> <span class="title">user</span> &#x27;<span class="title">root</span>&#x27;@&#x27;<span class="title">localhost</span>&#x27; (<span class="title">using</span> <span class="title">password</span>: <span class="title">YES</span>)</span></span><br></pre></td></tr></table></figure><ul><li><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></pre></td><td class="code"><pre><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/ssm_db?serverTimezone=UTC</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="number">0127</span></span><br></pre></td></tr></table></figure></li><li><p>可以看到配置文件中的密码设置为了0127，一般人看到就会认为因为使用者的生日是1月27日，所以密码使用0127，但是计算机在接收数据时会认为这是一个<code>八进制数</code>，因为该数以0开头且每个数字均小于8，所以会被转化为十进制下的87存储起来。</p></li></ul><div class="note warning no-icon flat"><p>解决方法：</p><ol><li>使用字符串时标准加上引号包裹，如<code>&quot;0127&quot;</code></li><li>遇到0开头的数据多加注意。</li></ol></div><h2 id="测试"><a href="#测试" class="headerlink" title="测试"></a>测试</h2><p>测试是保障程序正确性的唯一屏障，在企业级开发中更是不可缺少，但是由于测试代码往往不产生实际效益，可能很多初学者并不是很关注，其实测试非常重要</p><h3 id="加载测试专用属性"><a href="#加载测试专用属性" class="headerlink" title="加载测试专用属性"></a>加载测试专用属性</h3><h4 id="临时属性"><a href="#临时属性" class="headerlink" title="临时属性"></a>临时属性</h4><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></pre></td><td class="code"><pre><span class="line"><span class="attr">env:</span></span><br><span class="line">  <span class="attr">maxMemory:</span> <span class="string">32GB</span></span><br><span class="line">  <span class="attr">minMemory:</span> <span class="string">16GB</span></span><br></pre></td></tr></table></figure><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></pre></td><td class="code"><pre><span class="line"><span class="attr">env:</span></span><br><span class="line">  <span class="attr">maxMemory:</span> <span class="string">16GB</span></span><br><span class="line">  <span class="attr">minMemory:</span> <span class="string">8GB</span></span><br></pre></td></tr></table></figure><p>这个时候我们肯定不能每次测试的时候都去修改源码application.yml中的配置进行测试。于是我们想到在前面也学过添加<code>临时属性</code>，那么测试时如何添加临时属性呢？</p><p>​springboot为测试时使用临时属性提供了对应的功能入口。在测试用例程序中，可以通过对注解<code>@SpringBootTest</code>添加属性来模拟临时属性，具体如下：<br><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><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">//properties属性可以为当前测试用例添加临时的属性配置</span></span><br><span class="line"><span class="meta">@SpringBootTest(properties = &#123;&quot;test.prop=testValue1&quot;&#125;)</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">PropertiesAndArgsTest</span> &#123;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Value(&quot;$&#123;test.prop&#125;&quot;)</span></span><br><span class="line">    <span class="keyword">private</span> String msg;</span><br><span class="line">    </span><br><span class="line">    <span class="meta">@Test</span></span><br><span class="line">    <span class="keyword">void</span> <span class="title function_">testProperties</span><span class="params">()</span>&#123;</span><br><span class="line">        System.out.println(msg);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></p><h4 id="临时参数"><a href="#临时参数" class="headerlink" title="临时参数"></a>临时参数</h4><p>除了上述这种情况，在前面讲解使用命令行启动springboot程序时讲过，通过命令行参数也可以设置属性值。而且线上启动程序时，通常都会添加一些专用的配置信息。作为运维人员他们才不懂java，更不懂这些配置的信息具体格式该怎么写，那如果我们作为开发者提供了对应的书写内容后，能否提前测试一下这些配置信息是否有效呢？当时是可以的，还是通过注解<code>@SpringBootTest</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></pre></td><td class="code"><pre><span class="line"><span class="comment">//args属性可以为当前测试用例添加临时的命令行参数</span></span><br><span class="line"><span class="meta">@SpringBootTest(args=&#123;&quot;--test.prop=testValue2&quot;&#125;)</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">PropertiesAndArgsTest</span> &#123;</span><br><span class="line">    </span><br><span class="line">    <span class="meta">@Value(&quot;$&#123;test.prop&#125;&quot;)</span></span><br><span class="line">    <span class="keyword">private</span> String msg;</span><br><span class="line">    </span><br><span class="line">    <span class="meta">@Test</span></span><br><span class="line">    <span class="keyword">void</span> <span class="title function_">testProperties</span><span class="params">()</span>&#123;</span><br><span class="line">        System.out.println(msg);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>​使用注解<code>@SpringBootTest的args</code>属性就可以为当前测试用例模拟命令行参数并进行测试。</p><ul><li>此时可能会有一个问题，那就是添加的临时属性和临时参数谁的优先级更高呢？<ul><li>这个问题在前面运维实用篇遇到过类似的，而结果也相同，属性加载时会严格按照下面的顺序进行加载，越往下的优先级越高。在这个属性加载优先级的顺序中，明确规定了命令行参数的优先级排序是11，而配置属性的优先级是3，结果就不言而喻了<div class="img-wrap"><div class="img-bg"><img class="img" src="https://raw.githubusercontent.com/silvan2077/markdown_pic/main/Picgo/image-20211206100859236.png"/></div></div></li></ul></li></ul><h3 id="加载测试专用配置"><a href="#加载测试专用配置" class="headerlink" title="加载测试专用配置"></a>加载测试专用配置</h3><ul><li><p>在测试过程中，偶尔需要临时加载一些Bean，专门应用于测试环境，这时候就可以使用<code>@Import</code>注解来实现。</p></li><li><p>在之前学习Spring时我们就知道一个spring环境中可以设置若干个配置文件或配置类，若干个配置信息可以同时生效。现在我们的需求就是在测试环境中再添加一个配置类，然后启动测试环境时，生效此配置就行了。做法和spring环境中加载多个配置信息的方式完全一样。具体操作步骤如下：</p></li></ul><p><strong>步骤一</strong>：在测试包test中创建专用的测试环境配置类</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="meta">@Configuration</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">MsgConfig</span> &#123;</span><br><span class="line">    <span class="meta">@Bean</span></span><br><span class="line">    <span class="keyword">public</span> String <span class="title function_">msg</span><span class="params">()</span>&#123;</span><br><span class="line">        <span class="keyword">return</span> <span class="string">&quot;bean msg&quot;</span>;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>上述配置仅用于演示当前实验效果，实际开发可不能这么注入String类型的数据</p><p><strong>步骤二</strong>：在启动测试环境时，导入测试环境专用的配置类，使用<code>@Import</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></pre></td><td class="code"><pre><span class="line"><span class="meta">@SpringBootTest</span></span><br><span class="line"><span class="meta">@Import(&#123;MsgConfig.class&#125;)</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">ConfigurationTest</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> String msg;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Test</span></span><br><span class="line">    <span class="keyword">void</span> <span class="title function_">testConfiguration</span><span class="params">()</span>&#123;</span><br><span class="line">        System.out.println(msg);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>到这里就通过<code>@Import</code>属性实现了基于开发环境的配置基础上，对配置进行测试环境的追加操作,这样我们就可以实现每一个不同的测试用例加载不同的bean的效果，丰富测试用例的编写，同时不影响开发环境的配置。</p><h3 id="Web环境模拟测试"><a href="#Web环境模拟测试" class="headerlink" title="Web环境模拟测试"></a>Web环境模拟测试</h3><ul><li>目前已经能够实现业务层和数据层的测试，并通过设置临时配置控制每个测试用例加载不同的测试数据，但是我们目前对表现层的测试都是通过postman进行手工测试的，没有在打包过程中体现表现层能被测试通过，现在便来学习如何测试表现层。</li><li>对表现层功能进行测试需要一个基础和一个功能，一个基础即测试程序时必须启动Web环境，一个功能即测试程序需要具备发送Web请求的能力。</li><li>测试表现层接口这项工作成功转换成两件事<ol><li>启动Web环境</li><li>发送Web请求</li></ol></li></ul><h4 id="启动Web环境"><a href="#启动Web环境" class="headerlink" title="启动Web环境"></a>启动Web环境</h4><ul><li>在每个SpringBoot测试类的上方会标注<code>@SpringBootTest</code>注解，而在这个注解中还有一个属性叫做<code>WebEnvironment</code>，可以通过设置该属性，在测试用例中启动Web环境:<figure class="highlight java"><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="meta">@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">WebTest</span> &#123;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>测试类中启动web环境时，可以指定启动的Web环境对应的端口，springboot提供了4种设置值，分别如下：</li></ul><div class="img-wrap"><div class="img-bg"><img class="img" src="https://raw.githubusercontent.com/silvan2077/markdown_pic/main/Picgo/image-20220223125453317.png"/></div></div><ul><li><code>MOCK</code>：根据当前设置确认是否启动web环境，例如使用了Servlet的API就启动web环境，属于适配性的配置</li><li><code>DEFINED_PORT</code>：使用自定义的端口作为web服务器端口</li><li><code>RANDOM_PORT</code>：使用随机端口作为web服务器端口</li><li><code>NONE</code>：不启动web环境</li></ul><p>​        通过上述配置，现在启动测试程序时就可以正常启用web环境了，建议在测试时使用<code>RANDOM_PORT</code>，避免代码中因为写死设定引发线上功能打包测试时由于端口冲突导致意外现象的出现。</p><h4 id="测试类中发送请求"><a href="#测试类中发送请求" class="headerlink" title="测试类中发送请求"></a>测试类中发送请求</h4><p>对于测试类中发送请求，其实java的API就提供对应的功能，只不过平时很少使用，所以较为陌生。springboot为了便于开发者进行对应的功能开发，对其又进行了包装，简化了开发步骤，具体操作如下：</p><p><strong>步骤一</strong>：在测试类中开启web虚拟调用功能，通过注解<code>@AutoConfigureMockMvc</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></pre></td><td class="code"><pre><span class="line"><span class="meta">@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)</span></span><br><span class="line"><span class="comment">//开启虚拟MVC调用</span></span><br><span class="line"><span class="meta">@AutoConfigureMockMvc</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">WebTest</span> &#123;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p><strong>步骤二</strong>：定义发起虚拟调用的对象<code>MockMVC</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></pre></td><td class="code"><pre><span class="line"><span class="meta">@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)</span></span><br><span class="line"><span class="comment">//开启虚拟MVC调用</span></span><br><span class="line"><span class="meta">@AutoConfigureMockMvc</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">WebTest</span> &#123;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Test</span></span><br><span class="line">    <span class="keyword">void</span> <span class="title function_">testWeb</span><span class="params">(<span class="meta">@Autowired</span> MockMvc mvc)</span> &#123;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p><strong>步骤三</strong>：创建一个虚拟请求对象，封装请求的路径，并使用MockMVC对象发送对应请求</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></pre></td><td class="code"><pre><span class="line"><span class="meta">@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)</span></span><br><span class="line"><span class="comment">//开启虚拟MVC调用</span></span><br><span class="line"><span class="meta">@AutoConfigureMockMvc</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">WebTest</span> &#123;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Test</span></span><br><span class="line">    <span class="keyword">void</span> <span class="title function_">testWeb</span><span class="params">(<span class="meta">@Autowired</span> MockMvc mvc)</span> <span class="keyword">throws</span> Exception &#123;</span><br><span class="line">        <span class="comment">//http://localhost:8080/books</span></span><br><span class="line">        <span class="comment">//创建虚拟请求，当前访问/books</span></span><br><span class="line">        <span class="type">MockHttpServletRequestBuilder</span> <span class="variable">builder</span> <span class="operator">=</span> MockMvcRequestBuilders.get(<span class="string">&quot;/books&quot;</span>);</span><br><span class="line">        <span class="comment">//执行对应的请求</span></span><br><span class="line">        mvc.perform(builder);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>执行测试程序，现在就可以正常的发送<code>/books</code>对应的请求了，注意访问路径不要写<code>http://localhost:8080/books</code>，因为前面的服务器IP地址和端口使用的是当前虚拟的web环境，无需指定，仅指定请求的具体路径即可。</p><h4 id="Web环境请求结果比对"><a href="#Web环境请求结果比对" class="headerlink" title="Web环境请求结果比对"></a>Web环境请求结果比对</h4><ul><li>目前已经能成功发送请求了，但是还没有起到测试的效果，需要比对预计值和真实值是否一致才能确认是否成功通过测试。</li><li>其实发完请求后得到的信息只有一种，即<code>响应对象</code>。至于响应对象中包含什么，就可以比对什么。常见的比对内容如下：<div class="tabs" id="请求记过比对"><ul class="nav-tabs"><li class="tab active"><button type="button" data-href="#请求记过比对-1">响应状态匹配</button></li><li class="tab"><button type="button" data-href="#请求记过比对-2">响应体匹配（非json数据格式）</button></li><li class="tab"><button type="button" data-href="#请求记过比对-3">响应体匹配(json数据格式)</button></li><li class="tab"><button type="button" data-href="#请求记过比对-4">响应头匹配</button></li></ul><div class="tab-contents"><div class="tab-item-content active" id="请求记过比对-1"><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><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">@Test</span></span><br><span class="line"><span class="keyword">void</span> <span class="title function_">testStatus</span><span class="params">(<span class="meta">@Autowired</span> MockMvc mvc)</span> <span class="keyword">throws</span> Exception &#123;</span><br><span class="line">    <span class="type">MockHttpServletRequestBuilder</span> <span class="variable">builder</span> <span class="operator">=</span> MockMvcRequestBuilders.get(<span class="string">&quot;/books&quot;</span>);</span><br><span class="line">    <span class="type">ResultActions</span> <span class="variable">action</span> <span class="operator">=</span> mvc.perform(builder);</span><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">StatusResultMatchers</span> <span class="variable">status</span> <span class="operator">=</span> MockMvcResultMatchers.status();</span><br><span class="line">    <span class="comment">//预计本次调用时成功的：状态200</span></span><br><span class="line">    <span class="type">ResultMatcher</span> <span class="variable">ok</span> <span class="operator">=</span> status.isOk();</span><br><span class="line">    <span class="comment">//添加预计值到本次调用过程中进行匹配</span></span><br><span class="line">    action.andExpect(ok);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><button type="button" class="tab-to-top" aria-label="scroll to top"><i class="fas fa-arrow-up"></i></button></div><div class="tab-item-content" id="请求记过比对-2"><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><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="meta">@Test</span></span><br><span class="line"><span class="keyword">void</span> <span class="title function_">testBody</span><span class="params">(<span class="meta">@Autowired</span> MockMvc mvc)</span> <span class="keyword">throws</span> Exception &#123;</span><br><span class="line">    <span class="type">MockHttpServletRequestBuilder</span> <span class="variable">builder</span> <span class="operator">=</span> MockMvcRequestBuilders.get(<span class="string">&quot;/books&quot;</span>);</span><br><span class="line">    <span class="type">ResultActions</span> <span class="variable">action</span> <span class="operator">=</span> mvc.perform(builder);</span><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">ContentResultMatchers</span> <span class="variable">content</span> <span class="operator">=</span> MockMvcResultMatchers.content();</span><br><span class="line">    <span class="type">ResultMatcher</span> <span class="variable">result</span> <span class="operator">=</span> content.string(<span class="string">&quot;springboot2&quot;</span>);</span><br><span class="line">    <span class="comment">//添加预计值到本次调用过程中进行匹配</span></span><br><span class="line">    action.andExpect(result);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><button type="button" class="tab-to-top" aria-label="scroll to top"><i class="fas fa-arrow-up"></i></button></div><div class="tab-item-content" id="请求记过比对-3"><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><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="meta">@Test</span></span><br><span class="line"><span class="keyword">void</span> <span class="title function_">testJson</span><span class="params">(<span class="meta">@Autowired</span> MockMvc mvc)</span> <span class="keyword">throws</span> Exception &#123;</span><br><span class="line">    <span class="type">MockHttpServletRequestBuilder</span> <span class="variable">builder</span> <span class="operator">=</span> MockMvcRequestBuilders.get(<span class="string">&quot;/books&quot;</span>);</span><br><span class="line">    <span class="type">ResultActions</span> <span class="variable">action</span> <span class="operator">=</span> mvc.perform(builder);</span><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">ContentResultMatchers</span> <span class="variable">content</span> <span class="operator">=</span> MockMvcResultMatchers.content();</span><br><span class="line">    <span class="type">ResultMatcher</span> <span class="variable">result</span> <span class="operator">=</span> content.json(<span class="string">&quot;&#123;\&quot;id\&quot;:1,\&quot;name\&quot;:\&quot;springboot2\&quot;,\&quot;type\&quot;:\&quot;springboot\&quot;&#125;&quot;</span>);</span><br><span class="line">    <span class="comment">//添加预计值到本次调用过程中进行匹配</span></span><br><span class="line">    action.andExpect(result);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><button type="button" class="tab-to-top" aria-label="scroll to top"><i class="fas fa-arrow-up"></i></button></div><div class="tab-item-content" id="请求记过比对-4"><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><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="meta">@Test</span></span><br><span class="line"><span class="keyword">void</span> <span class="title function_">testContentType</span><span class="params">(<span class="meta">@Autowired</span> MockMvc mvc)</span> <span class="keyword">throws</span> Exception &#123;</span><br><span class="line">    <span class="type">MockHttpServletRequestBuilder</span> <span class="variable">builder</span> <span class="operator">=</span> MockMvcRequestBuilders.get(<span class="string">&quot;/books&quot;</span>);</span><br><span class="line">    <span class="type">ResultActions</span> <span class="variable">action</span> <span class="operator">=</span> mvc.perform(builder);</span><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">HeaderResultMatchers</span> <span class="variable">header</span> <span class="operator">=</span> MockMvcResultMatchers.header();</span><br><span class="line">    <span class="type">ResultMatcher</span> <span class="variable">contentType</span> <span class="operator">=</span> header.string(<span class="string">&quot;Content-Type&quot;</span>, <span class="string">&quot;application/json&quot;</span>);</span><br><span class="line">    <span class="comment">//添加预计值到本次调用过程中进行匹配</span></span><br><span class="line">    action.andExpect(contentType);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><button type="button" class="tab-to-top" aria-label="scroll to top"><i class="fas fa-arrow-up"></i></button></div></div></div></li></ul><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="meta">@Test</span></span><br><span class="line"><span class="keyword">void</span> <span class="title function_">testGetById</span><span class="params">(<span class="meta">@Autowired</span> MockMvc mvc)</span> <span class="keyword">throws</span> Exception &#123;</span><br><span class="line">    <span class="type">MockHttpServletRequestBuilder</span> <span class="variable">builder</span> <span class="operator">=</span> MockMvcRequestBuilders.get(<span class="string">&quot;/books&quot;</span>);</span><br><span class="line">    <span class="type">ResultActions</span> <span class="variable">action</span> <span class="operator">=</span> mvc.perform(builder);</span><br><span class="line"></span><br><span class="line">    <span class="type">StatusResultMatchers</span> <span class="variable">status</span> <span class="operator">=</span> MockMvcResultMatchers.status();</span><br><span class="line">    <span class="type">ResultMatcher</span> <span class="variable">ok</span> <span class="operator">=</span> status.isOk();</span><br><span class="line">    action.andExpect(ok);</span><br><span class="line"></span><br><span class="line">    <span class="type">HeaderResultMatchers</span> <span class="variable">header</span> <span class="operator">=</span> MockMvcResultMatchers.header();</span><br><span class="line">    <span class="type">ResultMatcher</span> <span class="variable">contentType</span> <span class="operator">=</span> header.string(<span class="string">&quot;Content-Type&quot;</span>, <span class="string">&quot;application/json&quot;</span>);</span><br><span class="line">    action.andExpect(contentType);</span><br><span class="line"></span><br><span class="line">    <span class="type">ContentResultMatchers</span> <span class="variable">content</span> <span class="operator">=</span> MockMvcResultMatchers.content();</span><br><span class="line">    <span class="type">ResultMatcher</span> <span class="variable">result</span> <span class="operator">=</span> content.json(<span class="string">&quot;&#123;\&quot;id\&quot;:1,\&quot;name\&quot;:\&quot;springboot\&quot;,\&quot;type\&quot;:\&quot;springboot\&quot;&#125;&quot;</span>);</span><br><span class="line">    action.andExpect(result);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h3 id="数据层测试回滚"><a href="#数据层测试回滚" class="headerlink" title="数据层测试回滚"></a>数据层测试回滚</h3><p>此时测试程序可以进行表现层、业务层、数据层接口对应的功能测试了，但是测试用例开发完成后，在打包的阶段由于test生命周期属于必须被运行的生命周期，如果跳过会给系统带来极高的安全隐患，所以测试用例必须执行。但是新的问题就呈现了，测试用例如果测试时产生了事务提交就会在测试过程中对数据库数据产生影响，进而产生垃圾数据。该如何避免这个情况呢？</p><p>springboot针对此问题给出了最简单的解决方案，在原始测试用例中添加注解<code>@Transactional</code>即可实现当前测试用例的事务不提交。当程序运行后，只要注解<code>@Transactional</code>出现的位置存在注解<code>@SpringBootTest</code>，springboot就会认为这是一个测试程序，无需提交事务，所以也就可以避免事务的提交。</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="meta">@SpringBootTest</span></span><br><span class="line"><span class="meta">@Transactional</span></span><br><span class="line"><span class="meta">@Rollback(true)</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">DaoTest</span> &#123;</span><br><span class="line">    <span class="meta">@Autowired</span></span><br><span class="line">    <span class="keyword">private</span> BookService bookService;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Test</span></span><br><span class="line">    <span class="keyword">void</span> <span class="title function_">testSave</span><span class="params">()</span>&#123;</span><br><span class="line">        <span class="type">Book</span> <span class="variable">book</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">Book</span>();</span><br><span class="line">        book.setName(<span class="string">&quot;springboot3&quot;</span>);</span><br><span class="line">        book.setType(<span class="string">&quot;springboot3&quot;</span>);</span><br><span class="line">        book.setDescription(<span class="string">&quot;springboot3&quot;</span>);</span><br><span class="line"></span><br><span class="line">        bookService.save(book);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><div class="note warning no-icon flat"><p>如果开发者想提交事务，再添加一个<code>@RollBack</code>的注解，设置回滚状态为false即可正常提交事务</p></div><p><strong>总结</strong></p><ol><li>在springboot的测试类中通过添加注解@Transactional来阻止测试用例提交事务</li><li>通过注解@Rollback控制springboot测试类执行结果是否提交事务，需要配合注解@Transactional使用</li></ol><h3 id="测试用例数据设定"><a href="#测试用例数据设定" class="headerlink" title="测试用例数据设定"></a>测试用例数据设定</h3><ul><li>对于测试用例的数据固定书写肯定是不合理的，springboot提供了在配置中<code>使用随机值</code>的机制，确保每次运行程序加载的数据都是随机的。具体如下：</li></ul><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="attr">testcase:</span></span><br><span class="line">  <span class="attr">book:</span></span><br><span class="line">    <span class="attr">id:</span> <span class="string">$&#123;random.int&#125;</span></span><br><span class="line">    <span class="attr">id2:</span> <span class="string">$&#123;random.int(10)&#125;</span></span><br><span class="line">    <span class="attr">type:</span> <span class="string">$&#123;random.int!5,10!&#125;</span></span><br><span class="line">    <span class="attr">name:</span> <span class="string">$&#123;random.value&#125;</span></span><br><span class="line">    <span class="attr">uuid:</span> <span class="string">$&#123;random.uuid&#125;</span></span><br><span class="line">    <span class="attr">publishTime:</span> <span class="string">$&#123;random.long&#125;</span></span><br></pre></td></tr></table></figure><ul><li>当前配置就可以在每次运行程序时创建一组随机数据，避免每次运行时数据都是固定值的尴尬现象发生，有助于测试功能的进行。数据的加载按照之前加载数据的形式，使用<code>@ConfigurationProperties</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><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">@Component</span></span><br><span class="line"><span class="meta">@Data</span></span><br><span class="line"><span class="meta">@ConfigurationProperties(prefix = &quot;testcase.book&quot;)</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">BookCase</span> &#123;</span><br><span class="line">    <span class="keyword">private</span> <span class="type">int</span> id;</span><br><span class="line">    <span class="keyword">private</span> <span class="type">int</span> id2;</span><br><span class="line">    <span class="keyword">private</span> <span class="type">int</span> type;</span><br><span class="line">    <span class="keyword">private</span> String name;</span><br><span class="line">    <span class="keyword">private</span> String uuid;</span><br><span class="line">    <span class="keyword">private</span> <span class="type">long</span> publishTime;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>​        对于随机值的产生，还有一些小的限定规则，比如产生的数值性数据可以设置范围等，具体如下：<br><div class="img-wrap"><div class="img-bg"><img class="img" src="https://raw.githubusercontent.com/silvan2077/markdown_pic/main/Picgo/image-20220223135454862.png"/></div></div></p><ul><li>${random.int}表示随机整数</li><li>${random.int(10)}表示10以内的随机数</li><li>${random.int(10,20)}表示10到20的随机数</li><li><strong>其中()可以是任意字符，例如[]，!!均可</strong></li></ul><h2 id="数据层解决方案"><a href="#数据层解决方案" class="headerlink" title="数据层解决方案"></a>数据层解决方案</h2><p>​开发实用篇前三章基本上是开胃菜，从这里开始，不再是单纯的在springboot内部搞事情了，要涉及到很多相关知识。</p><h3 id="SQL"><a href="#SQL" class="headerlink" title="SQL"></a>SQL</h3><p>之前做SSMP整合的时候数据层解决方案涉及到了MySQL数据库与MyBatisPlus框架，后面又学了Druid数据源的配置，所以现在数据层解决方案可以说是Mysql+Druid+MyBatisPlus。而三个技术分别对应了数据层操作的三个层面：</p><ul><li>数据源技术：Druid</li><li>持久化技术：MyBatisPlus</li><li>数据库技术：MySQL</li></ul><h4 id="数据源技术"><a href="#数据源技术" class="headerlink" title="数据源技术"></a>数据源技术</h4><p>目前使用的数据源技术是<code>Druid</code>，运行时可以在日志中看到对应的数据源初始化信息，具体如下：</p><figure class="highlight cmd"><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">INFO <span class="number">28600</span> --- [           main] c.a.d.s.b.a.DruidDataSourceAutoConfigure : Init DruidDataSource</span><br><span class="line">INFO <span class="number">28600</span> --- [           main] com.alibaba.druid.pool.DruidDataSource   : &#123;dataSource-<span class="number">1</span>&#125; inited</span><br></pre></td></tr></table></figure><p>如果不使用Druid数据源，程序运行后是什么样子呢？是独立的数据库连接对象还是有其他的连接池技术支持呢？将Druid技术对应的starter去掉再次运行程序可以在日志中找到如下初始化信息：</p><figure class="highlight cmd"><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">INFO <span class="number">31820</span> --- [           main] com.zaxxer.hikari.HikariDataSource       : HikariPool-<span class="number">1</span> - Starting...</span><br><span class="line">INFO <span class="number">31820</span> --- [           main] com.zaxxer.hikari.HikariDataSource       : HikariPool-<span class="number">1</span> - <span class="built_in">Start</span> completed.</span><br></pre></td></tr></table></figure><p>​此时虽然没有<code>DruidDataSource</code>相关的信息了，但是日志中出现了<code>HikariDataSource</code>这个信息，这就是springboot内嵌数据源。</p><p>​springboot提供了3款内嵌数据源技术，分别如下：</p><ul><li>HikariCP</li><li>Tomcat提供DataSource</li><li>Commons DBCP</li></ul><ul><li>SpringBoot默认使用的数据源是<code>HikartCP</code>，如果不想要<code>HikartCP</code>，且使用Tomcay作为Web服务器进行Web程序的开发，那么可以使用第二种Tomcat提供DataSource，只需要把HikartCP技术的坐标排除掉就可以了。而第三种DBCP的使用条件就更苛刻了，需要将前面两种都排除掉才会默认使用。</li></ul><p>之前配置druid时使用的配置如下：<br><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></pre></td><td class="code"><pre><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">druid:</span></span><br><span class="line">     <span class="attr">url:</span> <span class="string">jdbc:mysql://localhost:3306/ssm_db?serverTimezone=UTC</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">username:</span> <span class="string">root</span></span><br><span class="line">      <span class="attr">password:</span> <span class="string">root</span></span><br></pre></td></tr></table></figure></p><p>换成是默认的数据源HikariCP后，直接吧druid删掉就行了，如下：</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></pre></td><td class="code"><pre><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/ssm_db?serverTimezone=UTC</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">username:</span> <span class="string">root</span></span><br><span class="line">    <span class="attr">password:</span> <span class="string">root</span></span><br></pre></td></tr></table></figure><p>也可以写上是对hikari做的配置，但是url地址要单独配置，如下：</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></pre></td><td class="code"><pre><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/ssm_db?serverTimezone=UTC</span></span><br><span class="line">    <span class="attr">hikari:</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">username:</span> <span class="string">root</span></span><br><span class="line">      <span class="attr">password:</span> <span class="string">root</span></span><br></pre></td></tr></table></figure><p>如果想对hikari做进一步的配置，可以继续配置其独立的属性。例如：<br><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="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/ssm_db?serverTimezone=UTC</span></span><br><span class="line">    <span class="attr">hikari:</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">username:</span> <span class="string">root</span></span><br><span class="line">      <span class="attr">password:</span> <span class="string">root</span></span><br><span class="line">      <span class="attr">maximum-pool-size:</span> <span class="number">50</span></span><br></pre></td></tr></table></figure></p><div class="note info no-icon flat"><p>如果不想使用hikari数据源，使用<code>tomcat的数据源</code>或者<code>DBCP配置格式</code>也是一样的。学习到这里，以后我们做数据层时，数据源对象的选择就不再是单一的使用druid数据源技术了，可以根据需要自行选择。</p></div><h4 id="持久化技术"><a href="#持久化技术" class="headerlink" title="持久化技术"></a>持久化技术</h4><p>springboot给开发者提供了一套现成的数据层技术，叫做<code>JdbcTemplate</code>。其实这个技术不能说是springboot提供的，因为不使用springboot技术，一样能使用它，他是由spring技术提供的，所以在springboot技术范畴中，这个技术也是存在的，毕竟springboot技术是加速spring程序开发而创建的。</p><p>​这个技术其实就是回归到jdbc最原始的编程形式来进行数据层的开发，下面直接上操作步骤：<br><strong>步骤一</strong>：导入jdbc对应的坐标，记得是starter</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.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-jdbc<span class="tag">&lt;/<span class="name">artifactId</span>&gt;</span></span><br><span class="line">&lt;/dependency</span><br></pre></td></tr></table></figure><p><strong>步骤二</strong>：自动装配<code>JdbcTemplate</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></pre></td><td class="code"><pre><span class="line"><span class="meta">@SpringBootTest</span></span><br><span class="line"><span class="keyword">class</span> <span class="title class_">Springboot15SqlApplicationTests</span> &#123;</span><br><span class="line">    <span class="meta">@Test</span></span><br><span class="line">    <span class="keyword">void</span> <span class="title function_">testJdbcTemplate</span><span class="params">(<span class="meta">@Autowired</span> JdbcTemplate jdbcTemplate)</span>&#123;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p><strong>步骤三</strong>：使用JdbcTemplate实现查询操作（非实体类封装数据的查询操作）</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></pre></td><td class="code"><pre><span class="line"><span class="meta">@Test</span></span><br><span class="line"><span class="keyword">void</span> <span class="title function_">testJdbcTemplate</span><span class="params">(<span class="meta">@Autowired</span> JdbcTemplate jdbcTemplate)</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 tbl_book&quot;</span>;</span><br><span class="line">    List&lt;Map&lt;String, Object&gt;&gt; maps = jdbcTemplate.queryForList(sql);</span><br><span class="line">    System.out.println(maps);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p><strong>步骤四</strong>：使用JdbcTemplate实现查询操作（实体类封装数据的查询操作）</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="meta">@Test</span></span><br><span class="line"><span class="keyword">void</span> <span class="title function_">testJdbcTemplate</span><span class="params">(<span class="meta">@Autowired</span> JdbcTemplate jdbcTemplate)</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 tbl_book&quot;</span>;</span><br><span class="line">    RowMapper&lt;Book&gt; rm = <span class="keyword">new</span> <span class="title class_">RowMapper</span>&lt;Book&gt;() &#123;</span><br><span class="line">        <span class="meta">@Override</span></span><br><span class="line">        <span class="keyword">public</span> Book <span class="title function_">mapRow</span><span class="params">(ResultSet rs, <span class="type">int</span> rowNum)</span> <span class="keyword">throws</span> SQLException &#123;</span><br><span class="line">            <span class="type">Book</span> <span class="variable">temp</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">Book</span>();</span><br><span class="line">            temp.setId(rs.getInt(<span class="string">&quot;id&quot;</span>));</span><br><span class="line">            temp.setName(rs.getString(<span class="string">&quot;name&quot;</span>));</span><br><span class="line">            temp.setType(rs.getString(<span class="string">&quot;type&quot;</span>));</span><br><span class="line">            temp.setDescription(rs.getString(<span class="string">&quot;description&quot;</span>));</span><br><span class="line">            <span class="keyword">return</span> temp;</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;;</span><br><span class="line">    List&lt;Book&gt; list = jdbcTemplate.query(sql, rm);</span><br><span class="line">    System.out.println(list);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p><strong>步骤五</strong>：使用JdbcTemplate实现增删改操作</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></pre></td><td class="code"><pre><span class="line"><span class="meta">@Test</span></span><br><span class="line"><span class="keyword">void</span> <span class="title function_">testJdbcTemplateSave</span><span class="params">(<span class="meta">@Autowired</span> JdbcTemplate jdbcTemplate)</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 tbl_book values(3,&#x27;springboot1&#x27;,&#x27;springboot2&#x27;,&#x27;springboot3&#x27;)&quot;</span>;</span><br><span class="line">    jdbcTemplate.update(sql);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>​        如果想对JdbcTemplate对象进行相关配置，可以在yml文件中进行设定，具体如下：</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></pre></td><td class="code"><pre><span class="line"><span class="attr">spring:</span></span><br><span class="line">  <span class="attr">jdbc:</span></span><br><span class="line">    <span class="attr">template:</span></span><br><span class="line">      <span class="attr">query-timeout:</span> <span class="number">-1</span>   <span class="comment"># 查询超时时间</span></span><br><span class="line">      <span class="attr">max-rows:</span> <span class="number">500</span>       <span class="comment"># 最大行数</span></span><br><span class="line">      <span class="attr">fetch-size:</span> <span class="number">-1</span>      <span class="comment"># 缓存行数</span></span><br></pre></td></tr></table></figure><h4 id="数据库技术"><a href="#数据库技术" class="headerlink" title="数据库技术"></a>数据库技术</h4><p>截止到目前，springboot给开发者提供了内置的数据源解决方案和持久化解决方案，在数据层解决方案三件套中还剩下一个数据库，莫非springboot也提供有内置的解决方案？还真有，还不是一个，三个，这一节就来说说内置的数据库解决方案。</p><p>​springboot提供了3款内置的数据库，分别是</p><ul><li>H2</li><li>HSQL</li><li>Derby</li></ul><p>​以上三款数据库除了可以独立安装之外，还可以像tomcat服务器一样，采用内嵌的形式运行在spirngboot容器中。像tomcat一样内嵌在容器中运行，说明他们也是java对象，<strong>这三款数据库的底层就是使用java语言开发的。</strong></p><div class="note info no-icon flat"><p>使用内嵌服务的原因：</p><ul><li>这三款数据库采用内嵌容器的形式运行，在应用程序运行后，如果我们进行测试工作，此时测试的数据无需存储在磁盘上，但是又要测试使用，内嵌数据库就方便了，运行在内存中，该测试测试，该运行运行，等服务器关闭后，一切烟消云散，省去了维护数据库的麻烦</li></ul></div><p>以H2数据库为例讲解如何使用这些内嵌数据库，操作步骤也非常简单</p><p><strong>步骤一</strong>：导入H2数据库对应的坐标，一共2个</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></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.h2database<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>h2<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>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></pre></td></tr></table></figure><p><strong>步骤二</strong>：将工程设置为web工程，启动工程时启动H2数据库</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.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></pre></td></tr></table></figure><p><strong>步骤三</strong>：通过配置开启H2数据库控制台访问程序，也可以使用其他的数据库连接软件操作</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></pre></td><td class="code"><pre><span class="line"><span class="attr">spring:</span></span><br><span class="line">  <span class="attr">h2:</span></span><br><span class="line">    <span class="attr">console:</span></span><br><span class="line">      <span class="attr">enabled:</span> <span class="literal">true</span></span><br><span class="line">      <span class="attr">path:</span> <span class="string">/h2</span></span><br></pre></td></tr></table></figure><p>web端访问路径/h2，访问密码123456，如果访问失败，先配置下列数据源，启动程序运行后再次访问/h2路径就可以正常访问了</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></pre></td><td class="code"><pre><span class="line"><span class="attr">datasource:</span></span><br><span class="line">  <span class="attr">url:</span> <span class="string">jdbc:h2:~/test</span></span><br><span class="line">  <span class="attr">hikari:</span></span><br><span class="line">    <span class="attr">driver-class-name:</span> <span class="string">org.h2.Driver</span></span><br><span class="line">    <span class="attr">username:</span> <span class="string">sa</span></span><br><span class="line">    <span class="attr">password:</span> <span class="number">123456</span></span><br></pre></td></tr></table></figure><p><strong>步骤四</strong>：使用<code>JdbcTemplate</code>或<code>MyBatisPlus</code>技术操作数据库<br>（略）</p><div class="note warning no-icon flat"><ul><li>其实只是换了一个数据库而已，其他的东西都不受影响。需要注意的是如果产品上线，需要把内存级数据库关闭，采用MySQL数据库作为数据持久化方案，关闭方式是设置enabled属性为false。</li></ul></div><p>经过这一通学习，发现SQL相关的数据层可选技术一下多了很多</p><ul><li><code>数据源技术</code>：Druid、Hikari、tomcat DataSource、DBCP</li><li><code>持久化技术</code>：MyBatisPlus、MyBatis、JdbcTemplate</li><li><code>数据库技术</code>：MySQL、H2、HSQL、Derby</li></ul><h3 id="NoSQL"><a href="#NoSQL" class="headerlink" title="NoSQL"></a>NoSQL</h3><ul><li>对于NoSQL，SpringBoot官方文档中提供了十种相关技术的整合方案，接下来对国内最流行的几款NoSQL数据库进行整合，分别是<code>Redis</code>，<code>MongoDB</code>，<code>ES</code><br>​</li><li>对于每种技术在整合前会先说明一下安装和基本使用，然后再讲整合。</li></ul><h4 id="SpringBoot整合Redis"><a href="#SpringBoot整合Redis" class="headerlink" title="SpringBoot整合Redis"></a>SpringBoot整合Redis</h4><ul><li>Redis是一款采用<code>key-value</code>数据存储格式的内存级NoSQL数据库，重点关注<code>数据存储格式</code>，是key-value格式，也就是键值对的存储形式。</li><li>与MySQL数据库不同，MySQL数据库有表、有字段、有记录，Redis没有这些东西，就是一个名称对应一个值，并且数据以存储在<code>内存</code>中使用为主。什么叫以存储在内存中为主？其实Redis有它的数据持久化方案，分别是<code>RDB</code>和<code>AOF</code>，但是Redis自身并不是为了数据持久化而生的，主要是在内存中保存数据，加速数据访问的，所以说是一款内存级数据库。</li><li>Redis支持多种数据存储格式，比如可以直接存字符串，也可以存一个map集合，list集合</li></ul><h5 id="基本操作"><a href="#基本操作" class="headerlink" title="基本操作"></a>基本操作</h5><ul><li><p><strong>启动服务器</strong></p><figure class="highlight cmd"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">redis-server.exe redis.windows.conf</span><br></pre></td></tr></table></figure><p>初学者无需调整服务器对外服务端口，默认6379。</p></li><li><p><strong>启动客户端</strong></p><figure class="highlight cmd"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">redis-cli.exe</span><br></pre></td></tr></table></figure><p>如果启动redis服务器失败，可以先启动客户端，然后执行shutdown操作后退出，此时redis服务器就可以正常执行了。</p></li></ul><ul><li><p>服务器启动后，使用客户端就可以连接服务器，类似于启动完MySQL数据库，然后启动SQL命令行操作数据库。        </p></li><li><p>放置一个字符串数据到redis中，先为数据定义一个名称，比如name,age等，然后使用命令set设置数据到redis服务器中即可</p></li></ul><figure class="highlight cmd"><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="built_in">set</span> name itheima</span><br><span class="line"><span class="built_in">set</span> age <span class="number">12</span></span><br></pre></td></tr></table></figure><ul><li>从redis中取出已经放入的数据，根据名称取，就可以得到对应数据。如果没有对应数据就会得到(null)</li></ul><figure class="highlight cmd"><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">get name</span><br><span class="line">get age</span><br></pre></td></tr></table></figure><ul><li>以上使用的数据存储是一个名称对应一个值，如果要维护的数据过多，可以使用别的数据存储结构。例如hash，它是一种一个名称下可以存储多个数据的存储模型，并且每个数据也可以有自己的二级存储名称。向hash结构中存储数据格式如下：</li></ul><figure class="highlight cmd"><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">hset a a1 aa1#对外key名称是a，在名称为a的存储模型中，a1这个key中保存了数据aa1</span><br><span class="line">hset a a2 aa2</span><br></pre></td></tr></table></figure><ul><li>获取hash结构中的数据命令如下</li></ul><figure class="highlight cmd"><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">hget a a1#得到aa1</span><br><span class="line">hget a a2#得到aa2</span><br></pre></td></tr></table></figure><h5 id="整合"><a href="#整合" class="headerlink" title="整合"></a>整合</h5><ul><li>在进行整合之前先梳理一下整合的思想，springboot整合任何技术其实就是在springboot中使用对应技术的API。如果两个技术没有交集，就不存在整合的概念了。所谓整合其实就是使用springboot技术去管理其他技术，几个问题是躲不掉的。</li></ul><ol><li>需要先导入对应技术的坐标，而整合之后，这些坐标都有了一些变化</li><li>任何技术通常都会有一些相关的设置信息，整合之后，这些信息如何写，写在哪是一个问题</li><li>没有整合之前操作如果是模式A的话，整合之后如果没有给开发者带来一些便捷操作，那整合将毫无意义，所以整合后操作肯定要简化一些，那对应的操作方式自然也有所不同</li></ol><ul><li><p>按照上面的三个问题去思考springboot整合所有技术是一种通用思想，在整合的过程中会逐步摸索出整合的套路，而且适用性非常强，经过若干种技术的整合后基本上可以总结出一套固定思维。</p></li><li><p>下面就开始springboot整合redis，操作步骤如下：</p></li></ul><p><strong>步骤一</strong>：导入springboot整合redis的starter坐标<br>可以在创建模块时勾选，归属在NoSQL中，也可以通过pom.xml导入<br><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.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></pre></td></tr></table></figure></p><p><strong>步骤二</strong>：进行基础配置<br>操作redis，最基本的信息就是操作哪一台redis服务器，所以服务器地址属于基础配置信息，不可缺少。但是即便你不配置，目前也是可以用的。因为以上两组信息<code>host</code>和<code>port</code>都有默认配置，刚好就是上述配置值。<br><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="attr">spring:</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></pre></td></tr></table></figure></p><p>​        </p><p><strong>步骤三</strong>：使用springboot整合redis的专用客户端接口操作，此处使用的是RedisTemplate</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="meta">@SpringBootTest</span></span><br><span class="line"><span class="keyword">class</span> <span class="title class_">Springboot16RedisApplicationTests</span> &#123;</span><br><span class="line">    <span class="meta">@Autowired</span></span><br><span class="line">    <span class="keyword">private</span> RedisTemplate redisTemplate;</span><br><span class="line">    <span class="meta">@Test</span></span><br><span class="line">    <span class="keyword">void</span> <span class="title function_">set</span><span class="params">()</span> &#123;</span><br><span class="line">        <span class="type">ValueOperations</span> <span class="variable">ops</span> <span class="operator">=</span> redisTemplate.opsForValue();</span><br><span class="line">        ops.set(<span class="string">&quot;age&quot;</span>,<span class="number">41</span>);</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="meta">@Test</span></span><br><span class="line">    <span class="keyword">void</span> <span class="title function_">get</span><span class="params">()</span> &#123;</span><br><span class="line">        <span class="type">ValueOperations</span> <span class="variable">ops</span> <span class="operator">=</span> redisTemplate.opsForValue();</span><br><span class="line">        <span class="type">Object</span> <span class="variable">age</span> <span class="operator">=</span> ops.get(<span class="string">&quot;name&quot;</span>);</span><br><span class="line">        System.out.println(age);</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="meta">@Test</span></span><br><span class="line">    <span class="keyword">void</span> <span class="title function_">hset</span><span class="params">()</span> &#123;</span><br><span class="line">        <span class="type">HashOperations</span> <span class="variable">ops</span> <span class="operator">=</span> redisTemplate.opsForHash();</span><br><span class="line">        ops.put(<span class="string">&quot;info&quot;</span>,<span class="string">&quot;b&quot;</span>,<span class="string">&quot;bb&quot;</span>);</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="meta">@Test</span></span><br><span class="line">    <span class="keyword">void</span> <span class="title function_">hget</span><span class="params">()</span> &#123;</span><br><span class="line">        <span class="type">HashOperations</span> <span class="variable">ops</span> <span class="operator">=</span> redisTemplate.opsForHash();</span><br><span class="line">        <span class="type">Object</span> <span class="variable">val</span> <span class="operator">=</span> ops.get(<span class="string">&quot;info&quot;</span>, <span class="string">&quot;b&quot;</span>);</span><br><span class="line">        System.out.println(val);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br></pre></td></tr></table></figure><p>​        在操作redis时，需要先确认操作何种数据，根据数据种类得到操作接口。例如使用opsForValue()获取string类型的数据操作接口，使用opsForHash()获取hash类型的数据操作接口，剩下的就是调用对应api操作了。各种类型的数据操作接口如下：</p><div class="img-wrap"><div class="img-bg"><img class="img" src="https://raw.githubusercontent.com/silvan2077/markdown_pic/main/Picgo/image-20220224103104908.png"/></div></div><ul><li>由于redis内部不提供java对象的存储格式，因此当操作的数据以对象的形式存在时，会进行转码，转换成字符串格式后进行操作。为了方便开发者使用基于字符串为数据的操作，springboot整合redis时提供了专用的API接口StringRedisTemplate，可以理解为这是RedisTemplate的一种指定数据泛型的操作API。</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></pre></td><td class="code"><pre><span class="line"><span class="meta">@SpringBootTest</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">StringRedisTemplateTest</span> &#123;</span><br><span class="line">    <span class="meta">@Autowired</span></span><br><span class="line">    <span class="keyword">private</span> StringRedisTemplate stringRedisTemplate;</span><br><span class="line">    <span class="meta">@Test</span></span><br><span class="line">    <span class="keyword">void</span> <span class="title function_">get</span><span class="params">()</span>&#123;</span><br><span class="line">        ValueOperations&lt;String, String&gt; ops = stringRedisTemplate.opsForValue();</span><br><span class="line">        <span class="type">String</span> <span class="variable">name</span> <span class="operator">=</span> ops.get(<span class="string">&quot;name&quot;</span>);</span><br><span class="line">        System.out.println(name);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p><strong>redis客户端选择</strong></p><ul><li>springboot整合redis技术提供了多种客户端兼容模式，默认提供的是<code>lettucs</code>客户端技术，也可以根据需要切换成指定客户端技术，例如<code>jedis</code>客户端技术，切换成<code>jedis</code>客户端技术操作步骤如下：</li></ul><p><strong>步骤一</strong>：导入jedis坐标</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>redis.clients<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>jedis<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>​        jedis坐标受springboot管理，无需提供版本号</p><p><strong>步骤二</strong>：配置客户端技术类型，设置为<code>jedis</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></pre></td><td class="code"><pre><span class="line"><span class="attr">spring:</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 class="attr">client-type:</span> <span class="string">jedis</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></pre></td><td class="code"><pre><span class="line"><span class="attr">spring:</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 class="attr">client-type:</span> <span class="string">jedis</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">16</span></span><br><span class="line">    <span class="attr">jedis:</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">16</span></span><br></pre></td></tr></table></figure><div class="note warning no-icon flat"><p><strong>lettcus与jedis区别</strong></p><ul><li>jedis连接Redis服务器是<code>直连模式</code>，当多线程模式下使用jedis会存在线程安全问题，解决方案可以通过配置连接池使每个连接专用，这样整体性能就大受影响</li><li>lettcus基于Netty框架进行与Redis服务器连接，底层设计中采用<code>StatefulRedisConnection</code>。 <code>StatefulRedisConnection</code>自身是线程安全的，可以保障并发访问安全问题，所以一个连接可以被多线程复用。当然lettcus也支持多连接实例一起工作</li></ul></div><h4 id="SpringBoot整合MongoDB"><a href="#SpringBoot整合MongoDB" class="headerlink" title="SpringBoot整合MongoDB"></a>SpringBoot整合MongoDB</h4><p>使用Redis技术可以有效的提高数据访问速度，但是由于Redis的数据格式单一性，无法操作结构化数据，当操作对象型的数据时，Redis就显得捉襟见肘。在保障访问速度的情况下，如果想操作结构化数据，此时就需要使用全新的数据存储结束来解决此问题，本节讲解springboot如何整合MongoDB技术。</p><p>MongoDB是一个<code>开源、高性能、无模式</code>的<code>文档型数据库</code>，它是NoSQL数据库产品中的一种，是最像关系型数据库的非关系型数据库。</p><p>什么是无模式呢？简单说就是作为一款数据库，没有固定的数据存储结构，第一条数据可能有A、B、C一共3个字段，第二条数据可能有D、E、F也是3个字段，第三条数据可能是A、C、E3个字段，也就是说数据的结构不固定，这就是无模式。有人会说这有什么用啊？灵活，随时变更，不受约束。基于上述特点，MongoDB的应用面也会产生一些变化。以下列出了一些可以使用MongoDB作为数据存储的场景，但是并不是必须使用MongoDB的场景：</p><ul><li>淘宝用户数据<ul><li>存储位置：数据库</li><li>特征：永久性存储，修改频度极低</li></ul></li><li>游戏装备数据、游戏道具数据<ul><li>存储位置：数据库、Mongodb</li><li>特征：永久性存储与临时存储相结合、修改频度较高</li></ul></li><li>直播数据、打赏数据、粉丝数据<ul><li>存储位置：数据库、Mongodb</li><li>特征：永久性存储与临时存储相结合，修改频度极高</li></ul></li><li>物联网数据<ul><li>存储位置：Mongodb</li><li>特征：临时存储，修改频度飞速</li></ul></li></ul><h5 id="基本操作-1"><a href="#基本操作-1" class="headerlink" title="基本操作"></a>基本操作</h5><p>MongoDB虽然是一款数据库，但是它的操作并不使用SQL语句进行，因此操作方式可能比较陌生，好在有一些类似于Navicat的数据库客户端软件，能够便捷的操作MongoDB，先安装一个客户端，再来操作MongoDB。</p><p>​        同类型的软件较多，本次安装的软件时Robo3t，Robot3t是一款绿色软件，无需安装，解压缩即可。解压缩完毕后进入安装目录双击robot3t.exe即可使用。</p><p><img src="img\image-20220224114911573.png" alt="image-20220224114911573" style="zoom: 33%;" /></p><p>​        打开软件首先要连接MongoDB服务器，选择【File】菜单，选择【Connect…】</p><p><img src="img\image-20220224115202422.png" alt="image-20220224115202422"></p><p>​        进入连接管理界面后，选择左上角的【Create】链接，创建新的连接设置</p><p><img src="img\image-20220224115254200.png" alt="image-20220224115254200" style="zoom:80%;" /></p><p>​        如果输入设置值即可连接（默认不修改即可连接本机27017端口）</p><p><img src="img\image-20220224115300266.png" alt="image-20220224115300266"></p><p>​        连接成功后在命令输入区域输入命令即可操作MongoDB。</p><p>​        创建数据库：在左侧菜单中使用右键创建，输入数据库名称即可</p><p>​        创建集合：在Collections上使用右键创建，输入集合名称即可，集合等同于数据库中的表的作用</p><p>​        新增文档：（文档是一种类似json格式的数据，初学者可以先把数据理解为就是json数据）    </p><figure class="highlight cmd"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">db.集合名称.insert/save/insertOne(文档)</span><br></pre></td></tr></table></figure><p>​        删除文档：</p><figure class="highlight cmd"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">db.集合名称.remove(条件)</span><br></pre></td></tr></table></figure><p>​        修改文档：</p><figure class="highlight cmd"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">db.集合名称.update(条件，&#123;操作种类:&#123;文档&#125;&#125;)</span><br></pre></td></tr></table></figure><p>​        查询文档：</p><figure class="highlight cmd"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><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><br><span class="line">查询全部：   db.集合.<span class="built_in">find</span>();</span><br><span class="line">查第一条：   db.集合.findOne()</span><br><span class="line">查询指定数量文档：db.集合.<span class="built_in">find</span>().limit(<span class="number">10</span>)//查<span class="number">10</span>条文档</span><br><span class="line">跳过指定数量文档：db.集合.<span class="built_in">find</span>().skip(<span class="number">20</span>)//跳过<span class="number">20</span>条文档</span><br><span class="line">统计：  db.集合.count()</span><br><span class="line">排序：db.集合.<span class="built_in">sort</span>(&#123;age:<span class="number">1</span>&#125;)//按age升序排序</span><br><span class="line">投影：db.集合名称.<span class="built_in">find</span>(条件,&#123;name:<span class="number">1</span>,age:<span class="number">1</span>&#125;) //仅保留name与age域</span><br><span class="line"></span><br><span class="line">条件查询</span><br><span class="line">基本格式：db.集合.<span class="built_in">find</span>(&#123;条件&#125;)</span><br><span class="line">模糊查询：db.集合.<span class="built_in">find</span>(&#123;域名:/正则表达式/&#125;)  //等同SQL中的like，比like强大，可以执行正则所有规则</span><br><span class="line">条件比较运算：   db.集合.<span class="built_in">find</span>(&#123;域名:&#123;$gt:值&#125;&#125;)//等同SQL中的数值比较操作，例如：name&gt;<span class="number">18</span></span><br><span class="line">包含查询：db.集合.<span class="built_in">find</span>(&#123;域名:&#123;$<span class="keyword">in</span>:[值<span class="number">1</span>，值<span class="number">2</span>]&#125;&#125;)//等同于SQL中的<span class="keyword">in</span></span><br><span class="line">条件连接查询：   db.集合.<span class="built_in">find</span>(&#123;$and:[&#123;条件<span class="number">1</span>&#125;,&#123;条件<span class="number">2</span>&#125;]&#125;)   //等同于SQL中的and、or</span><br></pre></td></tr></table></figure><p>​        有关MongoDB的基础操作就普及到这里，需要全面掌握MongoDB技术，请参看相关教程学习。</p><h5 id="整合-1"><a href="#整合-1" class="headerlink" title="整合"></a>整合</h5><p>​        使用springboot整合MongDB该如何进行呢？其实springboot为什么使用的开发者这么多，就是因为他的套路几乎完全一样。导入坐标，做配置，使用API接口操作。整合Redis如此，整合MongoDB同样如此。</p><p>​        第一，先导入对应技术的整合starter坐标</p><p>​        第二，配置必要信息</p><p>​        第三，使用提供的API操作即可</p><p>​        下面就开始springboot整合MongoDB，操作步骤如下：</p><p><strong>步骤①</strong>：导入springboot整合MongoDB的starter坐标</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.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-mongodb<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>​        上述坐标也可以在创建模块的时候通过勾选的形式进行选择，同样归属NoSQL分类中</p><p><img src="img\image-20220224120721626.png" alt="image-20220224120721626" style="zoom: 67%;" /></p><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="attr">spring:</span></span><br><span class="line">  <span class="attr">data:</span></span><br><span class="line">    <span class="attr">mongodb:</span></span><br><span class="line">      <span class="attr">uri:</span> <span class="string">mongodb://localhost/itheima</span></span><br></pre></td></tr></table></figure><p>​        操作MongoDB需要的配置与操作redis一样，最基本的信息都是操作哪一台服务器，区别就是连接的服务器IP地址和端口不同，书写格式不同而已。</p><p><strong>步骤③</strong>：使用springboot整合MongoDB的专用客户端接口MongoTemplate来进行操作</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></pre></td><td class="code"><pre><span class="line"><span class="meta">@SpringBootTest</span></span><br><span class="line"><span class="keyword">class</span> <span class="title class_">Springboot17MongodbApplicationTests</span> &#123;</span><br><span class="line">    <span class="meta">@Autowired</span></span><br><span class="line">    <span class="keyword">private</span> MongoTemplate mongoTemplate;</span><br><span class="line">    <span class="meta">@Test</span></span><br><span class="line">    <span class="keyword">void</span> <span class="title function_">contextLoads</span><span class="params">()</span> &#123;</span><br><span class="line">        <span class="type">Book</span> <span class="variable">book</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">Book</span>();</span><br><span class="line">        book.setId(<span class="number">2</span>);</span><br><span class="line">        book.setName(<span class="string">&quot;springboot2&quot;</span>);</span><br><span class="line">        book.setType(<span class="string">&quot;springboot2&quot;</span>);</span><br><span class="line">        book.setDescription(<span class="string">&quot;springboot2&quot;</span>);</span><br><span class="line">        mongoTemplate.save(book);</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="meta">@Test</span></span><br><span class="line">    <span class="keyword">void</span> <span class="title function_">find</span><span class="params">()</span>&#123;</span><br><span class="line">        List&lt;Book&gt; all = mongoTemplate.findAll(Book.class);</span><br><span class="line">        System.out.println(all);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>​        整合工作到这里就做完了，感觉既熟悉也陌生。熟悉的是这个套路，三板斧，就这三招，导坐标做配置用API操作，陌生的是这个技术，里面具体的操作API可能会不熟悉，有关springboot整合MongoDB我们就讲到这里。有兴趣可以继续学习MongoDB的操作，然后再来这里通过编程的形式操作MongoDB。</p><p><strong>总结</strong></p><ol><li>springboot整合MongoDB步骤<ol><li>导入springboot整合MongoDB的starter坐标</li><li>进行基础配置</li><li>使用springboot整合MongoDB的专用客户端接口MongoTemplate操作</li></ol></li></ol><h4 id="SpringBoot整合ES"><a href="#SpringBoot整合ES" class="headerlink" title="SpringBoot整合ES"></a>SpringBoot整合ES</h4><p>​        NoSQL解决方案已经讲完了两种技术的整合了，Redis可以使用内存加载数据并实现数据快速访问，MongoDB可以在内存中存储类似对象的数据并实现数据的快速访问，在企业级开发中对于速度的追求是永无止境的。下面要讲的内容也是一款NoSQL解决方案，只不过他的作用不是为了直接加速数据的读写，而是加速数据的查询的，叫做ES技术。</p><p>​        ES（Elasticsearch）是一个分布式全文搜索引擎，重点是全文搜索。</p><p>​        那什么是全文搜索呢？比如用户要买一本书，以Java为关键字进行搜索，不管是书名中还是书的介绍中，甚至是书的作者名字，只要包含java就作为查询结果返回给用户查看，上述过程就使用了全文搜索技术。搜索的条件不再是仅用于对某一个字段进行比对，而是在一条数据中使用搜索条件去比对更多的字段，只要能匹配上就列入查询结果，这就是全文搜索的目的。而ES技术就是一种可以实现上述效果的技术。</p><p>​        要实现全文搜索的效果，不可能使用数据库中like操作去进行比对，这种效率太低了。ES设计了一种全新的思想，来实现全文搜索。具体操作过程如下：</p><ol><li><p>将被查询的字段的数据全部文本信息进行查分，分成若干个词</p><ul><li>例如“中华人民共和国”就会被拆分成三个词，分别是“中华”、“人民”、“共和国”，此过程有专业术语叫做分词。分词的策略不同，分出的效果不一样，不同的分词策略称为分词器。</li></ul></li><li><p>将分词得到的结果存储起来，对应每条数据的id</p><ul><li><p>例如id为1的数据中名称这一项的值是“中华人民共和国”，那么分词结束后，就会出现“中华”对应id为1，“人民”对应id为1，“共和国”对应id为1</p></li><li><p>例如id为2的数据中名称这一项的值是“人民代表大会“，那么分词结束后，就会出现“人民”对应id为2，“代表”对应id为2，“大会”对应id为2</p></li><li><p>此时就会出现如下对应结果，按照上述形式可以对所有文档进行分词。需要注意分词的过程不是仅对一个字段进行，而是对每一个参与查询的字段都执行，最终结果汇总到一个表格中</p><p>| 分词结果关键字 | 对应id |<br>| ——————— | ——— |<br>| 中华           | 1      |<br>| 人民           | 1,2    |<br>| 共和国         | 1      |<br>| 代表           | 2      |<br>| 大会           | 2      |</p></li></ul></li><li><p>当进行查询时，如果输入“人民”作为查询条件，可以通过上述表格数据进行比对，得到id值1,2，然后根据id值就可以得到查询的结果数据了。</p></li></ol><p>​        上述过程中分词结果关键字内容每一个都不相同，作用有点类似于数据库中的索引，是用来加速数据查询的。但是数据库中的索引是对某一个字段进行添加索引，而这里的分词结果关键字不是一个完整的字段值，只是一个字段中的其中的一部分内容。并且索引使用时是根据索引内容查找整条数据，全文搜索中的分词结果关键字查询后得到的并不是整条的数据，而是数据的id，要想获得具体数据还要再次查询，因此这里为这种分词结果关键字起了一个全新的名称，叫做<strong>倒排索引</strong>。</p><p>​        通过上述内容的学习，发现使用ES其实准备工作还是挺多的，必须先建立文档的倒排索引，然后才能继续使用。快速了解一下ES的工作原理，下面直接开始我们的学习，老规矩，先安装，再操作，最后说整合。</p><h5 id="安装"><a href="#安装" class="headerlink" title="安装"></a>安装</h5><p>​        windows版安装包下载地址：<a href="https://www.elastic.co/cn/downloads/elasticsearch">https://</a><a href="https://www.elastic.co/cn/downloads/elasticsearch">www.elastic.co/cn/downloads/elasticsearch</a></p><p>​        下载的安装包是解压缩就能使用的zip文件，解压缩完毕后会得到如下文件</p><p><img src="img\image-20220225132756400.png" alt="image-20220225132756400"></p><ul><li>bin目录：包含所有的可执行命令</li><li>config目录：包含ES服务器使用的配置文件</li><li>jdk目录：此目录中包含了一个完整的jdk工具包，版本17，当ES升级时，使用最新版本的jdk确保不会出现版本支持性不足的问题</li><li>lib目录：包含ES运行的依赖jar文件</li><li>logs目录：包含ES运行后产生的所有日志文件</li><li>modules目录：包含ES软件中所有的功能模块，也是一个一个的jar包。和jar目录不同，jar目录是ES运行期间依赖的jar包，modules是ES软件自己的功能jar包</li><li>plugins目录：包含ES软件安装的插件，默认为空</li></ul><p><strong>启动服务器</strong></p><figure class="highlight cmd"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">elasticsearch.bat</span><br></pre></td></tr></table></figure><p>​        双击elasticsearch.bat文件即可启动ES服务器，默认服务端口9200。通过浏览器访问<a href="http://localhost:9200看到如下信息视为ES服务器正常启动">http://localhost:9200看到如下信息视为ES服务器正常启动</a></p><figure class="highlight cmd"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><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">&#123;</span><br><span class="line">  &quot;name&quot; : &quot;CZBK-**********&quot;,</span><br><span class="line">  &quot;cluster_name&quot; : &quot;elasticsearch&quot;,</span><br><span class="line">  &quot;cluster_uuid&quot; : &quot;j137DSswTPG8U4Yb-<span class="number">0</span>T1Mg&quot;,</span><br><span class="line">  &quot;version&quot; : &#123;</span><br><span class="line">    &quot;number&quot; : &quot;<span class="number">7</span>.<span class="number">16</span>.<span class="number">2</span>&quot;,</span><br><span class="line">    &quot;build_flavor&quot; : &quot;default&quot;,</span><br><span class="line">    &quot;build_type&quot; : &quot;zip&quot;,</span><br><span class="line">    &quot;build_hash&quot; : &quot;<span class="number">2</span>b937c44140b6559905130a8650c64dbd0879cfb&quot;,</span><br><span class="line">    &quot;build_date&quot; : &quot;<span class="number">2021</span>-<span class="number">12</span>-<span class="number">18</span>T19:<span class="number">42</span>:<span class="number">46</span>.<span class="number">604893745</span>Z&quot;,</span><br><span class="line">    &quot;build_snapshot&quot; : false,</span><br><span class="line">    &quot;lucene_version&quot; : &quot;<span class="number">8</span>.<span class="number">10</span>.<span class="number">1</span>&quot;,</span><br><span class="line">    &quot;minimum_wire_compatibility_version&quot; : &quot;<span class="number">6</span>.<span class="number">8</span>.<span class="number">0</span>&quot;,</span><br><span class="line">    &quot;minimum_index_compatibility_version&quot; : &quot;<span class="number">6</span>.<span class="number">0</span>.<span class="number">0</span>-beta1&quot;</span><br><span class="line">  &#125;,</span><br><span class="line">  &quot;tagline&quot; : &quot;You Know, <span class="keyword">for</span> Search&quot;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h5 id="基本操作-2"><a href="#基本操作-2" class="headerlink" title="基本操作"></a>基本操作</h5><p>​        ES中保存有我们要查询的数据，只不过格式和数据库存储数据格式不同而已。在ES中我们要先创建倒排索引，这个索引的功能又点类似于数据库的表，然后将数据添加到倒排索引中，添加的数据称为文档。所以要进行ES的操作要先创建索引，再添加文档，这样才能进行后续的查询操作。</p><p>​        要操作ES可以通过Rest风格的请求来进行，也就是说发送一个请求就可以执行一个操作。比如新建索引，删除索引这些操作都可以使用发送请求的形式来进行。</p><ul><li><p>创建索引，books是索引名称，下同</p><figure class="highlight cmd"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">PUT请求http://localhost:<span class="number">9200</span>/books</span><br></pre></td></tr></table></figure><p>发送请求后，看到如下信息即索引创建成功</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></pre></td><td class="code"><pre><span class="line"><span class="punctuation">&#123;</span></span><br><span class="line">    <span class="attr">&quot;acknowledged&quot;</span><span class="punctuation">:</span> <span class="literal"><span class="keyword">true</span></span><span class="punctuation">,</span></span><br><span class="line">    <span class="attr">&quot;shards_acknowledged&quot;</span><span class="punctuation">:</span> <span class="literal"><span class="keyword">true</span></span><span class="punctuation">,</span></span><br><span class="line">    <span class="attr">&quot;index&quot;</span><span class="punctuation">:</span> <span class="string">&quot;books&quot;</span></span><br><span class="line"><span class="punctuation">&#125;</span></span><br></pre></td></tr></table></figure><p>重复创建已经存在的索引会出现错误信息，reason属性中描述错误原因</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><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><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="punctuation">&#123;</span></span><br><span class="line">    <span class="attr">&quot;error&quot;</span><span class="punctuation">:</span> <span class="punctuation">&#123;</span></span><br><span class="line">        <span class="attr">&quot;root_cause&quot;</span><span class="punctuation">:</span> <span class="punctuation">[</span></span><br><span class="line">            <span class="punctuation">&#123;</span></span><br><span class="line">                <span class="attr">&quot;type&quot;</span><span class="punctuation">:</span> <span class="string">&quot;resource_already_exists_exception&quot;</span><span class="punctuation">,</span></span><br><span class="line">                <span class="attr">&quot;reason&quot;</span><span class="punctuation">:</span> <span class="string">&quot;index [books/VgC_XMVAQmedaiBNSgO2-w] already exists&quot;</span><span class="punctuation">,</span></span><br><span class="line">                <span class="attr">&quot;index_uuid&quot;</span><span class="punctuation">:</span> <span class="string">&quot;VgC_XMVAQmedaiBNSgO2-w&quot;</span><span class="punctuation">,</span></span><br><span class="line">                <span class="attr">&quot;index&quot;</span><span class="punctuation">:</span> <span class="string">&quot;books&quot;</span></span><br><span class="line">            <span class="punctuation">&#125;</span></span><br><span class="line">        <span class="punctuation">]</span><span class="punctuation">,</span></span><br><span class="line">        <span class="attr">&quot;type&quot;</span><span class="punctuation">:</span> <span class="string">&quot;resource_already_exists_exception&quot;</span><span class="punctuation">,</span></span><br><span class="line">        <span class="attr">&quot;reason&quot;</span><span class="punctuation">:</span> <span class="string">&quot;index [books/VgC_XMVAQmedaiBNSgO2-w] already exists&quot;</span><span class="punctuation">,</span># books索引已经存在</span><br><span class="line">        <span class="attr">&quot;index_uuid&quot;</span><span class="punctuation">:</span> <span class="string">&quot;VgC_XMVAQmedaiBNSgO2-w&quot;</span><span class="punctuation">,</span></span><br><span class="line">        <span class="attr">&quot;index&quot;</span><span class="punctuation">:</span> <span class="string">&quot;book&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;status&quot;</span><span class="punctuation">:</span> <span class="number">400</span></span><br><span class="line"><span class="punctuation">&#125;</span></span><br></pre></td></tr></table></figure></li><li><p>查询索引</p><figure class="highlight cmd"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">GET请求http://localhost:<span class="number">9200</span>/books</span><br></pre></td></tr></table></figure><p>查询索引得到索引相关信息，如下</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><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><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="punctuation">&#123;</span></span><br><span class="line">    <span class="attr">&quot;book&quot;</span><span class="punctuation">:</span> <span class="punctuation">&#123;</span></span><br><span class="line">        <span class="attr">&quot;aliases&quot;</span><span class="punctuation">:</span> <span class="punctuation">&#123;</span><span class="punctuation">&#125;</span><span class="punctuation">,</span></span><br><span class="line">        <span class="attr">&quot;mappings&quot;</span><span class="punctuation">:</span> <span class="punctuation">&#123;</span><span class="punctuation">&#125;</span><span class="punctuation">,</span></span><br><span class="line">        <span class="attr">&quot;settings&quot;</span><span class="punctuation">:</span> <span class="punctuation">&#123;</span></span><br><span class="line">            <span class="attr">&quot;index&quot;</span><span class="punctuation">:</span> <span class="punctuation">&#123;</span></span><br><span class="line">                <span class="attr">&quot;routing&quot;</span><span class="punctuation">:</span> <span class="punctuation">&#123;</span></span><br><span class="line">                    <span class="attr">&quot;allocation&quot;</span><span class="punctuation">:</span> <span class="punctuation">&#123;</span></span><br><span class="line">                        <span class="attr">&quot;include&quot;</span><span class="punctuation">:</span> <span class="punctuation">&#123;</span></span><br><span class="line">                            <span class="attr">&quot;_tier_preference&quot;</span><span class="punctuation">:</span> <span class="string">&quot;data_content&quot;</span></span><br><span class="line">                        <span class="punctuation">&#125;</span></span><br><span class="line">                    <span class="punctuation">&#125;</span></span><br><span class="line">                <span class="punctuation">&#125;</span><span class="punctuation">,</span></span><br><span class="line">                <span class="attr">&quot;number_of_shards&quot;</span><span class="punctuation">:</span> <span class="string">&quot;1&quot;</span><span class="punctuation">,</span></span><br><span class="line">                <span class="attr">&quot;provided_name&quot;</span><span class="punctuation">:</span> <span class="string">&quot;books&quot;</span><span class="punctuation">,</span></span><br><span class="line">                <span class="attr">&quot;creation_date&quot;</span><span class="punctuation">:</span> <span class="string">&quot;1645768584849&quot;</span><span class="punctuation">,</span></span><br><span class="line">                <span class="attr">&quot;number_of_replicas&quot;</span><span class="punctuation">:</span> <span class="string">&quot;1&quot;</span><span class="punctuation">,</span></span><br><span class="line">                <span class="attr">&quot;uuid&quot;</span><span class="punctuation">:</span> <span class="string">&quot;VgC_XMVAQmedaiBNSgO2-w&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;created&quot;</span><span class="punctuation">:</span> <span class="string">&quot;7160299&quot;</span></span><br><span class="line">                <span class="punctuation">&#125;</span></span><br><span class="line">            <span class="punctuation">&#125;</span></span><br><span class="line">        <span class="punctuation">&#125;</span></span><br><span class="line">    <span class="punctuation">&#125;</span></span><br><span class="line"><span class="punctuation">&#125;</span></span><br></pre></td></tr></table></figure><p>如果查询了不存在的索引，会返回错误信息，例如查询名称为book的索引后信息如下</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><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><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="punctuation">&#123;</span></span><br><span class="line">    <span class="attr">&quot;error&quot;</span><span class="punctuation">:</span> <span class="punctuation">&#123;</span></span><br><span class="line">        <span class="attr">&quot;root_cause&quot;</span><span class="punctuation">:</span> <span class="punctuation">[</span></span><br><span class="line">            <span class="punctuation">&#123;</span></span><br><span class="line">                <span class="attr">&quot;type&quot;</span><span class="punctuation">:</span> <span class="string">&quot;index_not_found_exception&quot;</span><span class="punctuation">,</span></span><br><span class="line">                <span class="attr">&quot;reason&quot;</span><span class="punctuation">:</span> <span class="string">&quot;no such index [book]&quot;</span><span class="punctuation">,</span></span><br><span class="line">                <span class="attr">&quot;resource.type&quot;</span><span class="punctuation">:</span> <span class="string">&quot;index_or_alias&quot;</span><span class="punctuation">,</span></span><br><span class="line">                <span class="attr">&quot;resource.id&quot;</span><span class="punctuation">:</span> <span class="string">&quot;book&quot;</span><span class="punctuation">,</span></span><br><span class="line">                <span class="attr">&quot;index_uuid&quot;</span><span class="punctuation">:</span> <span class="string">&quot;_na_&quot;</span><span class="punctuation">,</span></span><br><span class="line">                <span class="attr">&quot;index&quot;</span><span class="punctuation">:</span> <span class="string">&quot;book&quot;</span></span><br><span class="line">            <span class="punctuation">&#125;</span></span><br><span class="line">        <span class="punctuation">]</span><span class="punctuation">,</span></span><br><span class="line">        <span class="attr">&quot;type&quot;</span><span class="punctuation">:</span> <span class="string">&quot;index_not_found_exception&quot;</span><span class="punctuation">,</span></span><br><span class="line">        <span class="attr">&quot;reason&quot;</span><span class="punctuation">:</span> <span class="string">&quot;no such index [book]&quot;</span><span class="punctuation">,</span># 没有book索引</span><br><span class="line">        <span class="attr">&quot;resource.type&quot;</span><span class="punctuation">:</span> <span class="string">&quot;index_or_alias&quot;</span><span class="punctuation">,</span></span><br><span class="line">        <span class="attr">&quot;resource.id&quot;</span><span class="punctuation">:</span> <span class="string">&quot;book&quot;</span><span class="punctuation">,</span></span><br><span class="line">        <span class="attr">&quot;index_uuid&quot;</span><span class="punctuation">:</span> <span class="string">&quot;_na_&quot;</span><span class="punctuation">,</span></span><br><span class="line">        <span class="attr">&quot;index&quot;</span><span class="punctuation">:</span> <span class="string">&quot;book&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;status&quot;</span><span class="punctuation">:</span> <span class="number">404</span></span><br><span class="line"><span class="punctuation">&#125;</span></span><br></pre></td></tr></table></figure></li><li><p>删除索引</p><figure class="highlight cmd"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">DELETE请求http://localhost:<span class="number">9200</span>/books</span><br></pre></td></tr></table></figure><p>删除所有后，给出删除结果</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></pre></td><td class="code"><pre><span class="line"><span class="punctuation">&#123;</span></span><br><span class="line">    <span class="attr">&quot;acknowledged&quot;</span><span class="punctuation">:</span> <span class="literal"><span class="keyword">true</span></span></span><br><span class="line"><span class="punctuation">&#125;</span></span><br></pre></td></tr></table></figure><p>如果重复删除，会给出错误信息，同样在reason属性中描述具体的错误原因</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><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><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="punctuation">&#123;</span></span><br><span class="line">    <span class="attr">&quot;error&quot;</span><span class="punctuation">:</span> <span class="punctuation">&#123;</span></span><br><span class="line">        <span class="attr">&quot;root_cause&quot;</span><span class="punctuation">:</span> <span class="punctuation">[</span></span><br><span class="line">            <span class="punctuation">&#123;</span></span><br><span class="line">                <span class="attr">&quot;type&quot;</span><span class="punctuation">:</span> <span class="string">&quot;index_not_found_exception&quot;</span><span class="punctuation">,</span></span><br><span class="line">                <span class="attr">&quot;reason&quot;</span><span class="punctuation">:</span> <span class="string">&quot;no such index [books]&quot;</span><span class="punctuation">,</span></span><br><span class="line">                <span class="attr">&quot;resource.type&quot;</span><span class="punctuation">:</span> <span class="string">&quot;index_or_alias&quot;</span><span class="punctuation">,</span></span><br><span class="line">                <span class="attr">&quot;resource.id&quot;</span><span class="punctuation">:</span> <span class="string">&quot;book&quot;</span><span class="punctuation">,</span></span><br><span class="line">                <span class="attr">&quot;index_uuid&quot;</span><span class="punctuation">:</span> <span class="string">&quot;_na_&quot;</span><span class="punctuation">,</span></span><br><span class="line">                <span class="attr">&quot;index&quot;</span><span class="punctuation">:</span> <span class="string">&quot;book&quot;</span></span><br><span class="line">            <span class="punctuation">&#125;</span></span><br><span class="line">        <span class="punctuation">]</span><span class="punctuation">,</span></span><br><span class="line">        <span class="attr">&quot;type&quot;</span><span class="punctuation">:</span> <span class="string">&quot;index_not_found_exception&quot;</span><span class="punctuation">,</span></span><br><span class="line">        <span class="attr">&quot;reason&quot;</span><span class="punctuation">:</span> <span class="string">&quot;no such index [books]&quot;</span><span class="punctuation">,</span># 没有books索引</span><br><span class="line">        <span class="attr">&quot;resource.type&quot;</span><span class="punctuation">:</span> <span class="string">&quot;index_or_alias&quot;</span><span class="punctuation">,</span></span><br><span class="line">        <span class="attr">&quot;resource.id&quot;</span><span class="punctuation">:</span> <span class="string">&quot;book&quot;</span><span class="punctuation">,</span></span><br><span class="line">        <span class="attr">&quot;index_uuid&quot;</span><span class="punctuation">:</span> <span class="string">&quot;_na_&quot;</span><span class="punctuation">,</span></span><br><span class="line">        <span class="attr">&quot;index&quot;</span><span class="punctuation">:</span> <span class="string">&quot;book&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;status&quot;</span><span class="punctuation">:</span> <span class="number">404</span></span><br><span class="line"><span class="punctuation">&#125;</span></span><br></pre></td></tr></table></figure></li><li><p>创建索引并指定分词器</p><p>​        前面创建的索引是未指定分词器的，可以在创建索引时添加请求参数，设置分词器。目前国内较为流行的分词器是IK分词器，使用前先在下对应的分词器，然后使用。IK分词器下载地址：<a href="https://github.com/medcl/elasticsearch-analysis-ik/releases">https://github.com/medcl/elasticsearch-analysis-ik/releases</a></p><p>​        分词器下载后解压到ES安装目录的plugins目录中即可，安装分词器后需要重新启动ES服务器。使用IK分词器创建索引格式：</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><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><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">PUT请求http<span class="punctuation">:</span><span class="comment">//localhost:9200/books</span></span><br><span class="line"></span><br><span class="line">请求参数如下（注意是json格式的参数）</span><br><span class="line"><span class="punctuation">&#123;</span></span><br><span class="line">    <span class="attr">&quot;mappings&quot;</span><span class="punctuation">:</span><span class="punctuation">&#123;</span>#定义mappings属性，替换创建索引时对应的mappings属性</span><br><span class="line">        <span class="attr">&quot;properties&quot;</span><span class="punctuation">:</span><span class="punctuation">&#123;</span>#定义索引中包含的属性设置</span><br><span class="line">            <span class="attr">&quot;id&quot;</span><span class="punctuation">:</span><span class="punctuation">&#123;</span>#设置索引中包含id属性</span><br><span class="line">                <span class="attr">&quot;type&quot;</span><span class="punctuation">:</span><span class="string">&quot;keyword&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;name&quot;</span><span class="punctuation">:</span><span class="punctuation">&#123;</span>#设置索引中包含name属性</span><br><span class="line">                <span class="attr">&quot;type&quot;</span><span class="punctuation">:</span><span class="string">&quot;text&quot;</span><span class="punctuation">,</span>              #当前属性是文本信息，参与分词  </span><br><span class="line">                <span class="attr">&quot;analyzer&quot;</span><span class="punctuation">:</span><span class="string">&quot;ik_max_word&quot;</span><span class="punctuation">,</span>   #使用IK分词器进行分词             </span><br><span class="line">                <span class="attr">&quot;copy_to&quot;</span><span class="punctuation">:</span><span class="string">&quot;all&quot;</span>#分词结果拷贝到all属性中</span><br><span class="line">            <span class="punctuation">&#125;</span><span class="punctuation">,</span></span><br><span class="line">            <span class="attr">&quot;type&quot;</span><span class="punctuation">:</span><span class="punctuation">&#123;</span></span><br><span class="line">                <span class="attr">&quot;type&quot;</span><span class="punctuation">:</span><span class="string">&quot;keyword&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;description&quot;</span><span class="punctuation">:</span><span class="punctuation">&#123;</span></span><br><span class="line">                <span class="attr">&quot;type&quot;</span><span class="punctuation">:</span><span class="string">&quot;text&quot;</span><span class="punctuation">,</span>                </span><br><span class="line">                <span class="attr">&quot;analyzer&quot;</span><span class="punctuation">:</span><span class="string">&quot;ik_max_word&quot;</span><span class="punctuation">,</span>                </span><br><span class="line">                <span class="attr">&quot;copy_to&quot;</span><span class="punctuation">:</span><span class="string">&quot;all&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;all&quot;</span><span class="punctuation">:</span><span class="punctuation">&#123;</span>#定义属性，用来描述多个字段的分词结果集合，当前属性可以参与查询</span><br><span class="line">                <span class="attr">&quot;type&quot;</span><span class="punctuation">:</span><span class="string">&quot;text&quot;</span><span class="punctuation">,</span>                </span><br><span class="line">                <span class="attr">&quot;analyzer&quot;</span><span class="punctuation">:</span><span class="string">&quot;ik_max_word&quot;</span></span><br><span class="line">            <span class="punctuation">&#125;</span></span><br><span class="line">        <span class="punctuation">&#125;</span></span><br><span class="line">    <span class="punctuation">&#125;</span></span><br><span class="line"><span class="punctuation">&#125;</span></span><br></pre></td></tr></table></figure><p>​        创建完毕后返回结果和不使用分词器创建索引的结果是一样的，此时可以通过查看索引信息观察到添加的请求参数mappings已经进入到了索引属性中</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><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><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="punctuation">&#123;</span></span><br><span class="line">    <span class="attr">&quot;books&quot;</span><span class="punctuation">:</span> <span class="punctuation">&#123;</span></span><br><span class="line">        <span class="attr">&quot;aliases&quot;</span><span class="punctuation">:</span> <span class="punctuation">&#123;</span><span class="punctuation">&#125;</span><span class="punctuation">,</span></span><br><span class="line">        <span class="attr">&quot;mappings&quot;</span><span class="punctuation">:</span> <span class="punctuation">&#123;</span>#mappings属性已经被替换</span><br><span class="line">            <span class="attr">&quot;properties&quot;</span><span class="punctuation">:</span> <span class="punctuation">&#123;</span></span><br><span class="line">                <span class="attr">&quot;all&quot;</span><span class="punctuation">:</span> <span class="punctuation">&#123;</span></span><br><span class="line">                    <span class="attr">&quot;type&quot;</span><span class="punctuation">:</span> <span class="string">&quot;text&quot;</span><span class="punctuation">,</span></span><br><span class="line">                    <span class="attr">&quot;analyzer&quot;</span><span class="punctuation">:</span> <span class="string">&quot;ik_max_word&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;description&quot;</span><span class="punctuation">:</span> <span class="punctuation">&#123;</span></span><br><span class="line">                    <span class="attr">&quot;type&quot;</span><span class="punctuation">:</span> <span class="string">&quot;text&quot;</span><span class="punctuation">,</span></span><br><span class="line">                    <span class="attr">&quot;copy_to&quot;</span><span class="punctuation">:</span> <span class="punctuation">[</span></span><br><span class="line">                        <span class="string">&quot;all&quot;</span></span><br><span class="line">                    <span class="punctuation">]</span><span class="punctuation">,</span></span><br><span class="line">                    <span class="attr">&quot;analyzer&quot;</span><span class="punctuation">:</span> <span class="string">&quot;ik_max_word&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;id&quot;</span><span class="punctuation">:</span> <span class="punctuation">&#123;</span></span><br><span class="line">                    <span class="attr">&quot;type&quot;</span><span class="punctuation">:</span> <span class="string">&quot;keyword&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;name&quot;</span><span class="punctuation">:</span> <span class="punctuation">&#123;</span></span><br><span class="line">                    <span class="attr">&quot;type&quot;</span><span class="punctuation">:</span> <span class="string">&quot;text&quot;</span><span class="punctuation">,</span></span><br><span class="line">                    <span class="attr">&quot;copy_to&quot;</span><span class="punctuation">:</span> <span class="punctuation">[</span></span><br><span class="line">                        <span class="string">&quot;all&quot;</span></span><br><span class="line">                    <span class="punctuation">]</span><span class="punctuation">,</span></span><br><span class="line">                    <span class="attr">&quot;analyzer&quot;</span><span class="punctuation">:</span> <span class="string">&quot;ik_max_word&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;type&quot;</span><span class="punctuation">:</span> <span class="punctuation">&#123;</span></span><br><span class="line">                    <span class="attr">&quot;type&quot;</span><span class="punctuation">:</span> <span class="string">&quot;keyword&quot;</span></span><br><span class="line">                <span class="punctuation">&#125;</span></span><br><span class="line">            <span class="punctuation">&#125;</span></span><br><span class="line">        <span class="punctuation">&#125;</span><span class="punctuation">,</span></span><br><span class="line">        <span class="attr">&quot;settings&quot;</span><span class="punctuation">:</span> <span class="punctuation">&#123;</span></span><br><span class="line">            <span class="attr">&quot;index&quot;</span><span class="punctuation">:</span> <span class="punctuation">&#123;</span></span><br><span class="line">                <span class="attr">&quot;routing&quot;</span><span class="punctuation">:</span> <span class="punctuation">&#123;</span></span><br><span class="line">                    <span class="attr">&quot;allocation&quot;</span><span class="punctuation">:</span> <span class="punctuation">&#123;</span></span><br><span class="line">                        <span class="attr">&quot;include&quot;</span><span class="punctuation">:</span> <span class="punctuation">&#123;</span></span><br><span class="line">                            <span class="attr">&quot;_tier_preference&quot;</span><span class="punctuation">:</span> <span class="string">&quot;data_content&quot;</span></span><br><span class="line">                        <span class="punctuation">&#125;</span></span><br><span class="line">                    <span class="punctuation">&#125;</span></span><br><span class="line">                <span class="punctuation">&#125;</span><span class="punctuation">,</span></span><br><span class="line">                <span class="attr">&quot;number_of_shards&quot;</span><span class="punctuation">:</span> <span class="string">&quot;1&quot;</span><span class="punctuation">,</span></span><br><span class="line">                <span class="attr">&quot;provided_name&quot;</span><span class="punctuation">:</span> <span class="string">&quot;books&quot;</span><span class="punctuation">,</span></span><br><span class="line">                <span class="attr">&quot;creation_date&quot;</span><span class="punctuation">:</span> <span class="string">&quot;1645769809521&quot;</span><span class="punctuation">,</span></span><br><span class="line">                <span class="attr">&quot;number_of_replicas&quot;</span><span class="punctuation">:</span> <span class="string">&quot;1&quot;</span><span class="punctuation">,</span></span><br><span class="line">                <span class="attr">&quot;uuid&quot;</span><span class="punctuation">:</span> <span class="string">&quot;DohYKvr_SZO4KRGmbZYmTQ&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;created&quot;</span><span class="punctuation">:</span> <span class="string">&quot;7160299&quot;</span></span><br><span class="line">                <span class="punctuation">&#125;</span></span><br><span class="line">            <span class="punctuation">&#125;</span></span><br><span class="line">        <span class="punctuation">&#125;</span></span><br><span class="line">    <span class="punctuation">&#125;</span></span><br><span class="line"><span class="punctuation">&#125;</span></span><br></pre></td></tr></table></figure></li></ul><p>目前我们已经有了索引了，但是索引中还没有数据，所以要先添加数据，ES中称数据为文档，下面进行文档操作。</p><ul><li><p>添加文档，有三种方式</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><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line">POST请求http<span class="punctuation">:</span><span class="comment">//localhost:9200/books/_doc#使用系统生成id</span></span><br><span class="line">POST请求http<span class="punctuation">:</span><span class="comment">//localhost:9200/books/_create/1#使用指定id</span></span><br><span class="line">POST请求http<span class="punctuation">:</span><span class="comment">//localhost:9200/books/_doc/1#使用指定id，不存在创建，存在更新（版本递增）</span></span><br><span class="line"></span><br><span class="line">文档通过请求参数传递，数据格式json</span><br><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;springboot&quot;</span><span class="punctuation">,</span></span><br><span class="line">    <span class="attr">&quot;type&quot;</span><span class="punctuation">:</span><span class="string">&quot;springboot&quot;</span><span class="punctuation">,</span></span><br><span class="line">    <span class="attr">&quot;description&quot;</span><span class="punctuation">:</span><span class="string">&quot;springboot&quot;</span></span><br><span class="line"><span class="punctuation">&#125;</span>  </span><br></pre></td></tr></table></figure></li><li><p>查询文档</p><figure class="highlight json"><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">GET请求http<span class="punctuation">:</span><span class="comment">//localhost:9200/books/_doc/1 #查询单个文档 </span></span><br><span class="line">GET请求http<span class="punctuation">:</span><span class="comment">//localhost:9200/books/_search #查询全部文档</span></span><br></pre></td></tr></table></figure></li><li><p>条件查询</p><figure class="highlight json"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">GET请求http<span class="punctuation">:</span><span class="comment">//localhost:9200/books/_search?q=name:springboot# q=查询属性名:查询属性值</span></span><br></pre></td></tr></table></figure></li><li><p>删除文档</p><figure class="highlight json"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">DELETE请求http<span class="punctuation">:</span><span class="comment">//localhost:9200/books/_doc/1</span></span><br></pre></td></tr></table></figure></li><li><p>修改文档（全量更新）</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></pre></td><td class="code"><pre><span class="line">PUT请求http<span class="punctuation">:</span><span class="comment">//localhost:9200/books/_doc/1</span></span><br><span class="line"></span><br><span class="line">文档通过请求参数传递，数据格式json</span><br><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;springboot&quot;</span><span class="punctuation">,</span></span><br><span class="line">    <span class="attr">&quot;type&quot;</span><span class="punctuation">:</span><span class="string">&quot;springboot&quot;</span><span class="punctuation">,</span></span><br><span class="line">    <span class="attr">&quot;description&quot;</span><span class="punctuation">:</span><span class="string">&quot;springboot&quot;</span></span><br><span class="line"><span class="punctuation">&#125;</span></span><br></pre></td></tr></table></figure></li><li><p>修改文档（部分更新）</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></pre></td><td class="code"><pre><span class="line">POST请求http<span class="punctuation">:</span><span class="comment">//localhost:9200/books/_update/1</span></span><br><span class="line"></span><br><span class="line">文档通过请求参数传递，数据格式json</span><br><span class="line"><span class="punctuation">&#123;</span></span><br><span class="line">    <span class="attr">&quot;doc&quot;</span><span class="punctuation">:</span><span class="punctuation">&#123;</span>#部分更新并不是对原始文档进行更新，而是对原始文档对象中的doc属性中的指定属性更新</span><br><span class="line">        <span class="attr">&quot;name&quot;</span><span class="punctuation">:</span><span class="string">&quot;springboot&quot;</span>#仅更新提供的属性值，未提供的属性值不参与更新操作</span><br><span class="line">    <span class="punctuation">&#125;</span></span><br><span class="line"><span class="punctuation">&#125;</span></span><br></pre></td></tr></table></figure></li></ul><h5 id="整合-2"><a href="#整合-2" class="headerlink" title="整合"></a>整合</h5><p>​        使用springboot整合ES该如何进行呢？老规矩，导入坐标，做配置，使用API接口操作。整合Redis如此，整合MongoDB如此，整合ES依然如此。太没有新意了，其实不是没有新意，这就是springboot的强大之处，所有东西都做成相同规则，对开发者来说非常友好。</p><p>​        下面就开始springboot整合ES，操作步骤如下：</p><p><strong>步骤①</strong>：导入springboot整合ES的starter坐标</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.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><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="attr">spring:</span></span><br><span class="line">  <span class="attr">elasticsearch:</span></span><br><span class="line">    <span class="attr">rest:</span></span><br><span class="line">      <span class="attr">uris:</span> <span class="string">http://localhost:9200</span></span><br></pre></td></tr></table></figure><p>​        配置ES服务器地址，端口9200</p><p><strong>步骤③</strong>：使用springboot整合ES的专用客户端接口ElasticsearchRestTemplate来进行操作</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></pre></td><td class="code"><pre><span class="line"><span class="meta">@SpringBootTest</span></span><br><span class="line"><span class="keyword">class</span> <span class="title class_">Springboot18EsApplicationTests</span> &#123;</span><br><span class="line">    <span class="meta">@Autowired</span></span><br><span class="line">    <span class="keyword">private</span> ElasticsearchRestTemplate template;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>​        上述操作形式是ES早期的操作方式，使用的客户端被称为Low Level Client，这种客户端操作方式性能方面略显不足，于是ES开发了全新的客户端操作方式，称为High Level Client。高级别客户端与ES版本同步更新，但是springboot最初整合ES的时候使用的是低级别客户端，所以企业开发需要更换成高级别的客户端模式。</p><p>​        下面使用高级别客户端方式进行springboot整合ES，操作步骤如下：</p><p><strong>步骤①</strong>：导入springboot整合ES高级别客户端的坐标，此种形式目前没有对应的starter</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.elasticsearch.client<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>elasticsearch-rest-high-level-client<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>步骤②</strong>：使用编程的形式设置连接的ES服务器，并获取客户端对象</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">@SpringBootTest</span></span><br><span class="line"><span class="keyword">class</span> <span class="title class_">Springboot18EsApplicationTests</span> &#123;</span><br><span class="line">    <span class="keyword">private</span> RestHighLevelClient client;</span><br><span class="line">      <span class="meta">@Test</span></span><br><span class="line">      <span class="keyword">void</span> <span class="title function_">testCreateClient</span><span class="params">()</span> <span class="keyword">throws</span> IOException &#123;</span><br><span class="line">          <span class="type">HttpHost</span> <span class="variable">host</span> <span class="operator">=</span> HttpHost.create(<span class="string">&quot;http://localhost:9200&quot;</span>);</span><br><span class="line">          <span class="type">RestClientBuilder</span> <span class="variable">builder</span> <span class="operator">=</span> RestClient.builder(host);</span><br><span class="line">          client = <span class="keyword">new</span> <span class="title class_">RestHighLevelClient</span>(builder);</span><br><span class="line">  </span><br><span class="line">          client.close();</span><br><span class="line">      &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>​        配置ES服务器地址与端口9200，记得客户端使用完毕需要手工关闭。由于当前客户端是手工维护的，因此不能通过自动装配的形式加载对象。</p><p><strong>步骤③</strong>：使用客户端对象操作ES，例如创建索引</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">@SpringBootTest</span></span><br><span class="line"><span class="keyword">class</span> <span class="title class_">Springboot18EsApplicationTests</span> &#123;</span><br><span class="line">    <span class="keyword">private</span> RestHighLevelClient client;</span><br><span class="line">      <span class="meta">@Test</span></span><br><span class="line">      <span class="keyword">void</span> <span class="title function_">testCreateIndex</span><span class="params">()</span> <span class="keyword">throws</span> IOException &#123;</span><br><span class="line">          <span class="type">HttpHost</span> <span class="variable">host</span> <span class="operator">=</span> HttpHost.create(<span class="string">&quot;http://localhost:9200&quot;</span>);</span><br><span class="line">          <span class="type">RestClientBuilder</span> <span class="variable">builder</span> <span class="operator">=</span> RestClient.builder(host);</span><br><span class="line">          client = <span class="keyword">new</span> <span class="title class_">RestHighLevelClient</span>(builder);</span><br><span class="line">          </span><br><span class="line">          <span class="type">CreateIndexRequest</span> <span class="variable">request</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">CreateIndexRequest</span>(<span class="string">&quot;books&quot;</span>);</span><br><span class="line">          client.indices().create(request, RequestOptions.DEFAULT); </span><br><span class="line">          </span><br><span class="line">          client.close();</span><br><span class="line">      &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>​        高级别客户端操作是通过发送请求的方式完成所有操作的，ES针对各种不同的操作，设定了各式各样的请求对象，上例中创建索引的对象是CreateIndexRequest，其他操作也会有自己专用的Request对象。</p><p>​        当前操作我们发现，无论进行ES何种操作，第一步永远是获取RestHighLevelClient对象，最后一步永远是关闭该对象的连接。在测试中可以使用测试类的特性去帮助开发者一次性的完成上述操作，但是在业务书写时，还需要自行管理。将上述代码格式转换成使用测试类的初始化方法和销毁方法进行客户端对象的维护。</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="meta">@SpringBootTest</span></span><br><span class="line"><span class="keyword">class</span> <span class="title class_">Springboot18EsApplicationTests</span> &#123;</span><br><span class="line">    <span class="meta">@BeforeEach</span><span class="comment">//在测试类中每个操作运行前运行的方法</span></span><br><span class="line">    <span class="keyword">void</span> <span class="title function_">setUp</span><span class="params">()</span> &#123;</span><br><span class="line">        <span class="type">HttpHost</span> <span class="variable">host</span> <span class="operator">=</span> HttpHost.create(<span class="string">&quot;http://localhost:9200&quot;</span>);</span><br><span class="line">        <span class="type">RestClientBuilder</span> <span class="variable">builder</span> <span class="operator">=</span> RestClient.builder(host);</span><br><span class="line">        client = <span class="keyword">new</span> <span class="title class_">RestHighLevelClient</span>(builder);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@AfterEach</span><span class="comment">//在测试类中每个操作运行后运行的方法</span></span><br><span class="line">    <span class="keyword">void</span> <span class="title function_">tearDown</span><span class="params">()</span> <span class="keyword">throws</span> IOException &#123;</span><br><span class="line">        client.close();</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">private</span> RestHighLevelClient client;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Test</span></span><br><span class="line">    <span class="keyword">void</span> <span class="title function_">testCreateIndex</span><span class="params">()</span> <span class="keyword">throws</span> IOException &#123;</span><br><span class="line">        <span class="type">CreateIndexRequest</span> <span class="variable">request</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">CreateIndexRequest</span>(<span class="string">&quot;books&quot;</span>);</span><br><span class="line">        client.indices().create(request, RequestOptions.DEFAULT);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>​        现在的书写简化了很多，也更合理。下面使用上述模式将所有的ES操作执行一遍，测试结果</p><p><strong>创建索引（IK分词器）</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></pre></td><td class="code"><pre><span class="line"><span class="meta">@Test</span></span><br><span class="line"><span class="keyword">void</span> <span class="title function_">testCreateIndexByIK</span><span class="params">()</span> <span class="keyword">throws</span> IOException &#123;</span><br><span class="line">    <span class="type">CreateIndexRequest</span> <span class="variable">request</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">CreateIndexRequest</span>(<span class="string">&quot;books&quot;</span>);</span><br><span class="line">    <span class="type">String</span> <span class="variable">json</span> <span class="operator">=</span> <span class="string">&quot;&#123;\n&quot;</span> +</span><br><span class="line">            <span class="string">&quot;    \&quot;mappings\&quot;:&#123;\n&quot;</span> +</span><br><span class="line">            <span class="string">&quot;        \&quot;properties\&quot;:&#123;\n&quot;</span> +</span><br><span class="line">            <span class="string">&quot;            \&quot;id\&quot;:&#123;\n&quot;</span> +</span><br><span class="line">            <span class="string">&quot;                \&quot;type\&quot;:\&quot;keyword\&quot;\n&quot;</span> +</span><br><span class="line">            <span class="string">&quot;            &#125;,\n&quot;</span> +</span><br><span class="line">            <span class="string">&quot;            \&quot;name\&quot;:&#123;\n&quot;</span> +</span><br><span class="line">            <span class="string">&quot;                \&quot;type\&quot;:\&quot;text\&quot;,\n&quot;</span> +</span><br><span class="line">            <span class="string">&quot;                \&quot;analyzer\&quot;:\&quot;ik_max_word\&quot;,\n&quot;</span> +</span><br><span class="line">            <span class="string">&quot;                \&quot;copy_to\&quot;:\&quot;all\&quot;\n&quot;</span> +</span><br><span class="line">            <span class="string">&quot;            &#125;,\n&quot;</span> +</span><br><span class="line">            <span class="string">&quot;            \&quot;type\&quot;:&#123;\n&quot;</span> +</span><br><span class="line">            <span class="string">&quot;                \&quot;type\&quot;:\&quot;keyword\&quot;\n&quot;</span> +</span><br><span class="line">            <span class="string">&quot;            &#125;,\n&quot;</span> +</span><br><span class="line">            <span class="string">&quot;            \&quot;description\&quot;:&#123;\n&quot;</span> +</span><br><span class="line">            <span class="string">&quot;                \&quot;type\&quot;:\&quot;text\&quot;,\n&quot;</span> +</span><br><span class="line">            <span class="string">&quot;                \&quot;analyzer\&quot;:\&quot;ik_max_word\&quot;,\n&quot;</span> +</span><br><span class="line">            <span class="string">&quot;                \&quot;copy_to\&quot;:\&quot;all\&quot;\n&quot;</span> +</span><br><span class="line">            <span class="string">&quot;            &#125;,\n&quot;</span> +</span><br><span class="line">            <span class="string">&quot;            \&quot;all\&quot;:&#123;\n&quot;</span> +</span><br><span class="line">            <span class="string">&quot;                \&quot;type\&quot;:\&quot;text\&quot;,\n&quot;</span> +</span><br><span class="line">            <span class="string">&quot;                \&quot;analyzer\&quot;:\&quot;ik_max_word\&quot;\n&quot;</span> +</span><br><span class="line">            <span class="string">&quot;            &#125;\n&quot;</span> +</span><br><span class="line">            <span class="string">&quot;        &#125;\n&quot;</span> +</span><br><span class="line">            <span class="string">&quot;    &#125;\n&quot;</span> +</span><br><span class="line">            <span class="string">&quot;&#125;&quot;</span>;</span><br><span class="line">    <span class="comment">//设置请求中的参数</span></span><br><span class="line">    request.source(json, XContentType.JSON);</span><br><span class="line">    client.indices().create(request, RequestOptions.DEFAULT);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>​        IK分词器是通过请求参数的形式进行设置的，设置请求参数使用request对象中的source方法进行设置，至于参数是什么，取决于你的操作种类。当请求中需要参数时，均可使用当前形式进行参数设置。    </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></pre></td><td class="code"><pre><span class="line"><span class="meta">@Test</span></span><br><span class="line"><span class="comment">//添加文档</span></span><br><span class="line"><span class="keyword">void</span> <span class="title function_">testCreateDoc</span><span class="params">()</span> <span class="keyword">throws</span> IOException &#123;</span><br><span class="line">    <span class="type">Book</span> <span class="variable">book</span> <span class="operator">=</span> bookDao.selectById(<span class="number">1</span>);</span><br><span class="line">    <span class="type">IndexRequest</span> <span class="variable">request</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">IndexRequest</span>(<span class="string">&quot;books&quot;</span>).id(book.getId().toString());</span><br><span class="line">    <span class="type">String</span> <span class="variable">json</span> <span class="operator">=</span> JSON.toJSONString(book);</span><br><span class="line">    request.source(json,XContentType.JSON);</span><br><span class="line">    client.index(request,RequestOptions.DEFAULT);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>​        添加文档使用的请求对象是IndexRequest，与创建索引使用的请求对象不同。    </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></pre></td><td class="code"><pre><span class="line"><span class="meta">@Test</span></span><br><span class="line"><span class="comment">//批量添加文档</span></span><br><span class="line"><span class="keyword">void</span> <span class="title function_">testCreateDocAll</span><span class="params">()</span> <span class="keyword">throws</span> IOException &#123;</span><br><span class="line">    List&lt;Book&gt; bookList = bookDao.selectList(<span class="literal">null</span>);</span><br><span class="line">    <span class="type">BulkRequest</span> <span class="variable">bulk</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">BulkRequest</span>();</span><br><span class="line">    <span class="keyword">for</span> (Book book : bookList) &#123;</span><br><span class="line">        <span class="type">IndexRequest</span> <span class="variable">request</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">IndexRequest</span>(<span class="string">&quot;books&quot;</span>).id(book.getId().toString());</span><br><span class="line">        <span class="type">String</span> <span class="variable">json</span> <span class="operator">=</span> JSON.toJSONString(book);</span><br><span class="line">        request.source(json,XContentType.JSON);</span><br><span class="line">        bulk.add(request);</span><br><span class="line">    &#125;</span><br><span class="line">    client.bulk(bulk,RequestOptions.DEFAULT);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>​        批量做时，先创建一个BulkRequest的对象，可以将该对象理解为是一个保存request对象的容器，将所有的请求都初始化好后，添加到BulkRequest对象中，再使用BulkRequest对象的bulk方法，一次性执行完毕。</p><p><strong>按id查询文档</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">@Test</span></span><br><span class="line"><span class="comment">//按id查询</span></span><br><span class="line"><span class="keyword">void</span> <span class="title function_">testGet</span><span class="params">()</span> <span class="keyword">throws</span> IOException &#123;</span><br><span class="line">    <span class="type">GetRequest</span> <span class="variable">request</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">GetRequest</span>(<span class="string">&quot;books&quot;</span>,<span class="string">&quot;1&quot;</span>);</span><br><span class="line">    <span class="type">GetResponse</span> <span class="variable">response</span> <span class="operator">=</span> client.get(request, RequestOptions.DEFAULT);</span><br><span class="line">    <span class="type">String</span> <span class="variable">json</span> <span class="operator">=</span> response.getSourceAsString();</span><br><span class="line">    System.out.println(json);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>​        根据id查询文档使用的请求对象是GetRequest。</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></pre></td><td class="code"><pre><span class="line"><span class="meta">@Test</span></span><br><span class="line"><span class="comment">//按条件查询</span></span><br><span class="line"><span class="keyword">void</span> <span class="title function_">testSearch</span><span class="params">()</span> <span class="keyword">throws</span> IOException &#123;</span><br><span class="line">    <span class="type">SearchRequest</span> <span class="variable">request</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">SearchRequest</span>(<span class="string">&quot;books&quot;</span>);</span><br><span class="line"></span><br><span class="line">    <span class="type">SearchSourceBuilder</span> <span class="variable">builder</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">SearchSourceBuilder</span>();</span><br><span class="line">    builder.query(QueryBuilders.termQuery(<span class="string">&quot;all&quot;</span>,<span class="string">&quot;spring&quot;</span>));</span><br><span class="line">    request.source(builder);</span><br><span class="line"></span><br><span class="line">    <span class="type">SearchResponse</span> <span class="variable">response</span> <span class="operator">=</span> client.search(request, RequestOptions.DEFAULT);</span><br><span class="line">    <span class="type">SearchHits</span> <span class="variable">hits</span> <span class="operator">=</span> response.getHits();</span><br><span class="line">    <span class="keyword">for</span> (SearchHit hit : hits) &#123;</span><br><span class="line">        <span class="type">String</span> <span class="variable">source</span> <span class="operator">=</span> hit.getSourceAsString();</span><br><span class="line">        <span class="comment">//System.out.println(source);</span></span><br><span class="line">        <span class="type">Book</span> <span class="variable">book</span> <span class="operator">=</span> JSON.parseObject(source, Book.class);</span><br><span class="line">        System.out.println(book);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>​        按条件查询文档使用的请求对象是SearchRequest，查询时调用SearchRequest对象的termQuery方法，需要给出查询属性名，此处支持使用合并字段，也就是前面定义索引属性时添加的all属性。</p><p>​        springboot整合ES的操作到这里就说完了，与前期进行springboot整合redis和mongodb的差别还是蛮大的，主要原始就是我们没有使用springboot整合ES的客户端对象。至于操作，由于ES操作种类过多，所以显得操作略微有点复杂。有关springboot整合ES就先学习到这里吧。</p><p><strong>总结</strong></p><ol><li>springboot整合ES步骤<ol><li>导入springboot整合ES的High Level Client坐标</li><li>手工管理客户端对象，包括初始化和关闭操作</li><li>使用High Level Client根据操作的种类不同，选择不同的Request对象完成对应操作</li></ol></li></ol><h2 id="KF-5-整合第三方技术"><a href="#KF-5-整合第三方技术" class="headerlink" title="KF-5.整合第三方技术"></a>KF-5.整合第三方技术</h2><p>​        通过第四章的学习，我们领略到了springboot在整合第三方技术时强大的一致性，在第五章中我们要使用springboot继续整合各种各样的第三方技术，通过本章的学习，可以将之前学习的springboot整合第三方技术的思想贯彻到底，还是那三板斧。导坐标、做配置、调API。</p><p>​        springboot能够整合的技术实在是太多了，可以说是万物皆可整。本章将从企业级开发中常用的一些技术作为出发点，对各种各样的技术进行整合。</p><h3 id="KF-5-1-缓存"><a href="#KF-5-1-缓存" class="headerlink" title="KF-5-1.缓存"></a>KF-5-1.缓存</h3><p>​        企业级应用主要作用是信息处理，当需要读取数据时，由于受限于数据库的访问效率，导致整体系统性能偏低。</p><p><img src="img\image-20220226154148303.png" alt="image-20220226154148303" style="zoom:67%;" /></p><p>​                                                                      应用程序直接与数据库打交道，访问效率低</p><p>​        为了改善上述现象，开发者通常会在应用程序与数据库之间建立一种临时的数据存储机制，该区域中的数据在内存中保存，读写速度较快，可以有效解决数据库访问效率低下的问题。这一块临时存储数据的区域就是缓存。</p><p><img src="img\image-20220226154233010.png" alt="image-20220226154233010" style="zoom:67%;" /></p><pre><code>                                         使用缓存后，应用程序与缓存打交道，缓存与数据库打交道，数据访问效率提高</code></pre><p>​        缓存是什么？缓存是一种介于数据永久存储介质与应用程序之间的数据临时存储介质，使用缓存可以有效的减少低速数据读取过程的次数（例如磁盘IO），提高系统性能。此外缓存不仅可以用于提高永久性存储介质的数据读取效率，还可以提供临时的数据存储空间。而springboot提供了对市面上几乎所有的缓存技术进行整合的方案，下面就一起开启springboot整合缓存之旅。</p><h4 id="SpringBoot内置缓存解决方案"><a href="#SpringBoot内置缓存解决方案" class="headerlink" title="SpringBoot内置缓存解决方案"></a>SpringBoot内置缓存解决方案</h4><p>​        springboot技术提供有内置的缓存解决方案，可以帮助开发者快速开启缓存技术，并使用缓存技术进行数据的快速操作，例如读取缓存数据和写入数据到缓存。</p><p><strong>步骤①</strong>：导入springboot提供的缓存技术对应的starter</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.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-cache<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>步骤②</strong>：启用缓存，在引导类上方标注注解@EnableCaching配置springboot程序中可以使用缓存</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="comment">//开启缓存功能</span></span><br><span class="line"><span class="meta">@EnableCaching</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">Springboot19CacheApplication</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(Springboot19CacheApplication.class, args);</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></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_">BookServiceImpl</span> <span class="keyword">implements</span> <span class="title class_">BookService</span> &#123;</span><br><span class="line">    <span class="meta">@Autowired</span></span><br><span class="line">    <span class="keyword">private</span> BookDao bookDao;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Cacheable(value=&quot;cacheSpace&quot;,key=&quot;#id&quot;)</span></span><br><span class="line">    <span class="keyword">public</span> Book <span class="title function_">getById</span><span class="params">(Integer id)</span> &#123;</span><br><span class="line">        <span class="keyword">return</span> bookDao.selectById(id);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>​        在业务方法上面使用注解@Cacheable声明当前方法的返回值放入缓存中，其中要指定缓存的存储位置，以及缓存中保存当前方法返回值对应的名称。上例中value属性描述缓存的存储位置，可以理解为是一个存储空间名，key属性描述了缓存中保存数据的名称，使用#id读取形参中的id值作为缓存名称。</p><p>​        使用@Cacheable注解后，执行当前操作，如果发现对应名称在缓存中没有数据，就正常读取数据，然后放入缓存；如果对应名称在缓存中有数据，就终止当前业务方法执行，直接返回缓存中的数据。</p><h4 id="手机验证码案例"><a href="#手机验证码案例" class="headerlink" title="手机验证码案例"></a>手机验证码案例</h4><p>​        为了便于下面演示各种各样的缓存技术，我们创建一个手机验证码的案例环境，模拟使用缓存保存手机验证码的过程。</p><p>​        手机验证码案例需求如下：</p><ul><li>输入手机号获取验证码，组织文档以短信形式发送给用户（页面模拟）</li><li>输入手机号和验证码验证结果</li></ul><p>​        为了描述上述操作，我们制作两个表现层接口，一个用来模拟发送短信的过程，其实就是根据用户提供的手机号生成一个验证码，然后放入缓存，另一个用来模拟验证码校验的过程，其实就是使用传入的手机号和验证码进行匹配，并返回最终匹配结果。下面直接制作本案例的模拟代码，先以上例中springboot提供的内置缓存技术来完成当前案例的制作。</p><p><strong>步骤①</strong>：导入springboot提供的缓存技术对应的starter</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.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-cache<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>步骤②</strong>：启用缓存，在引导类上方标注注解@EnableCaching配置springboot程序中可以使用缓存</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="comment">//开启缓存功能</span></span><br><span class="line"><span class="meta">@EnableCaching</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">Springboot19CacheApplication</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(Springboot19CacheApplication.class, args);</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></pre></td><td class="code"><pre><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_">SMSCode</span> &#123;</span><br><span class="line">    <span class="keyword">private</span> String tele;</span><br><span class="line">    <span class="keyword">private</span> String code;</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">interface</span> <span class="title class_">SMSCodeService</span> &#123;</span><br><span class="line">    <span class="keyword">public</span> String <span class="title function_">sendCodeToSMS</span><span class="params">(String tele)</span>;</span><br><span class="line">    <span class="keyword">public</span> <span class="type">boolean</span> <span class="title function_">checkCode</span><span class="params">(SMSCode smsCode)</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_">SMSCodeServiceImpl</span> <span class="keyword">implements</span> <span class="title class_">SMSCodeService</span> &#123;</span><br><span class="line">    <span class="meta">@Autowired</span></span><br><span class="line">    <span class="keyword">private</span> CodeUtils codeUtils;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@CachePut(value = &quot;smsCode&quot;, key = &quot;#tele&quot;)</span></span><br><span class="line">    <span class="keyword">public</span> String <span class="title function_">sendCodeToSMS</span><span class="params">(String tele)</span> &#123;</span><br><span class="line">        <span class="type">String</span> <span class="variable">code</span> <span class="operator">=</span> codeUtils.generator(tele);</span><br><span class="line">        <span class="keyword">return</span> code;</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_">checkCode</span><span class="params">(SMSCode smsCode)</span> &#123;</span><br><span class="line">        <span class="comment">//取出内存中的验证码与传递过来的验证码比对，如果相同，返回true</span></span><br><span class="line">        <span class="type">String</span> <span class="variable">code</span> <span class="operator">=</span> smsCode.getCode();</span><br><span class="line">        <span class="type">String</span> <span class="variable">cacheCode</span> <span class="operator">=</span> codeUtils.get(smsCode.getTele());</span><br><span class="line">        <span class="keyword">return</span> code.equals(cacheCode);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>​        获取验证码后，当验证码失效时必须重新获取验证码，因此在获取验证码的功能上不能使用@Cacheable注解，@Cacheable注解是缓存中没有值则放入值，缓存中有值则取值。此处的功能仅仅是生成验证码并放入缓存，并不具有从缓存中取值的功能，因此不能使用@Cacheable注解，应该使用仅具有向缓存中保存数据的功能，使用@CachePut注解即可。</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></pre></td><td class="code"><pre><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_">CodeUtils</span> &#123;</span><br><span class="line">    <span class="keyword">private</span> String [] patch = &#123;<span class="string">&quot;000000&quot;</span>,<span class="string">&quot;00000&quot;</span>,<span class="string">&quot;0000&quot;</span>,<span class="string">&quot;000&quot;</span>,<span class="string">&quot;00&quot;</span>,<span class="string">&quot;0&quot;</span>,<span class="string">&quot;&quot;</span>&#125;;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">public</span> String <span class="title function_">generator</span><span class="params">(String tele)</span>&#123;</span><br><span class="line">        <span class="type">int</span> <span class="variable">hash</span> <span class="operator">=</span> tele.hashCode();</span><br><span class="line">        <span class="type">int</span> <span class="variable">encryption</span> <span class="operator">=</span> <span class="number">20206666</span>;</span><br><span class="line">        <span class="type">long</span> <span class="variable">result</span> <span class="operator">=</span> hash ^ encryption;</span><br><span class="line">        <span class="type">long</span> <span class="variable">nowTime</span> <span class="operator">=</span> System.currentTimeMillis();</span><br><span class="line">        result = result ^ nowTime;</span><br><span class="line">        <span class="type">long</span> <span class="variable">code</span> <span class="operator">=</span> result % <span class="number">1000000</span>;</span><br><span class="line">        code = code &lt; <span class="number">0</span> ? -code : code;</span><br><span class="line">        <span class="type">String</span> <span class="variable">codeStr</span> <span class="operator">=</span> code + <span class="string">&quot;&quot;</span>;</span><br><span class="line">        <span class="type">int</span> <span class="variable">len</span> <span class="operator">=</span> codeStr.length();</span><br><span class="line">        <span class="keyword">return</span> patch[len] + codeStr;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Cacheable(value = &quot;smsCode&quot;,key=&quot;#tele&quot;)</span></span><br><span class="line">    <span class="keyword">public</span> String <span class="title function_">get</span><span class="params">(String tele)</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>步骤⑥</strong>：定义验证码功能的web层接口，一个方法用于提供手机号获取验证码，一个方法用于提供手机号和验证码进行校验</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="meta">@RestController</span></span><br><span class="line"><span class="meta">@RequestMapping(&quot;/sms&quot;)</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">SMSCodeController</span> &#123;</span><br><span class="line">    <span class="meta">@Autowired</span></span><br><span class="line">    <span class="keyword">private</span> SMSCodeService smsCodeService;</span><br><span class="line">    </span><br><span class="line">    <span class="meta">@GetMapping</span></span><br><span class="line">    <span class="keyword">public</span> String <span class="title function_">getCode</span><span class="params">(String tele)</span>&#123;</span><br><span class="line">        <span class="type">String</span> <span class="variable">code</span> <span class="operator">=</span> smsCodeService.sendCodeToSMS(tele);</span><br><span class="line">        <span class="keyword">return</span> code;</span><br><span class="line">    &#125;</span><br><span class="line">    </span><br><span class="line">    <span class="meta">@PostMapping</span></span><br><span class="line">    <span class="keyword">public</span> <span class="type">boolean</span> <span class="title function_">checkCode</span><span class="params">(SMSCode smsCode)</span>&#123;</span><br><span class="line">        <span class="keyword">return</span> smsCodeService.checkCode(smsCode);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h4 id="SpringBoot整合Ehcache缓存"><a href="#SpringBoot整合Ehcache缓存" class="headerlink" title="SpringBoot整合Ehcache缓存"></a>SpringBoot整合Ehcache缓存</h4><p>​        手机验证码的案例已经完成了，下面就开始springboot整合各种各样的缓存技术，第一个整合Ehcache技术。Ehcache是一种缓存技术，使用springboot整合Ehcache其实就是变更一下缓存技术的实现方式，话不多说，直接开整</p><p><strong>步骤①</strong>：导入Ehcache的坐标</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>net.sf.ehcache<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>ehcache<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>​        此处为什么不是导入Ehcache的starter，而是导入技术坐标呢？其实springboot整合缓存技术做的是通用格式，不管你整合哪种缓存技术，只是实现变化了，操作方式一样。这也体现出springboot技术的优点，统一同类技术的整合方式。</p><p><strong>步骤②</strong>：配置缓存技术实现使用Ehcache</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></pre></td><td class="code"><pre><span class="line"><span class="attr">spring:</span></span><br><span class="line">  <span class="attr">cache:</span></span><br><span class="line">    <span class="attr">type:</span> <span class="string">ehcache</span></span><br><span class="line">    <span class="attr">ehcache:</span></span><br><span class="line">      <span class="attr">config:</span> <span class="string">ehcache.xml</span></span><br></pre></td></tr></table></figure><p>​        配置缓存的类型type为ehcache，此处需要说明一下，当前springboot可以整合的缓存技术中包含有ehcach，所以可以这样书写。其实这个type不可以随便写的，不是随便写一个名称就可以整合的。</p><p>​        由于ehcache的配置有独立的配置文件格式，因此还需要指定ehcache的配置文件，以便于读取相应配置</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="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">ehcache</span> <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:noNamespaceSchemaLocation</span>=<span class="string">&quot;http://ehcache.org/ehcache.xsd&quot;</span></span></span><br><span class="line"><span class="tag">         <span class="attr">updateCheck</span>=<span class="string">&quot;false&quot;</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">diskStore</span> <span class="attr">path</span>=<span class="string">&quot;D:\ehcache&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;!-- external：是否永久存在，设置为true则不会被清除，此时与timeout冲突，通常设置为false--&gt;</span></span><br><span class="line">    <span class="comment">&lt;!-- diskPersistent：是否启用磁盘持久化--&gt;</span></span><br><span class="line">    <span class="comment">&lt;!-- maxElementsInMemory：最大缓存数量--&gt;</span></span><br><span class="line">    <span class="comment">&lt;!-- overflowToDisk：超过最大缓存数量是否持久化到磁盘--&gt;</span></span><br><span class="line">    <span class="comment">&lt;!-- timeToIdleSeconds：最大不活动间隔，设置过长缓存容易溢出，设置过短无效果，可用于记录时效性数据，例如验证码--&gt;</span></span><br><span class="line">    <span class="comment">&lt;!-- timeToLiveSeconds：最大存活时间--&gt;</span></span><br><span class="line">    <span class="comment">&lt;!-- memoryStoreEvictionPolicy：缓存清除策略--&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">defaultCache</span></span></span><br><span class="line"><span class="tag">        <span class="attr">eternal</span>=<span class="string">&quot;false&quot;</span></span></span><br><span class="line"><span class="tag">        <span class="attr">diskPersistent</span>=<span class="string">&quot;false&quot;</span></span></span><br><span class="line"><span class="tag">        <span class="attr">maxElementsInMemory</span>=<span class="string">&quot;1000&quot;</span></span></span><br><span class="line"><span class="tag">        <span class="attr">overflowToDisk</span>=<span class="string">&quot;false&quot;</span></span></span><br><span class="line"><span class="tag">        <span class="attr">timeToIdleSeconds</span>=<span class="string">&quot;60&quot;</span></span></span><br><span class="line"><span class="tag">        <span class="attr">timeToLiveSeconds</span>=<span class="string">&quot;60&quot;</span></span></span><br><span class="line"><span class="tag">        <span class="attr">memoryStoreEvictionPolicy</span>=<span class="string">&quot;LRU&quot;</span> /&gt;</span></span><br><span class="line"></span><br><span class="line">    <span class="tag">&lt;<span class="name">cache</span></span></span><br><span class="line"><span class="tag">        <span class="attr">name</span>=<span class="string">&quot;smsCode&quot;</span></span></span><br><span class="line"><span class="tag">        <span class="attr">eternal</span>=<span class="string">&quot;false&quot;</span></span></span><br><span class="line"><span class="tag">        <span class="attr">diskPersistent</span>=<span class="string">&quot;false&quot;</span></span></span><br><span class="line"><span class="tag">        <span class="attr">maxElementsInMemory</span>=<span class="string">&quot;1000&quot;</span></span></span><br><span class="line"><span class="tag">        <span class="attr">overflowToDisk</span>=<span class="string">&quot;false&quot;</span></span></span><br><span class="line"><span class="tag">        <span class="attr">timeToIdleSeconds</span>=<span class="string">&quot;10&quot;</span></span></span><br><span class="line"><span class="tag">        <span class="attr">timeToLiveSeconds</span>=<span class="string">&quot;10&quot;</span></span></span><br><span class="line"><span class="tag">        <span class="attr">memoryStoreEvictionPolicy</span>=<span class="string">&quot;LRU&quot;</span> /&gt;</span></span><br><span class="line"><span class="tag">&lt;/<span class="name">ehcache</span>&gt;</span></span><br></pre></td></tr></table></figure><p>​        注意前面的案例中，设置了数据保存的位置是smsCode</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></pre></td><td class="code"><pre><span class="line"><span class="meta">@CachePut(value = &quot;smsCode&quot;, key = &quot;#tele&quot;)</span></span><br><span class="line"><span class="keyword">public</span> String <span class="title function_">sendCodeToSMS</span><span class="params">(String tele)</span> &#123;</span><br><span class="line">    <span class="type">String</span> <span class="variable">code</span> <span class="operator">=</span> codeUtils.generator(tele);</span><br><span class="line">    <span class="keyword">return</span> code;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>​        这个设定需要保障ehcache中有一个缓存空间名称叫做smsCode的配置，前后要统一。在企业开发过程中，通过设置不同名称的cache来设定不同的缓存策略，应用于不同的缓存数据。</p><p>​        到这里springboot整合Ehcache就做完了，可以发现一点，原始代码没有任何修改，仅仅是加了一组配置就可以变更缓存供应商了，这也是springboot提供了统一的缓存操作接口的优势，变更实现并不影响原始代码的书写。</p><p><strong>总结</strong></p><ol><li>springboot使用Ehcache作为缓存实现需要导入Ehcache的坐标</li><li>修改设置，配置缓存供应商为ehcache，并提供对应的缓存配置文件</li></ol><p>​        </p><h4 id="SpringBoot整合Redis缓存"><a href="#SpringBoot整合Redis缓存" class="headerlink" title="SpringBoot整合Redis缓存"></a>SpringBoot整合Redis缓存</h4><p>​        上节使用Ehcache替换了springboot内置的缓存技术，其实springboot支持的缓存技术还很多，下面使用redis技术作为缓存解决方案来实现手机验证码案例。</p><p>​        比对使用Ehcache的过程，加坐标，改缓存实现类型为ehcache，做Ehcache的配置。如果还成redis做缓存呢？一模一样，加坐标，改缓存实现类型为redis，做redis的配置。差别之处只有一点，redis的配置可以在yml文件中直接进行配置，无需制作独立的配置文件。</p><p><strong>步骤①</strong>：导入redis的坐标</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.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></pre></td></tr></table></figure><p><strong>步骤②</strong>：配置缓存技术实现使用redis</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></pre></td><td class="code"><pre><span class="line"><span class="attr">spring:</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 class="attr">cache:</span></span><br><span class="line">    <span class="attr">type:</span> <span class="string">redis</span></span><br></pre></td></tr></table></figure><p>​        如果需要对redis作为缓存进行配置，注意不是对原始的redis进行配置，而是配置redis作为缓存使用相关的配置，隶属于spring.cache.redis节点下，注意不要写错位置了。</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></pre></td><td class="code"><pre><span class="line"><span class="attr">spring:</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 class="attr">cache:</span></span><br><span class="line">    <span class="attr">type:</span> <span class="string">redis</span></span><br><span class="line">    <span class="attr">redis:</span></span><br><span class="line">      <span class="attr">use-key-prefix:</span> <span class="literal">false</span></span><br><span class="line">      <span class="attr">key-prefix:</span> <span class="string">sms_</span></span><br><span class="line">      <span class="attr">cache-null-values:</span> <span class="literal">false</span></span><br><span class="line">      <span class="attr">time-to-live:</span> <span class="string">10s</span></span><br></pre></td></tr></table></figure><p><strong>总结</strong></p><ol><li>springboot使用redis作为缓存实现需要导入redis的坐标</li><li>修改设置，配置缓存供应商为redis，并提供对应的缓存配置</li></ol><h4 id="SpringBoot整合Memcached缓存"><a href="#SpringBoot整合Memcached缓存" class="headerlink" title="SpringBoot整合Memcached缓存"></a>SpringBoot整合Memcached缓存</h4><p>​        目前我们已经掌握了3种缓存解决方案的配置形式，分别是springboot内置缓存，ehcache和redis，本节研究一下国内比较流行的一款缓存memcached。</p><p>​        按照之前的套路，其实变更缓存并不繁琐，但是springboot并没有支持使用memcached作为其缓存解决方案，也就是说在type属性中没有memcached的配置选项，这里就需要更变一下处理方式了。在整合之前先安装memcached。</p><p><strong>安装</strong></p><p>​        windows版安装包下载地址：<a href="https://www.runoob.com/memcached/window-install-memcached.html">https://www.runoob.com/memcached/window-install-memcached.html</a></p><p>​        下载的安装包是解压缩就能使用的zip文件，解压缩完毕后会得到如下文件</p><p><img src="img\image-20220226174957040.png" alt="image-20220226174957040"></p><p>​        可执行文件只有一个memcached.exe，使用该文件可以将memcached作为系统服务启动，执行此文件时会出现报错信息，如下：</p><p><img src="img\image-20220226175141986.png" alt="image-20220226175141986" style="zoom:80%;" /></p><p>​        此处出现问题的原因是注册系统服务时需要使用管理员权限，当前账号权限不足导致安装服务失败，切换管理员账号权限启动命令行</p><p><img src="img\image-20220226175302903.png" alt="image-20220226175302903" style="zoom:80%;" /></p><p>​        然后再次执行安装服务的命令即可，如下：</p><figure class="highlight cmd"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">memcached.exe -d install</span><br></pre></td></tr></table></figure><p>​        服务安装完毕后可以使用命令启动和停止服务，如下：</p><figure class="highlight cmd"><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">memcached.exe -d <span class="built_in">start</span># 启动服务</span><br><span class="line">memcached.exe -d stop# 停止服务</span><br></pre></td></tr></table></figure><p>​        也可以在任务管理器中进行服务状态的切换</p><p><img src="img\image-20220226175441675.png" alt="image-20220226175441675" style="zoom:67%;" /></p><p><strong>变更缓存为Memcached</strong></p><p>​        由于memcached未被springboot收录为缓存解决方案，因此使用memcached需要通过手工硬编码的方式来使用，于是前面的套路都不适用了，需要自己写了。</p><p>​        memcached目前提供有三种客户端技术，分别是Memcached Client for Java、SpyMemcached和Xmemcached，其中性能指标各方面最好的客户端是Xmemcached，本次整合就使用这个作为客户端实现技术了。下面开始使用Xmemcached</p><p><strong>步骤①</strong>：导入xmemcached的坐标</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.googlecode.xmemcached<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>xmemcached<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.4.7<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>：配置memcached，制作memcached的配置类</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">@Configuration</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">XMemcachedConfig</span> &#123;</span><br><span class="line">    <span class="meta">@Bean</span></span><br><span class="line">    <span class="keyword">public</span> MemcachedClient <span class="title function_">getMemcachedClient</span><span class="params">()</span> <span class="keyword">throws</span> IOException &#123;</span><br><span class="line">        <span class="type">MemcachedClientBuilder</span> <span class="variable">memcachedClientBuilder</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">XMemcachedClientBuilder</span>(<span class="string">&quot;localhost:11211&quot;</span>);</span><br><span class="line">        <span class="type">MemcachedClient</span> <span class="variable">memcachedClient</span> <span class="operator">=</span> memcachedClientBuilder.build();</span><br><span class="line">        <span class="keyword">return</span> memcachedClient;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>​        memcached默认对外服务端口11211。</p><p><strong>步骤③</strong>：使用xmemcached客户端操作缓存，注入MemcachedClient对象</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">@Service</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">SMSCodeServiceImpl</span> <span class="keyword">implements</span> <span class="title class_">SMSCodeService</span> &#123;</span><br><span class="line">    <span class="meta">@Autowired</span></span><br><span class="line">    <span class="keyword">private</span> CodeUtils codeUtils;</span><br><span class="line">    <span class="meta">@Autowired</span></span><br><span class="line">    <span class="keyword">private</span> MemcachedClient memcachedClient;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">public</span> String <span class="title function_">sendCodeToSMS</span><span class="params">(String tele)</span> &#123;</span><br><span class="line">        <span class="type">String</span> <span class="variable">code</span> <span class="operator">=</span> codeUtils.generator(tele);</span><br><span class="line">        <span class="keyword">try</span> &#123;</span><br><span class="line">            memcachedClient.set(tele,<span class="number">10</span>,code);</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">        <span class="keyword">return</span> code;</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_">checkCode</span><span class="params">(SMSCode smsCode)</span> &#123;</span><br><span class="line">        <span class="type">String</span> <span class="variable">code</span> <span class="operator">=</span> <span class="literal">null</span>;</span><br><span class="line">        <span class="keyword">try</span> &#123;</span><br><span class="line">            code = memcachedClient.get(smsCode.getTele()).toString();</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">        <span class="keyword">return</span> smsCode.getCode().equals(code);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>​        设置值到缓存中使用set操作，取值使用get操作，其实更符合我们开发者的习惯。</p><p>​        上述代码中对于服务器的配置使用硬编码写死到了代码中，将此数据提取出来，做成独立的配置属性。</p><p><strong>定义配置属性</strong></p><p>​        以下过程采用前期学习的属性配置方式进行，当前操作有助于理解原理篇中的很多知识。</p><ul><li><p>定义配置类，加载必要的配置属性，读取配置文件中memcached节点信息</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">@Component</span></span><br><span class="line"><span class="meta">@ConfigurationProperties(prefix = &quot;memcached&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_">XMemcachedProperties</span> &#123;</span><br><span class="line">    <span class="keyword">private</span> String servers;</span><br><span class="line">    <span class="keyword">private</span> <span class="type">int</span> poolSize;</span><br><span class="line">    <span class="keyword">private</span> <span class="type">long</span> opTimeout;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></li><li><p>定义memcached节点信息</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="attr">memcached:</span></span><br><span class="line">  <span class="attr">servers:</span> <span class="string">localhost:11211</span></span><br><span class="line">  <span class="attr">poolSize:</span> <span class="number">10</span></span><br><span class="line">  <span class="attr">opTimeout:</span> <span class="number">3000</span></span><br></pre></td></tr></table></figure></li><li><p>在memcached配置类中加载信息</p></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></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_">XMemcachedConfig</span> &#123;</span><br><span class="line">    <span class="meta">@Autowired</span></span><br><span class="line">    <span class="keyword">private</span> XMemcachedProperties props;</span><br><span class="line">    <span class="meta">@Bean</span></span><br><span class="line">    <span class="keyword">public</span> MemcachedClient <span class="title function_">getMemcachedClient</span><span class="params">()</span> <span class="keyword">throws</span> IOException &#123;</span><br><span class="line">        <span class="type">MemcachedClientBuilder</span> <span class="variable">memcachedClientBuilder</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">XMemcachedClientBuilder</span>(props.getServers());</span><br><span class="line">        memcachedClientBuilder.setConnectionPoolSize(props.getPoolSize());</span><br><span class="line">        memcachedClientBuilder.setOpTimeout(props.getOpTimeout());</span><br><span class="line">        <span class="type">MemcachedClient</span> <span class="variable">memcachedClient</span> <span class="operator">=</span> memcachedClientBuilder.build();</span><br><span class="line">        <span class="keyword">return</span> memcachedClient;</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>memcached安装后需要启动对应服务才可以对外提供缓存功能，安装memcached服务需要基于windows系统管理员权限</li><li>由于springboot没有提供对memcached的缓存整合方案，需要采用手工编码的形式创建xmemcached客户端操作缓存</li><li>导入xmemcached坐标后，创建memcached配置类，注册MemcachedClient对应的bean，用于操作缓存</li><li>初始化MemcachedClient对象所需要使用的属性可以通过自定义配置属性类的形式加载</li></ol><p><strong>思考</strong></p><p>​        到这里已经完成了三种缓存的整合，其中redis和mongodb需要安装独立的服务器，连接时需要输入对应的服务器地址，这种是远程缓存，Ehcache是一个典型的内存级缓存，因为它什么也不用安装，启动后导入jar包就有缓存功能了。这个时候就要问了，能不能这两种缓存一起用呢？咱们下节再说。</p><h4 id="SpringBoot整合jetcache缓存"><a href="#SpringBoot整合jetcache缓存" class="headerlink" title="SpringBoot整合jetcache缓存"></a>SpringBoot整合jetcache缓存</h4><p>​        目前我们使用的缓存都是要么A要么B，能不能AB一起用呢？这一节就解决这个问题。springboot针对缓存的整合仅仅停留在用缓存上面，如果缓存自身不支持同时支持AB一起用，springboot也没办法，所以要想解决AB缓存一起用的问题，就必须找一款缓存能够支持AB两种缓存一起用，有这种缓存吗？还真有，阿里出品，jetcache。</p><p>​        jetcache严格意义上来说，并不是一个缓存解决方案，只能说他算是一个缓存框架，然后把别的缓存放到jetcache中管理，这样就可以支持AB缓存一起用了。并且jetcache参考了springboot整合缓存的思想，整体技术使用方式和springboot的缓存解决方案思想非常类似。下面咱们就先把jetcache用起来，然后再说它里面的一些小的功能。</p><p>​        做之前要先明确一下，jetcache并不是随便拿两个缓存都能拼到一起去的。目前jetcache支持的缓存方案本地缓存支持两种，远程缓存支持两种，分别如下：</p><ul><li>本地缓存（Local）<ul><li>LinkedHashMap</li><li>Caffeine</li></ul></li><li>远程缓存（Remote）<ul><li>Redis</li><li>Tair</li></ul></li></ul><p>​        其实也有人问我，为什么jetcache只支持2+2这么4款缓存呢？阿里研发这个技术其实主要是为了满足自身的使用需要。最初肯定只有1+1种，逐步变化成2+2种。下面就以LinkedHashMap+Redis的方案实现本地与远程缓存方案同时使用。</p><h5 id="纯远程方案"><a href="#纯远程方案" class="headerlink" title="纯远程方案"></a>纯远程方案</h5><p><strong>步骤①</strong>：导入springboot整合jetcache对应的坐标starter，当前坐标默认使用的远程方案是redis</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.alicp.jetcache<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>jetcache-starter-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>2.6.2<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></pre></td><td class="code"><pre><span class="line"><span class="attr">jetcache:</span></span><br><span class="line">  <span class="attr">remote:</span></span><br><span class="line">    <span class="attr">default:</span></span><br><span class="line">      <span class="attr">type:</span> <span class="string">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">poolConfig:</span></span><br><span class="line">        <span class="attr">maxTotal:</span> <span class="number">50</span></span><br></pre></td></tr></table></figure><p>​        其中poolConfig是必配项，否则会报错</p><p><strong>步骤③</strong>：启用缓存，在引导类上方标注注解@EnableCreateCacheAnnotation配置springboot程序中可以使用注解的形式创建缓存</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="comment">//jetcache启用缓存的主开关</span></span><br><span class="line"><span class="meta">@EnableCreateCacheAnnotation</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">Springboot20JetCacheApplication</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(Springboot20JetCacheApplication.class, args);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p><strong>步骤④</strong>：创建缓存对象Cache，并使用注解@CreateCache标记当前缓存的信息，然后使用Cache对象的API操作缓存，put写缓存，get读缓存。</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></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_">SMSCodeServiceImpl</span> <span class="keyword">implements</span> <span class="title class_">SMSCodeService</span> &#123;</span><br><span class="line">    <span class="meta">@Autowired</span></span><br><span class="line">    <span class="keyword">private</span> CodeUtils codeUtils;</span><br><span class="line">    </span><br><span class="line">    <span class="meta">@CreateCache(name=&quot;jetCache_&quot;,expire = 10,timeUnit = TimeUnit.SECONDS)</span></span><br><span class="line">    <span class="keyword">private</span> Cache&lt;String ,String&gt; jetCache;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">public</span> String <span class="title function_">sendCodeToSMS</span><span class="params">(String tele)</span> &#123;</span><br><span class="line">        <span class="type">String</span> <span class="variable">code</span> <span class="operator">=</span> codeUtils.generator(tele);</span><br><span class="line">        jetCache.put(tele,code);</span><br><span class="line">        <span class="keyword">return</span> code;</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_">checkCode</span><span class="params">(SMSCode smsCode)</span> &#123;</span><br><span class="line">        <span class="type">String</span> <span class="variable">code</span> <span class="operator">=</span> jetCache.get(smsCode.getTele());</span><br><span class="line">        <span class="keyword">return</span> smsCode.getCode().equals(code);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>​        通过上述jetcache使用远程方案连接redis可以看出，jetcache操作缓存时的接口操作更符合开发者习惯，使用缓存就先获取缓存对象Cache，放数据进去就是put，取数据出来就是get，更加简单易懂。并且jetcache操作缓存时，可以为某个缓存对象设置过期时间，将同类型的数据放入缓存中，方便有效周期的管理。</p><p>​        上述方案中使用的是配置中定义的default缓存，其实这个default是个名字，可以随便写，也可以随便加。例如再添加一种缓存解决方案，参照如下配置进行：</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></pre></td><td class="code"><pre><span class="line"><span class="attr">jetcache:</span></span><br><span class="line">  <span class="attr">remote:</span></span><br><span class="line">    <span class="attr">default:</span></span><br><span class="line">      <span class="attr">type:</span> <span class="string">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">poolConfig:</span></span><br><span class="line">        <span class="attr">maxTotal:</span> <span class="number">50</span></span><br><span class="line">    <span class="attr">sms:</span></span><br><span class="line">      <span class="attr">type:</span> <span class="string">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">poolConfig:</span></span><br><span class="line">        <span class="attr">maxTotal:</span> <span class="number">50</span></span><br></pre></td></tr></table></figure><p>​        如果想使用名称是sms的缓存，需要再创建缓存时指定参数area，声明使用对应缓存即可</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></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_">SMSCodeServiceImpl</span> <span class="keyword">implements</span> <span class="title class_">SMSCodeService</span> &#123;</span><br><span class="line">    <span class="meta">@Autowired</span></span><br><span class="line">    <span class="keyword">private</span> CodeUtils codeUtils;</span><br><span class="line">    </span><br><span class="line">    <span class="meta">@CreateCache(area=&quot;sms&quot;,name=&quot;jetCache_&quot;,expire = 10,timeUnit = TimeUnit.SECONDS)</span></span><br><span class="line">    <span class="keyword">private</span> Cache&lt;String ,String&gt; jetCache;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">public</span> String <span class="title function_">sendCodeToSMS</span><span class="params">(String tele)</span> &#123;</span><br><span class="line">        <span class="type">String</span> <span class="variable">code</span> <span class="operator">=</span> codeUtils.generator(tele);</span><br><span class="line">        jetCache.put(tele,code);</span><br><span class="line">        <span class="keyword">return</span> code;</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_">checkCode</span><span class="params">(SMSCode smsCode)</span> &#123;</span><br><span class="line">        <span class="type">String</span> <span class="variable">code</span> <span class="operator">=</span> jetCache.get(smsCode.getTele());</span><br><span class="line">        <span class="keyword">return</span> smsCode.getCode().equals(code);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h5 id="纯本地方案"><a href="#纯本地方案" class="headerlink" title="纯本地方案"></a>纯本地方案</h5><p>​        远程方案中，配置中使用remote表示远程，换成local就是本地，只不过类型不一样而已。</p><p><strong>步骤①</strong>：导入springboot整合jetcache对应的坐标starter</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.alicp.jetcache<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>jetcache-starter-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>2.6.2<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></pre></td><td class="code"><pre><span class="line"><span class="attr">jetcache:</span></span><br><span class="line">  <span class="attr">local:</span></span><br><span class="line">    <span class="attr">default:</span></span><br><span class="line">      <span class="attr">type:</span> <span class="string">linkedhashmap</span></span><br><span class="line">      <span class="attr">keyConvertor:</span> <span class="string">fastjson</span></span><br></pre></td></tr></table></figure><p>​        为了加速数据获取时key的匹配速度，jetcache要求指定key的类型转换器。简单说就是，如果你给了一个Object作为key的话，我先用key的类型转换器给转换成字符串，然后再保存。等到获取数据时，仍然是先使用给定的Object转换成字符串，然后根据字符串匹配。由于jetcache是阿里的技术，这里推荐key的类型转换器使用阿里的fastjson。</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></pre></td><td class="code"><pre><span class="line"><span class="meta">@SpringBootApplication</span></span><br><span class="line"><span class="comment">//jetcache启用缓存的主开关</span></span><br><span class="line"><span class="meta">@EnableCreateCacheAnnotation</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">Springboot20JetCacheApplication</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(Springboot20JetCacheApplication.class, args);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p><strong>步骤④</strong>：创建缓存对象Cache时，标注当前使用本地缓存</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="meta">@Service</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">SMSCodeServiceImpl</span> <span class="keyword">implements</span> <span class="title class_">SMSCodeService</span> &#123;</span><br><span class="line">    <span class="meta">@CreateCache(name=&quot;jetCache_&quot;,expire = 1000,timeUnit = TimeUnit.SECONDS,cacheType = CacheType.LOCAL)</span></span><br><span class="line">    <span class="keyword">private</span> Cache&lt;String ,String&gt; jetCache;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">public</span> String <span class="title function_">sendCodeToSMS</span><span class="params">(String tele)</span> &#123;</span><br><span class="line">        <span class="type">String</span> <span class="variable">code</span> <span class="operator">=</span> codeUtils.generator(tele);</span><br><span class="line">        jetCache.put(tele,code);</span><br><span class="line">        <span class="keyword">return</span> code;</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_">checkCode</span><span class="params">(SMSCode smsCode)</span> &#123;</span><br><span class="line">        <span class="type">String</span> <span class="variable">code</span> <span class="operator">=</span> jetCache.get(smsCode.getTele());</span><br><span class="line">        <span class="keyword">return</span> smsCode.getCode().equals(code);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>​        cacheType控制当前缓存使用本地缓存还是远程缓存，配置cacheType=CacheType.LOCAL即使用本地缓存。</p><h5 id="本地-远程方案"><a href="#本地-远程方案" class="headerlink" title="本地+远程方案"></a>本地+远程方案</h5><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></pre></td><td class="code"><pre><span class="line"><span class="attr">jetcache:</span></span><br><span class="line">  <span class="attr">local:</span></span><br><span class="line">    <span class="attr">default:</span></span><br><span class="line">      <span class="attr">type:</span> <span class="string">linkedhashmap</span></span><br><span class="line">      <span class="attr">keyConvertor:</span> <span class="string">fastjson</span></span><br><span class="line">  <span class="attr">remote:</span></span><br><span class="line">    <span class="attr">default:</span></span><br><span class="line">      <span class="attr">type:</span> <span class="string">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">poolConfig:</span></span><br><span class="line">        <span class="attr">maxTotal:</span> <span class="number">50</span></span><br><span class="line">    <span class="attr">sms:</span></span><br><span class="line">      <span class="attr">type:</span> <span class="string">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">poolConfig:</span></span><br><span class="line">        <span class="attr">maxTotal:</span> <span class="number">50</span></span><br></pre></td></tr></table></figure><p>​        在创建缓存的时候，配置cacheType为BOTH即则本地缓存与远程缓存同时使用。</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></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_">SMSCodeServiceImpl</span> <span class="keyword">implements</span> <span class="title class_">SMSCodeService</span> &#123;</span><br><span class="line">    <span class="meta">@CreateCache(name=&quot;jetCache_&quot;,expire = 1000,timeUnit = TimeUnit.SECONDS,cacheType = CacheType.BOTH)</span></span><br><span class="line">    <span class="keyword">private</span> Cache&lt;String ,String&gt; jetCache;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>​        cacheType如果不进行配置，默认值是REMOTE，即仅使用远程缓存方案。关于jetcache的配置，参考以下信息</p><div class="table-container"><table><thead><tr><th>属性</th><th>默认值</th><th>说明</th></tr></thead><tbody><tr><td>jetcache.statIntervalMinutes</td><td>0</td><td>统计间隔，0表示不统计</td></tr><tr><td>jetcache.hiddenPackages</td><td>无</td><td>自动生成name时，隐藏指定的包名前缀</td></tr><tr><td>jetcache.[local\</td><td>remote].${area}.type</td><td>无</td><td>缓存类型，本地支持linkedhashmap、caffeine，远程支持redis、tair</td></tr><tr><td>jetcache.[local\</td><td>remote].${area}.keyConvertor</td><td>无</td><td>key转换器，当前仅支持fastjson</td></tr><tr><td>jetcache.[local\</td><td>remote].${area}.valueEncoder</td><td>java</td><td>仅remote类型的缓存需要指定，可选java和kryo</td></tr><tr><td>jetcache.[local\</td><td>remote].${area}.valueDecoder</td><td>java</td><td>仅remote类型的缓存需要指定，可选java和kryo</td></tr><tr><td>jetcache.[local\</td><td>remote].${area}.limit</td><td>100</td><td>仅local类型的缓存需要指定，缓存实例最大元素数</td></tr><tr><td>jetcache.[local\</td><td>remote].${area}.expireAfterWriteInMillis</td><td>无穷大</td><td>默认过期时间，毫秒单位</td></tr><tr><td>jetcache.local.${area}.expireAfterAccessInMillis</td><td>0</td><td>仅local类型的缓存有效，毫秒单位，最大不活动间隔</td></tr></tbody></table></div><p>​        以上方案仅支持手工控制缓存，但是springcache方案中的方法缓存特别好用，给一个方法添加一个注解，方法就会自动使用缓存。jetcache也提供了对应的功能，即方法缓存。</p><p><strong>方法缓存</strong></p><p>​        jetcache提供了方法缓存方案，只不过名称变更了而已。在对应的操作接口上方使用注解@Cached即可</p><p><strong>步骤①</strong>：导入springboot整合jetcache对应的坐标starter</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.alicp.jetcache<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>jetcache-starter-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>2.6.2<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><span class="line">20</span><br><span class="line">21</span><br></pre></td><td class="code"><pre><span class="line"><span class="attr">jetcache:</span></span><br><span class="line">  <span class="attr">local:</span></span><br><span class="line">    <span class="attr">default:</span></span><br><span class="line">      <span class="attr">type:</span> <span class="string">linkedhashmap</span></span><br><span class="line">      <span class="attr">keyConvertor:</span> <span class="string">fastjson</span></span><br><span class="line">  <span class="attr">remote:</span></span><br><span class="line">    <span class="attr">default:</span></span><br><span class="line">      <span class="attr">type:</span> <span class="string">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">keyConvertor:</span> <span class="string">fastjson</span></span><br><span class="line">      <span class="attr">valueEncode:</span> <span class="string">java</span></span><br><span class="line">      <span class="attr">valueDecode:</span> <span class="string">java</span></span><br><span class="line">      <span class="attr">poolConfig:</span></span><br><span class="line">        <span class="attr">maxTotal:</span> <span class="number">50</span></span><br><span class="line">    <span class="attr">sms:</span></span><br><span class="line">      <span class="attr">type:</span> <span class="string">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">poolConfig:</span></span><br><span class="line">        <span class="attr">maxTotal:</span> <span class="number">50</span></span><br></pre></td></tr></table></figure><p>​        由于redis缓存中不支持保存对象，因此需要对redis设置当Object类型数据进入到redis中时如何进行类型转换。需要配置keyConvertor表示key的类型转换方式，同时标注value的转换类型方式，值进入redis时是java类型，标注valueEncode为java，值从redis中读取时转换成java，标注valueDecode为java。</p><p>​        注意，为了实现Object类型的值进出redis，需要保障进出redis的Object类型的数据必须实现序列化接口。</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="meta">@Data</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">Book</span> <span class="keyword">implements</span> <span class="title class_">Serializable</span> &#123;</span><br><span class="line">    <span class="keyword">private</span> Integer id;</span><br><span class="line">    <span class="keyword">private</span> String type;</span><br><span class="line">    <span class="keyword">private</span> String name;</span><br><span class="line">    <span class="keyword">private</span> String description;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p><strong>步骤③</strong>：启用缓存时开启方法缓存功能，并配置basePackages，说明在哪些包中开启方法缓存</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">@SpringBootApplication</span></span><br><span class="line"><span class="comment">//jetcache启用缓存的主开关</span></span><br><span class="line"><span class="meta">@EnableCreateCacheAnnotation</span></span><br><span class="line"><span class="comment">//开启方法注解缓存</span></span><br><span class="line"><span class="meta">@EnableMethodCache(basePackages = &quot;com.itheima&quot;)</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">Springboot20JetCacheApplication</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(Springboot20JetCacheApplication.class, args);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p><strong>步骤④</strong>：使用注解@Cached标注当前方法使用缓存</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="meta">@Service</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">BookServiceImpl</span> <span class="keyword">implements</span> <span class="title class_">BookService</span> &#123;</span><br><span class="line">    <span class="meta">@Autowired</span></span><br><span class="line">    <span class="keyword">private</span> BookDao bookDao;</span><br><span class="line">    </span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="meta">@Cached(name=&quot;book_&quot;,key=&quot;#id&quot;,expire = 3600,cacheType = CacheType.REMOTE)</span></span><br><span class="line">    <span class="keyword">public</span> Book <span class="title function_">getById</span><span class="params">(Integer id)</span> &#123;</span><br><span class="line">        <span class="keyword">return</span> bookDao.selectById(id);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h5 id="远程方案的数据同步"><a href="#远程方案的数据同步" class="headerlink" title="远程方案的数据同步"></a>远程方案的数据同步</h5><p>​        由于远程方案中redis保存的数据可以被多个客户端共享，这就存在了数据同步问题。jetcache提供了3个注解解决此问题，分别在更新、删除操作时同步缓存数据，和读取缓存时定时刷新数据</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></pre></td><td class="code"><pre><span class="line"><span class="meta">@CacheUpdate(name=&quot;book_&quot;,key=&quot;#book.id&quot;,value=&quot;#book&quot;)</span></span><br><span class="line"><span class="keyword">public</span> <span class="type">boolean</span> <span class="title function_">update</span><span class="params">(Book book)</span> &#123;</span><br><span class="line">    <span class="keyword">return</span> bookDao.updateById(book) &gt; <span class="number">0</span>;</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></pre></td><td class="code"><pre><span class="line"><span class="meta">@CacheInvalidate(name=&quot;book_&quot;,key = &quot;#id&quot;)</span></span><br><span class="line"><span class="keyword">public</span> <span class="type">boolean</span> <span class="title function_">delete</span><span class="params">(Integer id)</span> &#123;</span><br><span class="line">    <span class="keyword">return</span> bookDao.deleteById(id) &gt; <span class="number">0</span>;</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></pre></td><td class="code"><pre><span class="line"><span class="meta">@Cached(name=&quot;book_&quot;,key=&quot;#id&quot;,expire = 3600,cacheType = CacheType.REMOTE)</span></span><br><span class="line"><span class="meta">@CacheRefresh(refresh = 5)</span></span><br><span class="line"><span class="keyword">public</span> Book <span class="title function_">getById</span><span class="params">(Integer id)</span> &#123;</span><br><span class="line">    <span class="keyword">return</span> bookDao.selectById(id);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h5 id="数据报表"><a href="#数据报表" class="headerlink" title="数据报表"></a>数据报表</h5><p>​        jetcache还提供有简单的数据报表功能，帮助开发者快速查看缓存命中信息，只需要添加一个配置即可</p><figure class="highlight yaml"><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="attr">jetcache:</span></span><br><span class="line">  <span class="attr">statIntervalMinutes:</span> <span class="number">1</span></span><br></pre></td></tr></table></figure><p>​        设置后，每1分钟在控制台输出缓存数据命中信息</p><figure class="highlight cmd"><table><tr><td class="gutter"><pre><span class="line">1</span><br><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">[DefaultExecutor] c.alicp.jetcache.support.StatInfoLogger  : jetcache stat from <span class="number">2022</span>-<span class="number">02</span>-<span class="number">28</span> <span class="number">09</span>:<span class="number">32</span>:<span class="number">15</span>,<span class="number">892</span> to <span class="number">2022</span>-<span class="number">02</span>-<span class="number">28</span> <span class="number">09</span>:<span class="number">33</span>:<span class="number">00</span>,<span class="number">003</span></span><br><span class="line">cache    |    qps|   rate|   get|    hit|   fail|   expire|   avgLoadTime|   maxLoadTime</span><br><span class="line">---------+-------+-------+------+-------+-------+---------+--------------+--------------</span><br><span class="line">book_    |   <span class="number">0</span>.<span class="number">66</span>| <span class="number">75</span>.<span class="number">86</span>%|    <span class="number">29</span>|     <span class="number">22</span>|      <span class="number">0</span>|        <span class="number">0</span>|          <span class="number">28</span>.<span class="number">0</span>|           <span class="number">188</span></span><br><span class="line">---------+-------+-------+------+-------+-------+---------+--------------+--------------</span><br></pre></td></tr></table></figure><p><strong>总结</strong></p><ol><li>jetcache是一个类似于springcache的缓存解决方案，自身不具有缓存功能，它提供有本地缓存与远程缓存多级共同使用的缓存解决方案</li><li>jetcache提供的缓存解决方案受限于目前支持的方案，本地缓存支持两种，远程缓存支持两种</li><li>注意数据进入远程缓存时的类型转换问题</li><li>jetcache提供方法缓存，并提供了对应的缓存更新与刷新功能</li><li>jetcache提供有简单的缓存信息命中报表方便开发者即时监控缓存数据命中情况</li></ol><p><strong>思考</strong></p><p>​        jetcache解决了前期使用缓存方案单一的问题，但是仍然不能灵活的选择缓存进行搭配使用，是否存在一种技术可以灵活的搭配各种各样的缓存使用呢？有，咱们下一节再讲。</p><h4 id="SpringBoot整合j2cache缓存"><a href="#SpringBoot整合j2cache缓存" class="headerlink" title="SpringBoot整合j2cache缓存"></a>SpringBoot整合j2cache缓存</h4><p>​        jetcache可以在限定范围内构建多级缓存，但是灵活性不足，不能随意搭配缓存，本节介绍一种可以随意搭配缓存解决方案的缓存整合框架，j2cache。下面就来讲解如何使用这种缓存框架，以Ehcache与redis整合为例：</p><p><strong>步骤①</strong>：导入j2cache、redis、ehcache坐标</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></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>net.oschina.j2cache<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>j2cache-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>2.8.4-release<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>net.oschina.j2cache<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>j2cache-spring-boot2-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.8.0-release<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>net.sf.ehcache<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>ehcache<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>​        j2cache的starter中默认包含了redis坐标，官方推荐使用redis作为二级缓存，因此此处无需导入redis坐标</p><p><strong>步骤②</strong>：配置一级与二级缓存，并配置一二级缓存间数据传递方式，配置书写在名称为j2cache.properties的文件中。如果使用ehcache还需要单独添加ehcache的配置文件</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></pre></td><td class="code"><pre><span class="line"><span class="comment"># 1级缓存</span></span><br><span class="line"><span class="string">j2cache.L1.provider_class</span> <span class="string">=</span> <span class="string">ehcache</span></span><br><span class="line"><span class="string">ehcache.configXml</span> <span class="string">=</span> <span class="string">ehcache.xml</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># 2级缓存</span></span><br><span class="line"><span class="string">j2cache.L2.provider_class</span> <span class="string">=</span> <span class="string">net.oschina.j2cache.cache.support.redis.SpringRedisProvider</span></span><br><span class="line"><span class="string">j2cache.L2.config_section</span> <span class="string">=</span> <span class="string">redis</span></span><br><span class="line"><span class="string">redis.hosts</span> <span class="string">=</span> <span class="string">localhost:6379</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># 1级缓存中的数据如何到达二级缓存</span></span><br><span class="line"><span class="string">j2cache.broadcast</span> <span class="string">=</span> <span class="string">net.oschina.j2cache.cache.support.redis.SpringRedisPubSubPolicy</span></span><br></pre></td></tr></table></figure><p>​        此处配置不能乱配置，需要参照官方给出的配置说明进行。例如1级供应商选择ehcache，供应商名称仅仅是一个ehcache，但是2级供应商选择redis时要写专用的Spring整合Redis的供应商类名SpringRedisProvider，而且这个名称并不是所有的redis包中能提供的，也不是spring包中提供的。因此配置j2cache必须参照官方文档配置，而且还要去找专用的整合包，导入对应坐标才可以使用。</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></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_">SMSCodeServiceImpl</span> <span class="keyword">implements</span> <span class="title class_">SMSCodeService</span> &#123;</span><br><span class="line">    <span class="meta">@Autowired</span></span><br><span class="line">    <span class="keyword">private</span> CodeUtils codeUtils;</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> CacheChannel cacheChannel;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">public</span> String <span class="title function_">sendCodeToSMS</span><span class="params">(String tele)</span> &#123;</span><br><span class="line">        <span class="type">String</span> <span class="variable">code</span> <span class="operator">=</span> codeUtils.generator(tele);</span><br><span class="line">        cacheChannel.set(<span class="string">&quot;sms&quot;</span>,tele,code);</span><br><span class="line">        <span class="keyword">return</span> code;</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_">checkCode</span><span class="params">(SMSCode smsCode)</span> &#123;</span><br><span class="line">        <span class="type">String</span> <span class="variable">code</span> <span class="operator">=</span> cacheChannel.get(<span class="string">&quot;sms&quot;</span>,smsCode.getTele()).asString();</span><br><span class="line">        <span class="keyword">return</span> smsCode.getCode().equals(code);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>​        j2cache的使用和jetcache比较类似，但是无需开启使用的开关，直接定义缓存对象即可使用，缓存对象名CacheChannel。</p><p>​        j2cache的使用不复杂，配置是j2cache的核心，毕竟是一个整合型的缓存框架。缓存相关的配置过多，可以查阅j2cache-core核心包中的j2cache.properties文件中的说明。如下：</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><span class="line">20</span><br><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></pre></td><td class="code"><pre><span class="line"><span class="comment">#J2Cache configuration</span></span><br><span class="line"><span class="comment">#########################################</span></span><br><span class="line"><span class="comment"># Cache Broadcast Method</span></span><br><span class="line"><span class="comment"># values:</span></span><br><span class="line"><span class="comment"># jgroups -&gt; use jgroups&#x27;s multicast</span></span><br><span class="line"><span class="comment"># redis -&gt; use redis publish/subscribe mechanism (using jedis)</span></span><br><span class="line"><span class="comment"># lettuce -&gt; use redis publish/subscribe mechanism (using lettuce, Recommend)</span></span><br><span class="line"><span class="comment"># rabbitmq -&gt; use RabbitMQ publisher/consumer mechanism</span></span><br><span class="line"><span class="comment"># rocketmq -&gt; use RocketMQ publisher/consumer mechanism</span></span><br><span class="line"><span class="comment"># none -&gt; don&#x27;t notify the other nodes in cluster</span></span><br><span class="line"><span class="comment"># xx.xxxx.xxxx.Xxxxx your own cache broadcast policy classname that implement net.oschina.j2cache.cluster.ClusterPolicy</span></span><br><span class="line"><span class="comment">#########################################</span></span><br><span class="line"><span class="attr">j2cache.broadcast</span> = <span class="string">redis</span></span><br><span class="line"><span class="comment"></span></span><br><span class="line"><span class="comment"># jgroups properties</span></span><br><span class="line"><span class="attr">jgroups.channel.name</span> = <span class="string">j2cache</span></span><br><span class="line"><span class="attr">jgroups.configXml</span> = <span class="string">/network.xml</span></span><br><span class="line"><span class="comment"></span></span><br><span class="line"><span class="comment"># RabbitMQ properties</span></span><br><span class="line"><span class="attr">rabbitmq.exchange</span> = <span class="string">j2cache</span></span><br><span class="line"><span class="attr">rabbitmq.host</span> = <span class="string">localhost</span></span><br><span class="line"><span class="attr">rabbitmq.port</span> = <span class="string">5672</span></span><br><span class="line"><span class="attr">rabbitmq.username</span> = <span class="string">guest</span></span><br><span class="line"><span class="attr">rabbitmq.password</span> = <span class="string">guest</span></span><br><span class="line"><span class="comment"></span></span><br><span class="line"><span class="comment"># RocketMQ properties</span></span><br><span class="line"><span class="attr">rocketmq.name</span> = <span class="string">j2cache</span></span><br><span class="line"><span class="attr">rocketmq.topic</span> = <span class="string">j2cache</span></span><br><span class="line"><span class="comment"># use ; to split multi hosts</span></span><br><span class="line"><span class="attr">rocketmq.hosts</span> = <span class="string">127.0.0.1:9876</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"># Level 1&amp;2 provider</span></span><br><span class="line"><span class="comment"># values:</span></span><br><span class="line"><span class="comment"># none -&gt; disable this level cache</span></span><br><span class="line"><span class="comment"># ehcache -&gt; use ehcache2 as level 1 cache</span></span><br><span class="line"><span class="comment"># ehcache3 -&gt; use ehcache3 as level 1 cache</span></span><br><span class="line"><span class="comment"># caffeine -&gt; use caffeine as level 1 cache(only in memory)</span></span><br><span class="line"><span class="comment"># redis -&gt; use redis as level 2 cache (using jedis)</span></span><br><span class="line"><span class="comment"># lettuce -&gt; use redis as level 2 cache (using lettuce)</span></span><br><span class="line"><span class="comment"># readonly-redis -&gt; use redis as level 2 cache ,but never write data to it. if use this provider, you must uncomment `j2cache.L2.config_section` to make the redis configurations available.</span></span><br><span class="line"><span class="comment"># memcached -&gt; use memcached as level 2 cache (xmemcached),</span></span><br><span class="line"><span class="comment"># [classname] -&gt; use custom provider</span></span><br><span class="line"><span class="comment">#########################################</span></span><br><span class="line"></span><br><span class="line"><span class="attr">j2cache.L1.provider_class</span> = <span class="string">caffeine</span></span><br><span class="line"><span class="attr">j2cache.L2.provider_class</span> = <span class="string">redis</span></span><br><span class="line"><span class="comment"></span></span><br><span class="line"><span class="comment"># When L2 provider isn&#x27;t `redis`, using `L2.config_section = redis` to read redis configurations</span></span><br><span class="line"><span class="comment"># j2cache.L2.config_section = redis</span></span><br><span class="line"><span class="comment"></span></span><br><span class="line"><span class="comment"># Enable/Disable ttl in redis cache data (if disabled, the object in redis will never expire, default:true)</span></span><br><span class="line"><span class="comment"># NOTICE: redis hash mode (redis.storage = hash) do not support this feature)</span></span><br><span class="line"><span class="attr">j2cache.sync_ttl_to_redis</span> = <span class="string">true</span></span><br><span class="line"><span class="comment"></span></span><br><span class="line"><span class="comment"># Whether to cache null objects by default (default false)</span></span><br><span class="line"><span class="attr">j2cache.default_cache_null_object</span> = <span class="string">true</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"># Cache Serialization Provider</span></span><br><span class="line"><span class="comment"># values:</span></span><br><span class="line"><span class="comment"># fst -&gt; using fast-serialization (recommend)</span></span><br><span class="line"><span class="comment"># kryo -&gt; using kryo serialization</span></span><br><span class="line"><span class="comment"># json -&gt; using fst&#x27;s json serialization (testing)</span></span><br><span class="line"><span class="comment"># fastjson -&gt; using fastjson serialization (embed non-static class not support)</span></span><br><span class="line"><span class="comment"># java -&gt; java standard</span></span><br><span class="line"><span class="comment"># fse -&gt; using fse serialization</span></span><br><span class="line"><span class="comment"># [classname implements Serializer]</span></span><br><span class="line"><span class="comment">#########################################</span></span><br><span class="line"></span><br><span class="line"><span class="attr">j2cache.serialization</span> = <span class="string">json</span></span><br><span class="line"><span class="comment">#json.map.person = net.oschina.j2cache.demo.Person</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"># Ehcache configuration</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"># ehcache.configXml = /ehcache.xml</span></span><br><span class="line"><span class="comment"></span></span><br><span class="line"><span class="comment"># ehcache3.configXml = /ehcache3.xml</span></span><br><span class="line"><span class="comment"># ehcache3.defaultHeapSize = 1000</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"># Caffeine configuration</span></span><br><span class="line"><span class="comment"># caffeine.region.[name] = size, xxxx[s|m|h|d]</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">caffeine.properties</span> = <span class="string">/caffeine.properties</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"># Redis connection configuration</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"># Redis Cluster Mode</span></span><br><span class="line"><span class="comment">#</span></span><br><span class="line"><span class="comment"># single -&gt; single redis server</span></span><br><span class="line"><span class="comment"># sentinel -&gt; master-slaves servers</span></span><br><span class="line"><span class="comment"># cluster -&gt; cluster servers (数据库配置无效，使用 database = 0）</span></span><br><span class="line"><span class="comment"># sharded -&gt; sharded servers  (密码、数据库必须在 hosts 中指定，且连接池配置无效 ; redis://user:password@127.0.0.1:6379/0）</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="attr">redis.mode</span> = <span class="string">single</span></span><br><span class="line"><span class="comment"></span></span><br><span class="line"><span class="comment">#redis storage mode (generic|hash)</span></span><br><span class="line"><span class="attr">redis.storage</span> = <span class="string">generic</span></span><br><span class="line"><span class="comment"></span></span><br><span class="line"><span class="comment">## redis pub/sub channel name</span></span><br><span class="line"><span class="attr">redis.channel</span> = <span class="string">j2cache</span></span><br><span class="line"><span class="comment">## redis pub/sub server (using redis.hosts when empty)</span></span><br><span class="line"><span class="attr">redis.channel.host</span> =<span class="string"></span></span><br><span class="line"><span class="comment"></span></span><br><span class="line"><span class="comment">#cluster name just for sharded</span></span><br><span class="line"><span class="attr">redis.cluster_name</span> = <span class="string">j2cache</span></span><br><span class="line"><span class="comment"></span></span><br><span class="line"><span class="comment">## redis cache namespace optional, default[empty]</span></span><br><span class="line"><span class="attr">redis.namespace</span> =<span class="string"></span></span><br><span class="line"><span class="comment"></span></span><br><span class="line"><span class="comment">## redis command scan parameter count, default[1000]</span></span><br><span class="line"><span class="comment">#redis.scanCount = 1000</span></span><br><span class="line"><span class="comment"></span></span><br><span class="line"><span class="comment">## connection</span></span><br><span class="line"><span class="comment"># Separate multiple redis nodes with commas, such as 192.168.0.10:6379,192.168.0.11:6379,192.168.0.12:6379</span></span><br><span class="line"></span><br><span class="line"><span class="attr">redis.hosts</span> = <span class="string">127.0.0.1:6379</span></span><br><span class="line"><span class="attr">redis.timeout</span> = <span class="string">2000</span></span><br><span class="line"><span class="attr">redis.password</span> =<span class="string"></span></span><br><span class="line"><span class="attr">redis.database</span> = <span class="string">0</span></span><br><span class="line"><span class="attr">redis.ssl</span> = <span class="string">false</span></span><br><span class="line"><span class="comment"></span></span><br><span class="line"><span class="comment">## redis pool properties</span></span><br><span class="line"><span class="attr">redis.maxTotal</span> = <span class="string">100</span></span><br><span class="line"><span class="attr">redis.maxIdle</span> = <span class="string">10</span></span><br><span class="line"><span class="attr">redis.maxWaitMillis</span> = <span class="string">5000</span></span><br><span class="line"><span class="attr">redis.minEvictableIdleTimeMillis</span> = <span class="string">60000</span></span><br><span class="line"><span class="attr">redis.minIdle</span> = <span class="string">1</span></span><br><span class="line"><span class="attr">redis.numTestsPerEvictionRun</span> = <span class="string">10</span></span><br><span class="line"><span class="attr">redis.lifo</span> = <span class="string">false</span></span><br><span class="line"><span class="attr">redis.softMinEvictableIdleTimeMillis</span> = <span class="string">10</span></span><br><span class="line"><span class="attr">redis.testOnBorrow</span> = <span class="string">true</span></span><br><span class="line"><span class="attr">redis.testOnReturn</span> = <span class="string">false</span></span><br><span class="line"><span class="attr">redis.testWhileIdle</span> = <span class="string">true</span></span><br><span class="line"><span class="attr">redis.timeBetweenEvictionRunsMillis</span> = <span class="string">300000</span></span><br><span class="line"><span class="attr">redis.blockWhenExhausted</span> = <span class="string">false</span></span><br><span class="line"><span class="attr">redis.jmxEnabled</span> = <span class="string">false</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"># Lettuce scheme</span></span><br><span class="line"><span class="comment">#</span></span><br><span class="line"><span class="comment"># redis -&gt; single redis server</span></span><br><span class="line"><span class="comment"># rediss -&gt; single redis server with ssl</span></span><br><span class="line"><span class="comment"># redis-sentinel -&gt; redis sentinel</span></span><br><span class="line"><span class="comment"># redis-cluster -&gt; cluster servers</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"># Lettuce Mode</span></span><br><span class="line"><span class="comment">#</span></span><br><span class="line"><span class="comment"># single -&gt; single redis server</span></span><br><span class="line"><span class="comment"># sentinel -&gt; master-slaves servers</span></span><br><span class="line"><span class="comment"># cluster -&gt; cluster servers (数据库配置无效，使用 database = 0）</span></span><br><span class="line"><span class="comment"># sharded -&gt; sharded servers  (密码、数据库必须在 hosts 中指定，且连接池配置无效 ; redis://user:password@127.0.0.1:6379/0）</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">## redis command scan parameter count, default[1000]</span></span><br><span class="line"><span class="comment">#lettuce.scanCount = 1000</span></span><br><span class="line"><span class="attr">lettuce.mode</span> = <span class="string">single</span></span><br><span class="line"><span class="attr">lettuce.namespace</span> =<span class="string"></span></span><br><span class="line"><span class="attr">lettuce.storage</span> = <span class="string">hash</span></span><br><span class="line"><span class="attr">lettuce.channel</span> = <span class="string">j2cache</span></span><br><span class="line"><span class="attr">lettuce.scheme</span> = <span class="string">redis</span></span><br><span class="line"><span class="attr">lettuce.hosts</span> = <span class="string">127.0.0.1:6379</span></span><br><span class="line"><span class="attr">lettuce.password</span> =<span class="string"></span></span><br><span class="line"><span class="attr">lettuce.database</span> = <span class="string">0</span></span><br><span class="line"><span class="attr">lettuce.sentinelMasterId</span> =<span class="string"></span></span><br><span class="line"><span class="attr">lettuce.maxTotal</span> = <span class="string">100</span></span><br><span class="line"><span class="attr">lettuce.maxIdle</span> = <span class="string">10</span></span><br><span class="line"><span class="attr">lettuce.minIdle</span> = <span class="string">10</span></span><br><span class="line"><span class="comment"># timeout in milliseconds</span></span><br><span class="line"><span class="attr">lettuce.timeout</span> = <span class="string">10000</span></span><br><span class="line"><span class="comment"># redis cluster topology refresh interval in milliseconds</span></span><br><span class="line"><span class="attr">lettuce.clusterTopologyRefresh</span> = <span class="string">3000</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"># memcached server configurations</span></span><br><span class="line"><span class="comment"># refer to https://gitee.com/mirrors/XMemcached</span></span><br><span class="line"><span class="comment">#########################################</span></span><br><span class="line"></span><br><span class="line"><span class="attr">memcached.servers</span> = <span class="string">127.0.0.1:11211</span></span><br><span class="line"><span class="attr">memcached.username</span> =<span class="string"></span></span><br><span class="line"><span class="attr">memcached.password</span> =<span class="string"></span></span><br><span class="line"><span class="attr">memcached.connectionPoolSize</span> = <span class="string">10</span></span><br><span class="line"><span class="attr">memcached.connectTimeout</span> = <span class="string">1000</span></span><br><span class="line"><span class="attr">memcached.failureMode</span> = <span class="string">false</span></span><br><span class="line"><span class="attr">memcached.healSessionInterval</span> = <span class="string">1000</span></span><br><span class="line"><span class="attr">memcached.maxQueuedNoReplyOperations</span> = <span class="string">100</span></span><br><span class="line"><span class="attr">memcached.opTimeout</span> = <span class="string">100</span></span><br><span class="line"><span class="attr">memcached.sanitizeKeys</span> = <span class="string">false</span></span><br></pre></td></tr></table></figure><p><strong>总结</strong></p><ol><li>j2cache是一个缓存框架，自身不具有缓存功能，它提供多种缓存整合在一起使用的方案</li><li>j2cache需要通过复杂的配置设置各级缓存，以及缓存之间数据交换的方式</li><li>j2cache操作接口通过CacheChannel实现</li></ol><h3 id="KF-5-2-任务"><a href="#KF-5-2-任务" class="headerlink" title="KF-5-2.任务"></a>KF-5-2.任务</h3><p>​        springboot整合第三方技术第二部分我们来说说任务系统，其实这里说的任务系统指的是定时任务。定时任务是企业级开发中必不可少的组成部分，诸如长周期业务数据的计算，例如年度报表，诸如系统脏数据的处理，再比如系统性能监控报告，还有抢购类活动的商品上架，这些都离不开定时任务。本节将介绍两种不同的定时任务技术。</p><h4 id="Quartz"><a href="#Quartz" class="headerlink" title="Quartz"></a>Quartz</h4><p>​        Quartz技术是一个比较成熟的定时任务框架，怎么说呢？有点繁琐，用过的都知道，配置略微复杂。springboot对其进行整合后，简化了一系列的配置，将很多配置采用默认设置，这样开发阶段就简化了很多。再学习springboot整合Quartz前先普及几个Quartz的概念。</p><ul><li>工作（Job）：用于定义具体执行的工作</li><li>工作明细（JobDetail）：用于描述定时工作相关的信息</li><li>触发器（Trigger）：描述了工作明细与调度器的对应关系</li><li>调度器（Scheduler）：用于描述触发工作的执行规则，通常使用cron表达式定义规则</li></ul><p>​        简单说就是你定时干什么事情，这就是工作，工作不可能就是一个简单的方法，还要设置一些明细信息。工作啥时候执行，设置一个调度器，可以简单理解成设置一个工作执行的时间。工作和调度都是独立定义的，它们两个怎么配合到一起呢？用触发器。完了，就这么多。下面开始springboot整合Quartz。</p><p><strong>步骤①</strong>：导入springboot整合Quartz的starter</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.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-quartz<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>步骤②</strong>：定义任务Bean，按照Quartz的开发规范制作，继承QuartzJobBean</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></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">MyQuartz</span> <span class="keyword">extends</span> <span class="title class_">QuartzJobBean</span> &#123;</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_">executeInternal</span><span class="params">(JobExecutionContext context)</span> <span class="keyword">throws</span> JobExecutionException &#123;</span><br><span class="line">        System.out.println(<span class="string">&quot;quartz task run...&quot;</span>);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p><strong>步骤③</strong>：创建Quartz配置类，定义工作明细（JobDetail）与触发器的（Trigger）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></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_">QuartzConfig</span> &#123;</span><br><span class="line">    <span class="meta">@Bean</span></span><br><span class="line">    <span class="keyword">public</span> JobDetail <span class="title function_">printJobDetail</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> JobBuilder.newJob(MyQuartz.class).storeDurably().build();</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="meta">@Bean</span></span><br><span class="line">    <span class="keyword">public</span> Trigger <span class="title function_">printJobTrigger</span><span class="params">()</span>&#123;</span><br><span class="line">        <span class="type">ScheduleBuilder</span> <span class="variable">schedBuilder</span> <span class="operator">=</span> CronScheduleBuilder.cronSchedule(<span class="string">&quot;0/5 * * * * ?&quot;</span>);</span><br><span class="line">        <span class="comment">//绑定对应的工作明细</span></span><br><span class="line">        <span class="keyword">return</span> TriggerBuilder.newTrigger().forJob(printJobDetail()).withSchedule(schedBuilder).build();</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>​        工作明细中要设置对应的具体工作，使用newJob()操作传入对应的工作任务类型即可。</p><p>​        触发器需要绑定任务，使用forJob()操作传入绑定的工作明细对象。此处可以为工作明细设置名称然后使用名称绑定，也可以直接调用对应方法绑定。触发器中最核心的规则是执行时间，此处使用调度器定义执行时间，执行时间描述方式使用的是cron表达式。有关cron表达式的规则，各位小伙伴可以去参看相关课程学习，略微复杂，而且格式不能乱设置，不是写个格式就能用的，写不好就会出现冲突问题。</p><p><strong>总结</strong></p><ol><li>springboot整合Quartz就是将Quartz对应的核心对象交给spring容器管理，包含两个对象，JobDetail和Trigger对象</li><li>JobDetail对象描述的是工作的执行信息，需要绑定一个QuartzJobBean类型的对象</li><li>Trigger对象定义了一个触发器，需要为其指定绑定的JobDetail是哪个，同时要设置执行周期调度器</li></ol><p><strong>思考</strong></p><p>​        上面的操作看上去不多，但是Quartz将其中的对象划分粒度过细，导致开发的时候有点繁琐，spring针对上述规则进行了简化，开发了自己的任务管理组件——Task，如何用呢？咱们下节再说。</p><h4 id="Task"><a href="#Task" class="headerlink" title="Task"></a>Task</h4><p>​        spring根据定时任务的特征，将定时任务的开发简化到了极致。怎么说呢？要做定时任务总要告诉容器有这功能吧，然后定时执行什么任务直接告诉对应的bean什么时间执行就行了，就这么简单，一起来看怎么做</p><p><strong>步骤①</strong>：开启定时任务功能，在引导类上开启定时任务功能的开关，使用注解@EnableScheduling</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="comment">//开启定时任务功能</span></span><br><span class="line"><span class="meta">@EnableScheduling</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">Springboot22TaskApplication</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(Springboot22TaskApplication.class, args);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p><strong>步骤②</strong>：定义Bean，在对应要定时执行的操作上方，使用注解@Scheduled定义执行的时间，执行时间的描述方式还是cron表达式</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="meta">@Component</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">MyBean</span> &#123;</span><br><span class="line">    <span class="meta">@Scheduled(cron = &quot;0/1 * * * * ?&quot;)</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>&#123;</span><br><span class="line">        System.out.println(Thread.currentThread().getName()+<span class="string">&quot; :spring task run...&quot;</span>);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>​        完事，这就完成了定时任务的配置。总体感觉其实什么东西都没少，只不过没有将所有的信息都抽取成bean，而是直接使用注解绑定定时执行任务的事情而已。</p><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></pre></td><td class="code"><pre><span class="line"><span class="attr">spring:</span></span><br><span class="line">  <span class="attr">task:</span></span><br><span class="line">   <span class="attr">scheduling:</span></span><br><span class="line">      <span class="attr">pool:</span></span><br><span class="line">       <span class="attr">size:</span> <span class="number">1</span><span class="comment"># 任务调度线程池大小 默认 1</span></span><br><span class="line">      <span class="attr">thread-name-prefix:</span> <span class="string">ssm_</span>      <span class="comment"># 调度线程名称前缀 默认 scheduling-      </span></span><br><span class="line">        <span class="attr">shutdown:</span></span><br><span class="line">          <span class="attr">await-termination:</span> <span class="literal">false</span><span class="comment"># 线程池关闭时等待所有任务完成</span></span><br><span class="line">          <span class="attr">await-termination-period:</span> <span class="string">10s</span><span class="comment"># 调度线程关闭前最大等待时间，确保最后一定关闭</span></span><br></pre></td></tr></table></figure><p><strong>总结</strong></p><ol><li><p>spring task需要使用注解@EnableScheduling开启定时任务功能</p></li><li><p>为定时执行的的任务设置执行周期，描述方式cron表达式</p></li></ol><h3 id="KF-5-3-邮件"><a href="#KF-5-3-邮件" class="headerlink" title="KF-5-3.邮件"></a>KF-5-3.邮件</h3><p>​        springboot整合第三方技术第三部分我们来说说邮件系统，发邮件是java程序的基本操作，springboot整合javamail其实就是简化开发。不熟悉邮件的小伙伴可以先学习完javamail的基础操作，再来看这一部分内容才能感触到springboot整合javamail究竟简化了哪些操作。简化的多码？其实不多，差别不大，只是还个格式而已。</p><p>​        学习邮件发送之前先了解3个概念，这些概念规范了邮件操作过程中的标准。</p><ul><li>SMTP（Simple Mail Transfer Protocol）：简单邮件传输协议，用于<strong>发送</strong>电子邮件的传输协议</li><li>POP3（Post Office Protocol - Version 3）：用于<strong>接收</strong>电子邮件的标准协议</li><li>IMAP（Internet Mail Access Protocol）：互联网消息协议，是POP3的替代协议</li></ul><p>​        简单说就是SMPT是发邮件的标准，POP3是收邮件的标准，IMAP是对POP3的升级。我们制作程序中操作邮件，通常是发邮件，所以SMTP是使用的重点，收邮件大部分都是通过邮件客户端完成，所以开发收邮件的代码极少。除非你要读取邮件内容，然后解析，做邮件功能的统一处理。例如HR的邮箱收到求职者的简历，可以读取后统一处理。但是为什么不制作独立的投递简历的系统呢？所以说，好奇怪的需求，因为要想收邮件就要规范发邮件的人的书写格式，这个未免有点强人所难，并且极易收到外部攻击，你不可能使用白名单来收邮件。如果能使用白名单来收邮件然后解析邮件，还不如开发个系统给白名单中的人专用呢，更安全，总之就是鸡肋了。下面就开始学习springboot如何整合javamail发送邮件。</p><h4 id="发送简单邮件"><a href="#发送简单邮件" class="headerlink" title="发送简单邮件"></a>发送简单邮件</h4><p><strong>步骤①</strong>：导入springboot整合javamail的starter</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.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-mail<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>步骤②</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></pre></td><td class="code"><pre><span class="line"><span class="attr">spring:</span></span><br><span class="line">  <span class="attr">mail:</span></span><br><span class="line">    <span class="attr">host:</span> <span class="string">smtp.126.com</span></span><br><span class="line">    <span class="attr">username:</span> <span class="string">test@126.com</span></span><br><span class="line">    <span class="attr">password:</span> <span class="string">test</span></span><br></pre></td></tr></table></figure><p>​        java程序仅用于发送邮件，邮件的功能还是邮件供应商提供的，所以这里是用别人的邮件服务，要配置对应信息。</p><p>​        host配置的是提供邮件服务的主机协议，当前程序仅用于发送邮件，因此配置的是smtp的协议。</p><p>​        password并不是邮箱账号的登录密码，是邮件供应商提供的一个加密后的密码，也是为了保障系统安全性。不然外部人员通过地址访问下载了配置文件，直接获取到了邮件密码就会有极大的安全隐患。有关该密码的获取每个邮件供应商提供的方式都不一样，此处略过。可以到邮件供应商的设置页面找POP3或IMAP这些关键词找到对应的获取位置。下例仅供参考：</p><p><img src="img\image-20220228111251036.png" alt="image-20220228111251036"></p><p><strong>步骤③</strong>：使用JavaMailSender接口发送邮件</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">@Service</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">SendMailServiceImpl</span> <span class="keyword">implements</span> <span class="title class_">SendMailService</span> &#123;</span><br><span class="line">    <span class="meta">@Autowired</span></span><br><span class="line">    <span class="keyword">private</span> JavaMailSender javaMailSender;</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">String</span> <span class="variable">from</span> <span class="operator">=</span> <span class="string">&quot;test@qq.com&quot;</span>;</span><br><span class="line">    <span class="comment">//接收人</span></span><br><span class="line">    <span class="keyword">private</span> <span class="type">String</span> <span class="variable">to</span> <span class="operator">=</span> <span class="string">&quot;test@126.com&quot;</span>;</span><br><span class="line">    <span class="comment">//标题</span></span><br><span class="line">    <span class="keyword">private</span> <span class="type">String</span> <span class="variable">subject</span> <span class="operator">=</span> <span class="string">&quot;测试邮件&quot;</span>;</span><br><span class="line">    <span class="comment">//正文</span></span><br><span class="line">    <span class="keyword">private</span> <span class="type">String</span> <span class="variable">context</span> <span class="operator">=</span> <span class="string">&quot;测试邮件正文内容&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_">sendMail</span><span class="params">()</span> &#123;</span><br><span class="line">        <span class="type">SimpleMailMessage</span> <span class="variable">message</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">SimpleMailMessage</span>();</span><br><span class="line">        message.setFrom(from+<span class="string">&quot;(小甜甜)&quot;</span>);</span><br><span class="line">        message.setTo(to);</span><br><span class="line">        message.setSubject(subject);</span><br><span class="line">        message.setText(context);</span><br><span class="line">        javaMailSender.send(message);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>​        将发送邮件的必要信息（发件人、收件人、标题、正文）封装到SimpleMailMessage对象中，可以根据规则设置发送人昵称等。</p><h4 id="发送多组件邮件（附件、复杂正文）"><a href="#发送多组件邮件（附件、复杂正文）" class="headerlink" title="发送多组件邮件（附件、复杂正文）"></a>发送多组件邮件（附件、复杂正文）</h4><p>​        发送简单邮件仅需要提供对应的4个基本信息就可以了，如果想发送复杂的邮件，需要更换邮件对象。使用MimeMessage可以发送特殊的邮件。</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></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_">SendMailServiceImpl2</span> <span class="keyword">implements</span> <span class="title class_">SendMailService</span> &#123;</span><br><span class="line">    <span class="meta">@Autowired</span></span><br><span class="line">    <span class="keyword">private</span> JavaMailSender javaMailSender;</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">String</span> <span class="variable">from</span> <span class="operator">=</span> <span class="string">&quot;test@qq.com&quot;</span>;</span><br><span class="line">    <span class="comment">//接收人</span></span><br><span class="line">    <span class="keyword">private</span> <span class="type">String</span> <span class="variable">to</span> <span class="operator">=</span> <span class="string">&quot;test@126.com&quot;</span>;</span><br><span class="line">    <span class="comment">//标题</span></span><br><span class="line">    <span class="keyword">private</span> <span class="type">String</span> <span class="variable">subject</span> <span class="operator">=</span> <span class="string">&quot;测试邮件&quot;</span>;</span><br><span class="line">    <span class="comment">//正文</span></span><br><span class="line">    <span class="keyword">private</span> <span class="type">String</span> <span class="variable">context</span> <span class="operator">=</span> <span class="string">&quot;&lt;img src=&#x27;ABC.JPG&#x27;/&gt;&lt;a href=&#x27;https://www.itcast.cn&#x27;&gt;点开有惊喜&lt;/a&gt;&quot;</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_">sendMail</span><span class="params">()</span> &#123;</span><br><span class="line">        <span class="keyword">try</span> &#123;</span><br><span class="line">            <span class="type">MimeMessage</span> <span class="variable">message</span> <span class="operator">=</span> javaMailSender.createMimeMessage();</span><br><span class="line">            <span class="type">MimeMessageHelper</span> <span class="variable">helper</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">MimeMessageHelper</span>(message);</span><br><span class="line">            helper.setFrom(to+<span class="string">&quot;(小甜甜)&quot;</span>);</span><br><span class="line">            helper.setTo(from);</span><br><span class="line">            helper.setSubject(subject);</span><br><span class="line">            helper.setText(context,<span class="literal">true</span>);<span class="comment">//此处设置正文支持html解析</span></span><br><span class="line"></span><br><span class="line">            javaMailSender.send(message);</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></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><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">@Service</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">SendMailServiceImpl2</span> <span class="keyword">implements</span> <span class="title class_">SendMailService</span> &#123;</span><br><span class="line">    <span class="meta">@Autowired</span></span><br><span class="line">    <span class="keyword">private</span> JavaMailSender javaMailSender;</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">String</span> <span class="variable">from</span> <span class="operator">=</span> <span class="string">&quot;test@qq.com&quot;</span>;</span><br><span class="line">    <span class="comment">//接收人</span></span><br><span class="line">    <span class="keyword">private</span> <span class="type">String</span> <span class="variable">to</span> <span class="operator">=</span> <span class="string">&quot;test@126.com&quot;</span>;</span><br><span class="line">    <span class="comment">//标题</span></span><br><span class="line">    <span class="keyword">private</span> <span class="type">String</span> <span class="variable">subject</span> <span class="operator">=</span> <span class="string">&quot;测试邮件&quot;</span>;</span><br><span class="line">    <span class="comment">//正文</span></span><br><span class="line">    <span class="keyword">private</span> <span class="type">String</span> <span class="variable">context</span> <span class="operator">=</span> <span class="string">&quot;测试邮件正文&quot;</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_">sendMail</span><span class="params">()</span> &#123;</span><br><span class="line">        <span class="keyword">try</span> &#123;</span><br><span class="line">            <span class="type">MimeMessage</span> <span class="variable">message</span> <span class="operator">=</span> javaMailSender.createMimeMessage();</span><br><span class="line">            <span class="type">MimeMessageHelper</span> <span class="variable">helper</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">MimeMessageHelper</span>(message,<span class="literal">true</span>);<span class="comment">//此处设置支持附件</span></span><br><span class="line">            helper.setFrom(to+<span class="string">&quot;(小甜甜)&quot;</span>);</span><br><span class="line">            helper.setTo(from);</span><br><span class="line">            helper.setSubject(subject);</span><br><span class="line">            helper.setText(context);</span><br><span class="line"></span><br><span class="line">            <span class="comment">//添加附件</span></span><br><span class="line">            <span class="type">File</span> <span class="variable">f1</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">File</span>(<span class="string">&quot;springboot_23_mail-0.0.1-SNAPSHOT.jar&quot;</span>);</span><br><span class="line">            <span class="type">File</span> <span class="variable">f2</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">File</span>(<span class="string">&quot;resources\\logo.png&quot;</span>);</span><br><span class="line"></span><br><span class="line">            helper.addAttachment(f1.getName(),f1);</span><br><span class="line">            helper.addAttachment(<span class="string">&quot;最靠谱的培训结构.png&quot;</span>,f2);</span><br><span class="line"></span><br><span class="line">            javaMailSender.send(message);</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></pre></td></tr></table></figure><p><strong>总结</strong></p><ol><li>springboot整合javamail其实就是简化了发送邮件的客户端对象JavaMailSender的初始化过程，通过配置的形式加载信息简化开发过程</li></ol><h3 id="KF-5-4-消息"><a href="#KF-5-4-消息" class="headerlink" title="KF-5-4.消息"></a>KF-5-4.消息</h3><p>​        springboot整合第三方技术最后一部分我们来说说消息中间件，首先先介绍一下消息的应用。</p><h4 id="消息的概念"><a href="#消息的概念" class="headerlink" title="消息的概念"></a>消息的概念</h4><p>​        从广义角度来说，消息其实就是信息，但是和信息又有所不同。信息通常被定义为一组数据，而消息除了具有数据的特征之外，还有消息的来源与接收的概念。通常发送消息的一方称为消息的生产者，接收消息的一方称为消息的消费者。这样比较后，发现其实消息和信息差别还是很大的。</p><p>​        为什么要设置生产者和消费者呢？这就是要说到消息的意义了。信息通常就是一组数据，但是消息由于有了生产者和消费者，就出现了消息中所包含的信息可以被二次解读，生产者发送消息，可以理解为生产者发送了一个信息，也可以理解为生产者发送了一个命令；消费者接收消息，可以理解为消费者得到了一个信息，也可以理解为消费者得到了一个命令。对比一下我们会发现信息是一个基本数据，而命令则可以关联下一个行为动作，这样就可以理解为基于接收的消息相当于得到了一个行为动作，使用这些行为动作就可以组织成一个业务逻辑，进行进一步的操作。总的来说，消息其实也是一组信息，只是为其赋予了全新的含义，因为有了消息的流动，并且是有方向性的流动，带来了基于流动的行为产生的全新解读。开发者就可以基于消息的这种特殊解，将其换成代码中的指令。</p><p>​        对于消息的理解，初学者总认为消息内部的数据非常复杂，这是一个误区。比如我发送了一个消息，要求接受者翻译发送过去的内容。初学者会认为消息中会包含被翻译的文字，已经本次操作要执行翻译操作而不是打印操作。其实这种现象有点过度解读了，发送的消息中仅仅包含被翻译的文字，但是可以通过控制不同的人接收此消息来确认要做的事情。例如发送被翻译的文字仅到A程序，而A程序只能进行翻译操作，这样就可以发送简单的信息完成复杂的业务了，是通过接收消息的主体不同，进而执行不同的操作，而不会在消息内部定义数据的操作行为，当然如果开发者希望消息中包含操作种类信息也是可以的，只是提出消息的内容可以更简单，更单一。</p><p>​        对于消息的生产者与消费者的工作模式，还可以将消息划分成两种模式，同步消费与异步消息。</p><p>​        所谓同步消息就是生产者发送完消息，等待消费者处理，消费者处理完将结果告知生产者，然后生产者继续向下执行业务。这种模式过于卡生产者的业务执行连续性，在现在的企业级开发中，上述这种业务场景通常不会采用消息的形式进行处理。</p><p>​        所谓异步消息就是生产者发送完消息，无需等待消费者处理完毕，生产者继续向下执行其他动作。比如生产者发送了一个日志信息给日志系统，发送过去以后生产者就向下做其他事情了，无需关注日志系统的执行结果。日志系统根据接收到的日志信息继续进行业务执行，是单纯的记录日志，还是记录日志并报警，这些和生产者无关，这样生产者的业务执行效率就会大幅度提升。并且可以通过添加多个消费者来处理同一个生产者发送的消息来提高系统的高并发性，改善系统工作效率，提高用户体验。一旦某一个消费者由于各种问题宕机了，也不会对业务产生影响，提高了系统的高可用性。</p><p>​        以上简单的介绍了一下消息这种工作模式存在的意义，希望对各位学习者有所帮助。</p><h4 id="Java处理消息的标准规范"><a href="#Java处理消息的标准规范" class="headerlink" title="Java处理消息的标准规范"></a>Java处理消息的标准规范</h4><p>​        目前企业级开发中广泛使用的消息处理技术共三大类，具体如下：</p><ul><li>JMS</li><li>AMQP</li><li>MQTT</li></ul><p>​        为什么是三大类，而不是三个技术呢？因为这些都是规范，就想JDBC技术，是个规范，开发针对规范开发，运行还要靠实现类，例如MySQL提供了JDBC的实现，最终运行靠的还是实现。并且这三类规范都是针对异步消息进行处理的，也符合消息的设计本质，处理异步的业务。对以上三种消息规范做一下普及</p><h5 id="JMS"><a href="#JMS" class="headerlink" title="JMS"></a>JMS</h5><p>​        JMS（Java Message Service）,这是一个规范，作用等同于JDBC规范，提供了与消息服务相关的API接口。</p><p><strong>JMS消息模型</strong></p><p>​        JMS规范中规范了消息有两种模型。分别是<strong>点对点模型</strong>和<strong>发布订阅模型</strong>。</p><p>​        <strong>点对点模型</strong>：peer-2-peer，生产者会将消息发送到一个保存消息的容器中，通常使用队列模型，使用队列保存消息。一个队列的消息只能被一个消费者消费，或未被及时消费导致超时。这种模型下，生产者和消费者是一对一绑定的。</p><p>​        <strong>发布订阅模型</strong>：publish-subscribe，生产者将消息发送到一个保存消息的容器中，也是使用队列模型来保存。但是消息可以被多个消费者消费，生产者和消费者完全独立，相互不需要感知对方的存在。</p><p>​        以上这种分类是从消息的生产和消费过程来进行区分，针对消息所包含的信息不同，还可以进行不同类别的划分。</p><p><strong>JMS消息种类</strong></p><p>​        根据消息中包含的数据种类划分，可以将消息划分成6种消息。</p><ul><li>TextMessage</li><li>MapMessage</li><li>BytesMessage</li><li>StreamMessage</li><li>ObjectMessage</li><li>Message （只有消息头和属性）</li></ul><p>​        JMS主张不同种类的消息，消费方式不同，可以根据使用需要选择不同种类的消息。但是这一点也成为其诟病之处，后面再说。整体上来说，JMS就是典型的保守派，什么都按照J2EE的规范来，做一套规范，定义若干个标准，每个标准下又提供一大批API。目前对JMS规范实现的消息中间件技术还是挺多的，毕竟是皇家御用，肯定有人舔，例如ActiveMQ、Redis、HornetMQ。但是也有一些不太规范的实现，参考JMS的标准设计，但是又不完全满足其规范，例如：RabbitMQ、RocketMQ。</p><h5 id="AMQP"><a href="#AMQP" class="headerlink" title="AMQP"></a>AMQP</h5><p>​        JMS的问世为消息中间件提供了很强大的规范性支撑，但是使用的过程中就开始被人诟病，比如JMS设置的极其复杂的多种类消息处理机制。本来分门别类处理挺好的，为什么会被诟病呢？原因就在于JMS的设计是J2EE规范，站在Java开发的角度思考问题。但是现实往往是复杂度很高的。比如我有一个.NET开发的系统A，有一个Java开发的系统B，现在要从A系统给B系统发业务消息，结果两边数据格式不统一，没法操作。JMS不是可以统一数据格式吗？提供了6种数据种类，总有一款适合你啊。NO，一个都不能用。因为A系统的底层语言不是Java语言开发的，根本不支持那些对象。这就意味着如果想使用现有的业务系统A继续开发已经不可能了，必须推翻重新做使用Java语言开发的A系统。</p><p>​        这时候有人就提出说，你搞那么复杂，整那么多种类干什么？找一种大家都支持的消息数据类型不就解决这个跨平台的问题了吗？大家一想，对啊，于是AMQP孕育而生。</p><p>​        单从上面的说明中其实可以明确感知到，AMQP的出现解决的是消息传递时使用的消息种类的问题，化繁为简，但是其并没有完全推翻JMS的操作API，所以说AMQP仅仅是一种协议，规范了数据传输的格式而已。</p><p>​        AMQP（advanced message queuing protocol）：一种协议（高级消息队列协议，也是消息代理规范），规范了网络交换的数据格式，兼容JMS操作。<br><strong>优点</strong></p><p>​        具有跨平台性，服务器供应商，生产者，消费者可以使用不同的语言来实现</p><p><strong>JMS消息种类</strong></p><p>​        AMQP消息种类：byte[]</p><p>​        AMQP在JMS的消息模型基础上又进行了进一步的扩展，除了点对点和发布订阅的模型，开发了几种全新的消息模型，适应各种各样的消息发送。</p><p><strong>AMQP消息模型</strong></p><ul><li>direct exchange</li><li>fanout exchange</li><li>topic exchange</li><li>headers exchange</li><li>system exchange</li></ul><p>​        目前实现了AMQP协议的消息中间件技术也很多，而且都是较为流行的技术，例如：RabbitMQ、StormMQ、RocketMQ</p><h5 id="MQTT"><a href="#MQTT" class="headerlink" title="MQTT"></a>MQTT</h5><p>​        MQTT（Message Queueing Telemetry Transport）消息队列遥测传输，专为小设备设计，是物联网（IOT）生态系统中主要成分之一。由于与JavaEE企业级开发没有交集，此处不作过多的说明。</p><p>​        除了上述3种J2EE企业级应用中广泛使用的三种异步消息传递技术，还有一种技术也不能忽略，Kafka。</p><h5 id="KafKa"><a href="#KafKa" class="headerlink" title="KafKa"></a>KafKa</h5><p>​        Kafka，一种高吞吐量的分布式发布订阅消息系统，提供实时消息功能。Kafka技术并不是作为消息中间件为主要功能的产品，但是其拥有发布订阅的工作模式，也可以充当消息中间件来使用，而且目前企业级开发中其身影也不少见。</p><p>​        本节内容讲围绕着上述内容中的几种实现方案讲解springboot整合各种各样的消息中间件。由于各种消息中间件必须先安装再使用，下面的内容采用Windows系统安装，降低各位学习者的学习难度，基本套路和之前学习NoSQL解决方案一样，先安装再整合。</p><h4 id="购物订单发送手机短信案例"><a href="#购物订单发送手机短信案例" class="headerlink" title="购物订单发送手机短信案例"></a>购物订单发送手机短信案例</h4><p>​        为了便于下面演示各种各样的消息中间件技术，我们创建一个购物过程生成订单时为用户发送短信的案例环境，模拟使用消息中间件实现发送手机短信的过程。</p><p>​        手机验证码案例需求如下：</p><ul><li><p>执行下单业务时（模拟此过程），调用消息服务，将要发送短信的订单id传递给消息中间件</p></li><li><p>消息处理服务接收到要发送的订单id后输出订单id（模拟发短信）</p><p>由于不涉及数据读写，仅开发业务层与表现层，其中短信处理的业务代码独立开发，代码如下：</p></li></ul><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></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">interface</span> <span class="title class_">OrderService</span> &#123;</span><br><span class="line">    <span class="keyword">void</span> <span class="title function_">order</span><span class="params">(String id)</span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>​        模拟传入订单id，执行下订单业务，参数为虚拟设定，实际应为订单对应的实体类</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_">OrderServiceImpl</span> <span class="keyword">implements</span> <span class="title class_">OrderService</span> &#123;</span><br><span class="line">    <span class="meta">@Autowired</span></span><br><span class="line">    <span class="keyword">private</span> MessageService messageService;</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_">order</span><span class="params">(String id)</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">        <span class="comment">//短信消息处理</span></span><br><span class="line">        messageService.sendMessage(id);</span><br><span class="line">        System.out.println(<span class="string">&quot;订单处理结束&quot;</span>);</span><br><span class="line">        System.out.println();</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>​        业务层转调短信处理的服务MessageService</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></pre></td><td class="code"><pre><span class="line"><span class="meta">@RestController</span></span><br><span class="line"><span class="meta">@RequestMapping(&quot;/orders&quot;)</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">OrderController</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">@PostMapping(&quot;&#123;id&#125;&quot;)</span></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">order</span><span class="params">(<span class="meta">@PathVariable</span> String id)</span>&#123;</span><br><span class="line">        orderService.order(id);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>​        表现层对外开发接口，传入订单id即可（模拟）</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></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">interface</span> <span class="title class_">MessageService</span> &#123;</span><br><span class="line">    <span class="keyword">void</span> <span class="title function_">sendMessage</span><span class="params">(String id)</span>;</span><br><span class="line">    String <span class="title function_">doMessage</span><span class="params">()</span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>​        短信处理业务层接口提供两个操作，发送要处理的订单id到消息中间件，另一个操作目前暂且设计成处理消息，实际消息的处理过程不应该是手动执行，应该是自动执行，到具体实现时再进行设计</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="meta">@Service</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">MessageServiceImpl</span> <span class="keyword">implements</span> <span class="title class_">MessageService</span> &#123;</span><br><span class="line">    <span class="keyword">private</span> ArrayList&lt;String&gt; msgList = <span class="keyword">new</span> <span class="title class_">ArrayList</span>&lt;String&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_">sendMessage</span><span class="params">(String id)</span> &#123;</span><br><span class="line">        System.out.println(<span class="string">&quot;待发送短信的订单已纳入处理队列，id：&quot;</span>+id);</span><br><span class="line">        msgList.add(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">public</span> String <span class="title function_">doMessage</span><span class="params">()</span> &#123;</span><br><span class="line">        <span class="type">String</span> <span class="variable">id</span> <span class="operator">=</span> msgList.remove(<span class="number">0</span>);</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></pre></td></tr></table></figure><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></pre></td><td class="code"><pre><span class="line"><span class="meta">@RestController</span></span><br><span class="line"><span class="meta">@RequestMapping(&quot;/msgs&quot;)</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">MessageController</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> MessageService messageService;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@GetMapping</span></span><br><span class="line">    <span class="keyword">public</span> String <span class="title function_">doMessage</span><span class="params">()</span>&#123;</span><br><span class="line">        <span class="type">String</span> <span class="variable">id</span> <span class="operator">=</span> messageService.doMessage();</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></pre></td></tr></table></figure><p>​        短信处理表现层接口暂且开发出一个处理消息的入口，但是此业务是对应业务层中设计的模拟接口，实际业务不需要设计此接口。</p><p>​        下面开启springboot整合各种各样的消息中间件，从严格满足JMS规范的ActiveMQ开始</p><h4 id="SpringBoot整合ActiveMQ"><a href="#SpringBoot整合ActiveMQ" class="headerlink" title="SpringBoot整合ActiveMQ"></a>SpringBoot整合ActiveMQ</h4><p>​        ActiveMQ是MQ产品中的元老级产品，早期标准MQ产品之一，在AMQP协议没有出现之前，占据了消息中间件市场的绝大部分份额，后期因为AMQP系列产品的出现，迅速走弱，目前仅在一些线上运行的产品中出现，新产品开发较少采用。</p><h5 id="安装-1"><a href="#安装-1" class="headerlink" title="安装"></a>安装</h5><p>​        windows版安装包下载地址：<a href="https://activemq.apache.org/components/classic/download/">https://activemq.apache.org/components/classic/download</a><a href="https://activemq.apache.org/components/classic/download/">/</a></p><p>​        下载的安装包是解压缩就能使用的zip文件，解压缩完毕后会得到如下文件</p><p><img src="img\image-20220228160001620.png" alt="image-20220228160001620"></p><p><strong>启动服务器</strong></p><figure class="highlight cmd"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">activemq.bat</span><br></pre></td></tr></table></figure><p>​        运行bin目录下的win32或win64目录下的activemq.bat命令即可，根据自己的操作系统选择即可，默认对外服务端口61616。</p><p><strong>访问web管理服务</strong></p><p>​        ActiveMQ启动后会启动一个Web控制台服务，可以通过该服务管理ActiveMQ。</p><figure class="highlight cmd"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="function">http://127.0.0.1:8161/</span></span><br></pre></td></tr></table></figure><p>​        web管理服务默认端口8161，访问后可以打开ActiveMQ的管理界面，如下：</p><p><img src="img\image-20220228160844972.png" alt="image-20220228160844972" style="zoom:67%;" /></p><p>​        首先输入访问用户名和密码，初始化用户名和密码相同，均为：admin，成功登录后进入管理后台界面，如下：</p><p><img src="img\image-20220228161010401.png" alt="image-20220228161010401"></p><p>​        看到上述界面视为启动ActiveMQ服务成功。</p><p><strong>启动失败</strong></p><p>​        在ActiveMQ启动时要占用多个端口，以下为正常启动信息：</p><figure class="highlight cmd"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><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">wrapper  | --&gt; Wrapper Started as Console</span><br><span class="line">wrapper  | Launching a JVM...</span><br><span class="line">jvm <span class="number">1</span>    | Wrapper (Version <span class="number">3</span>.<span class="number">2</span>.<span class="number">3</span>) http://wrapper.tanukisoftware.org</span><br><span class="line">jvm <span class="number">1</span>    |   Copyright <span class="number">1999</span>-<span class="number">2006</span> Tanuki Software, Inc.  All Rights Reserved.</span><br><span class="line">jvm <span class="number">1</span>    |</span><br><span class="line">jvm <span class="number">1</span>    | Java Runtime: Oracle Corporation <span class="number">1</span>.<span class="number">8</span>.<span class="number">0</span>_172 D:\soft\jdk1.<span class="number">8</span>.<span class="number">0</span>_172\jre</span><br><span class="line">jvm <span class="number">1</span>    |   Heap sizes: current=<span class="number">249344</span>k  free=<span class="number">235037</span>k  max=<span class="number">932352</span>k</span><br><span class="line">jvm <span class="number">1</span>    |     JVM args: -Dactivemq.home=../.. -Dactivemq.base=../.. -Djavax.<span class="built_in">net</span>.ssl.keyStorePassword=password -Djavax.<span class="built_in">net</span>.ssl.trustStorePassword=password -Djavax.<span class="built_in">net</span>.ssl.keyStore=../../conf/broker.ks -Djavax.<span class="built_in">net</span>.ssl.trustStore=../../conf/broker.ts -Dcom.sun.management.jmxremote -Dorg.apache.activemq.UseDedicatedTaskRunner=true -Djava.util.logging.config.file=logging.properties -Dactivemq.conf=../../conf -Dactivemq.data=../../data -Djava.security.auth.login.config=../../conf/login.config -Xmx1024m -Djava.library.<span class="built_in">path</span>=../../bin/win64 -Dwrapper.key=<span class="number">7</span>ySrCD75XhLCpLjd -Dwrapper.port=<span class="number">32000</span> -Dwrapper.jvm.port.min=<span class="number">31000</span> -Dwrapper.jvm.port.max=<span class="number">31999</span> -Dwrapper.pid=<span class="number">9364</span> -Dwrapper.version=<span class="number">3</span>.<span class="number">2</span>.<span class="number">3</span> -Dwrapper.native_library=wrapper -Dwrapper.cpu.timeout=<span class="number">10</span> -Dwrapper.jvmid=<span class="number">1</span></span><br><span class="line">jvm <span class="number">1</span>    | Extensions classpath:</span><br><span class="line">jvm <span class="number">1</span>    |   [..\..\lib,..\..\lib\camel,..\..\lib\optional,..\..\lib\web,..\..\lib\extra]</span><br><span class="line">jvm <span class="number">1</span>    | ACTIVEMQ_HOME: ..\..</span><br><span class="line">jvm <span class="number">1</span>    | ACTIVEMQ_BASE: ..\..</span><br><span class="line">jvm <span class="number">1</span>    | ACTIVEMQ_CONF: ..\..\conf</span><br><span class="line">jvm <span class="number">1</span>    | ACTIVEMQ_DATA: ..\..\data</span><br><span class="line">jvm <span class="number">1</span>    | Loading message broker from: xbean:activemq.xml</span><br><span class="line">jvm <span class="number">1</span>    |  INFO | Refreshing org.apache.activemq.xbean.XBeanBrokerFactory$<span class="number">1</span>@<span class="number">5</span>f3ebfe0: startup <span class="built_in">date</span> [Mon Feb <span class="number">28</span> <span class="number">16</span>:<span class="number">07</span>:<span class="number">48</span> CST <span class="number">2022</span>]; root of context hierarchy</span><br><span class="line">jvm <span class="number">1</span>    |  INFO | Using Persistence Adapter: KahaDBPersistenceAdapter[D:\soft\activemq\bin\win64\..\..\data\kahadb]</span><br><span class="line">jvm <span class="number">1</span>    |  INFO | KahaDB is version <span class="number">7</span></span><br><span class="line">jvm <span class="number">1</span>    |  INFO | PListStore:[D:\soft\activemq\bin\win64\..\..\data\localhost\tmp_storage] started</span><br><span class="line">jvm <span class="number">1</span>    |  INFO | Apache ActiveMQ <span class="number">5</span>.<span class="number">16</span>.<span class="number">3</span> (localhost, ID:CZBK-<span class="number">20210302</span>VL-<span class="number">10434</span>-<span class="number">1646035669595</span>-<span class="number">0</span>:<span class="number">1</span>) is starting</span><br><span class="line">jvm <span class="number">1</span>    |  INFO | Listening <span class="keyword">for</span> connections <span class="built_in">at</span>: tcp://CZBK-<span class="number">20210302</span>VL:<span class="number">61616</span>?maximumConnections=<span class="number">1000</span>&amp;wireFormat.maxFrameSize=<span class="number">104857600</span></span><br><span class="line">jvm <span class="number">1</span>    |  INFO | Connector openwire started</span><br><span class="line">jvm <span class="number">1</span>    |  INFO | Listening <span class="keyword">for</span> connections <span class="built_in">at</span>: amqp://CZBK-<span class="number">20210302</span>VL:<span class="number">5672</span>?maximumConnections=<span class="number">1000</span>&amp;wireFormat.maxFrameSize=<span class="number">104857600</span></span><br><span class="line">jvm <span class="number">1</span>    |  INFO | Connector amqp started</span><br><span class="line">jvm <span class="number">1</span>    |  INFO | Listening <span class="keyword">for</span> connections <span class="built_in">at</span>: stomp://CZBK-<span class="number">20210302</span>VL:<span class="number">61613</span>?maximumConnections=<span class="number">1000</span>&amp;wireFormat.maxFrameSize=<span class="number">104857600</span></span><br><span class="line">jvm <span class="number">1</span>    |  INFO | Connector stomp started</span><br><span class="line">jvm <span class="number">1</span>    |  INFO | Listening <span class="keyword">for</span> connections <span class="built_in">at</span>: mqtt://CZBK-<span class="number">20210302</span>VL:<span class="number">1883</span>?maximumConnections=<span class="number">1000</span>&amp;wireFormat.maxFrameSize=<span class="number">104857600</span></span><br><span class="line">jvm <span class="number">1</span>    |  INFO | Connector mqtt started</span><br><span class="line">jvm <span class="number">1</span>    |  INFO | Starting Jetty server</span><br><span class="line">jvm <span class="number">1</span>    |  INFO | Creating Jetty connector</span><br><span class="line">jvm <span class="number">1</span>    |  WARN | ServletContext@o.e.j.s.ServletContextHandler@<span class="number">7350746</span>f&#123;/,null,STARTING&#125; has uncovered http methods <span class="keyword">for</span> <span class="built_in">path</span>: /</span><br><span class="line">jvm <span class="number">1</span>    |  INFO | Listening <span class="keyword">for</span> connections <span class="built_in">at</span> ws://CZBK-<span class="number">20210302</span>VL:<span class="number">61614</span>?maximumConnections=<span class="number">1000</span>&amp;wireFormat.maxFrameSize=<span class="number">104857600</span></span><br><span class="line">jvm <span class="number">1</span>    |  INFO | Connector ws started</span><br><span class="line">jvm <span class="number">1</span>    |  INFO | Apache ActiveMQ <span class="number">5</span>.<span class="number">16</span>.<span class="number">3</span> (localhost, ID:CZBK-<span class="number">20210302</span>VL-<span class="number">10434</span>-<span class="number">1646035669595</span>-<span class="number">0</span>:<span class="number">1</span>) started</span><br><span class="line">jvm <span class="number">1</span>    |  INFO | <span class="keyword">For</span> <span class="built_in">help</span> or <span class="built_in">more</span> information please see: http://activemq.apache.org</span><br><span class="line">jvm <span class="number">1</span>    |  WARN | Store limit is <span class="number">102400</span> mb (current store usage is <span class="number">0</span> mb). The data directory: D:\soft\activemq\bin\win64\..\..\data\kahadb only has <span class="number">68936</span> mb of usable space. - resetting to maximum available disk space: <span class="number">68936</span> mb</span><br><span class="line">jvm <span class="number">1</span>    |  INFO | ActiveMQ WebConsole available <span class="built_in">at</span> http://<span class="number">127</span>.<span class="number">0</span>.<span class="number">0</span>.<span class="number">1</span>:<span class="number">8161</span>/</span><br><span class="line">jvm <span class="number">1</span>    |  INFO | ActiveMQ Jolokia REST API available <span class="built_in">at</span> http://<span class="number">127</span>.<span class="number">0</span>.<span class="number">0</span>.<span class="number">1</span>:<span class="number">8161</span>/api/jolokia/</span><br></pre></td></tr></table></figure><p>​        其中占用的端口有：61616、5672、61613、1883、61614，如果启动失败，请先管理对应端口即可。以下就是某个端口占用的报错信息，可以从抛出异常的位置看出，启动5672端口时端口被占用，显示java.net.BindException: Address already in use: JVM_Bind。Windows系统中终止端口运行的操作参看<a href="#命令行启动常见问题及解决方案">【命令行启动常见问题及解决方案】</a></p><figure class="highlight cmd"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><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></pre></td><td class="code"><pre><span class="line">wrapper  | --&gt; Wrapper Started as Console</span><br><span class="line">wrapper  | Launching a JVM...</span><br><span class="line">jvm <span class="number">1</span>    | Wrapper (Version <span class="number">3</span>.<span class="number">2</span>.<span class="number">3</span>) http://wrapper.tanukisoftware.org</span><br><span class="line">jvm <span class="number">1</span>    |   Copyright <span class="number">1999</span>-<span class="number">2006</span> Tanuki Software, Inc.  All Rights Reserved.</span><br><span class="line">jvm <span class="number">1</span>    |</span><br><span class="line">jvm <span class="number">1</span>    | Java Runtime: Oracle Corporation <span class="number">1</span>.<span class="number">8</span>.<span class="number">0</span>_172 D:\soft\jdk1.<span class="number">8</span>.<span class="number">0</span>_172\jre</span><br><span class="line">jvm <span class="number">1</span>    |   Heap sizes: current=<span class="number">249344</span>k  free=<span class="number">235038</span>k  max=<span class="number">932352</span>k</span><br><span class="line">jvm <span class="number">1</span>    |     JVM args: -Dactivemq.home=../.. -Dactivemq.base=../.. -Djavax.<span class="built_in">net</span>.ssl.keyStorePassword=password -Djavax.<span class="built_in">net</span>.ssl.trustStorePassword=password -Djavax.<span class="built_in">net</span>.ssl.keyStore=../../conf/broker.ks -Djavax.<span class="built_in">net</span>.ssl.trustStore=../../conf/broker.ts -Dcom.sun.management.jmxremote -Dorg.apache.activemq.UseDedicatedTaskRunner=true -Djava.util.logging.config.file=logging.properties -Dactivemq.conf=../../conf -Dactivemq.data=../../data -Djava.security.auth.login.config=../../conf/login.config -Xmx1024m -Djava.library.<span class="built_in">path</span>=../../bin/win64 -Dwrapper.key=QPJoy9ZoXeWmmwTS -Dwrapper.port=<span class="number">32000</span> -Dwrapper.jvm.port.min=<span class="number">31000</span> -Dwrapper.jvm.port.max=<span class="number">31999</span> -Dwrapper.pid=<span class="number">14836</span> -Dwrapper.version=<span class="number">3</span>.<span class="number">2</span>.<span class="number">3</span> -Dwrapper.native_library=wrapper -Dwrapper.cpu.timeout=<span class="number">10</span> -Dwrapper.jvmid=<span class="number">1</span></span><br><span class="line">jvm <span class="number">1</span>    | Extensions classpath:</span><br><span class="line">jvm <span class="number">1</span>    |   [..\..\lib,..\..\lib\camel,..\..\lib\optional,..\..\lib\web,..\..\lib\extra]</span><br><span class="line">jvm <span class="number">1</span>    | ACTIVEMQ_HOME: ..\..</span><br><span class="line">jvm <span class="number">1</span>    | ACTIVEMQ_BASE: ..\..</span><br><span class="line">jvm <span class="number">1</span>    | ACTIVEMQ_CONF: ..\..\conf</span><br><span class="line">jvm <span class="number">1</span>    | ACTIVEMQ_DATA: ..\..\data</span><br><span class="line">jvm <span class="number">1</span>    | Loading message broker from: xbean:activemq.xml</span><br><span class="line">jvm <span class="number">1</span>    |  INFO | Refreshing org.apache.activemq.xbean.XBeanBrokerFactory$<span class="number">1</span>@<span class="number">2</span>c9392f5: startup <span class="built_in">date</span> [Mon Feb <span class="number">28</span> <span class="number">16</span>:<span class="number">06</span>:<span class="number">16</span> CST <span class="number">2022</span>]; root of context hierarchy</span><br><span class="line">jvm <span class="number">1</span>    |  INFO | Using Persistence Adapter: KahaDBPersistenceAdapter[D:\soft\activemq\bin\win64\..\..\data\kahadb]</span><br><span class="line">jvm <span class="number">1</span>    |  INFO | KahaDB is version <span class="number">7</span></span><br><span class="line">jvm <span class="number">1</span>    |  INFO | PListStore:[D:\soft\activemq\bin\win64\..\..\data\localhost\tmp_storage] started</span><br><span class="line">jvm <span class="number">1</span>    |  INFO | Apache ActiveMQ <span class="number">5</span>.<span class="number">16</span>.<span class="number">3</span> (localhost, ID:CZBK-<span class="number">20210302</span>VL-<span class="number">10257</span>-<span class="number">1646035577620</span>-<span class="number">0</span>:<span class="number">1</span>) is starting</span><br><span class="line">jvm <span class="number">1</span>    |  INFO | Listening <span class="keyword">for</span> connections <span class="built_in">at</span>: tcp://CZBK-<span class="number">20210302</span>VL:<span class="number">61616</span>?maximumConnections=<span class="number">1000</span>&amp;wireFormat.maxFrameSize=<span class="number">104857600</span></span><br><span class="line">jvm <span class="number">1</span>    |  INFO | Connector openwire started</span><br><span class="line">jvm <span class="number">1</span>    | ERROR | Failed to <span class="built_in">start</span> Apache ActiveMQ (localhost, ID:CZBK-<span class="number">20210302</span>VL-<span class="number">10257</span>-<span class="number">1646035577620</span>-<span class="number">0</span>:<span class="number">1</span>)</span><br><span class="line">jvm <span class="number">1</span>    | java.io.IOException: Transport Connector could <span class="keyword">not</span> be registered <span class="keyword">in</span> JMX: java.io.IOException: Failed to bind to server socket: amqp://<span class="number">0</span>.<span class="number">0</span>.<span class="number">0</span>.<span class="number">0</span>:<span class="number">5672</span>?maximumConnections=<span class="number">1000</span>&amp;wireFormat.maxFrameSize=<span class="number">104857600</span> due to: java.<span class="built_in">net</span>.BindException: Address already <span class="keyword">in</span> use: JVM_Bind</span><br><span class="line">jvm <span class="number">1</span>    |      <span class="built_in">at</span> org.apache.activemq.util.IOExceptionSupport.create(IOExceptionSupport.java:<span class="number">28</span>)</span><br><span class="line">jvm <span class="number">1</span>    |      <span class="built_in">at</span> org.apache.activemq.broker.BrokerService.registerConnectorMBean(BrokerService.java:<span class="number">2288</span>)</span><br><span class="line">jvm <span class="number">1</span>    |      <span class="built_in">at</span> org.apache.activemq.broker.BrokerService.startTransportConnector(BrokerService.java:<span class="number">2769</span>)</span><br><span class="line">jvm <span class="number">1</span>    |      <span class="built_in">at</span> org.apache.activemq.broker.BrokerService.startAllConnectors(BrokerService.java:<span class="number">2665</span>)</span><br><span class="line">jvm <span class="number">1</span>    |      <span class="built_in">at</span> org.apache.activemq.broker.BrokerService.doStartBroker(BrokerService.java:<span class="number">780</span>)</span><br><span class="line">jvm <span class="number">1</span>    |      <span class="built_in">at</span> org.apache.activemq.broker.BrokerService.startBroker(BrokerService.java:<span class="number">742</span>)</span><br><span class="line">jvm <span class="number">1</span>    |      <span class="built_in">at</span> org.apache.activemq.broker.BrokerService.<span class="built_in">start</span>(BrokerService.java:<span class="number">645</span>)</span><br><span class="line">jvm <span class="number">1</span>    |      <span class="built_in">at</span> org.apache.activemq.xbean.XBeanBrokerService.afterPropertiesSet(XBeanBrokerService.java:<span class="number">73</span>)</span><br><span class="line">jvm <span class="number">1</span>    |      <span class="built_in">at</span> sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)</span><br><span class="line">jvm <span class="number">1</span>    |      <span class="built_in">at</span> sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:<span class="number">62</span>)</span><br><span class="line">jvm <span class="number">1</span>    |      <span class="built_in">at</span> sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:<span class="number">43</span>)</span><br><span class="line">jvm <span class="number">1</span>    |      <span class="built_in">at</span> java.lang.reflect.Method.invoke(Method.java:<span class="number">498</span>)</span><br><span class="line">jvm <span class="number">1</span>    |      <span class="built_in">at</span> org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.invokeCustomInitMethod(AbstractAutowireCapableBeanFactory.java:<span class="number">1748</span>)</span><br><span class="line">jvm <span class="number">1</span>    |      <span class="built_in">at</span> org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.invokeInitMethods(AbstractAutowireCapableBeanFactory.java:<span class="number">1685</span>)</span><br><span class="line">jvm <span class="number">1</span>    |      <span class="built_in">at</span> org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.initializeBean(AbstractAutowireCapableBeanFactory.java:<span class="number">1615</span>)</span><br><span class="line">jvm <span class="number">1</span>    |      <span class="built_in">at</span> org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:<span class="number">553</span>)</span><br><span class="line">jvm <span class="number">1</span>    |      <span class="built_in">at</span> org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:<span class="number">481</span>)</span><br><span class="line">jvm <span class="number">1</span>    |      <span class="built_in">at</span> org.springframework.beans.factory.support.AbstractBeanFactory$<span class="number">1</span>.getObject(AbstractBeanFactory.java:<span class="number">312</span>)</span><br><span class="line">jvm <span class="number">1</span>    |      <span class="built_in">at</span> org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:<span class="number">230</span>)</span><br><span class="line">jvm <span class="number">1</span>    |      <span class="built_in">at</span> org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:<span class="number">308</span>)</span><br><span class="line">jvm <span class="number">1</span>    |      <span class="built_in">at</span> org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:<span class="number">197</span>)</span><br><span class="line">jvm <span class="number">1</span>    |      <span class="built_in">at</span> org.springframework.beans.factory.support.DefaultListableBeanFactory.preInstantiateSingletons(DefaultListableBeanFactory.java:<span class="number">756</span>)</span><br><span class="line">jvm <span class="number">1</span>    |      <span class="built_in">at</span> org.springframework.context.support.AbstractApplicationContext.finishBeanFactoryInitialization(AbstractApplicationContext.java:<span class="number">867</span>)</span><br><span class="line">jvm <span class="number">1</span>    |      <span class="built_in">at</span> org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:<span class="number">542</span>)</span><br><span class="line">jvm <span class="number">1</span>    |      <span class="built_in">at</span> org.apache.xbean.spring.context.ResourceXmlApplicationContext.&lt;init&gt;(ResourceXmlApplicationContext.java:<span class="number">64</span>)</span><br><span class="line">jvm <span class="number">1</span>    |      <span class="built_in">at</span> org.apache.xbean.spring.context.ResourceXmlApplicationContext.&lt;init&gt;(ResourceXmlApplicationContext.java:<span class="number">52</span>)</span><br><span class="line">jvm <span class="number">1</span>    |      <span class="built_in">at</span> org.apache.activemq.xbean.XBeanBrokerFactory$<span class="number">1</span>.&lt;init&gt;(XBeanBrokerFactory.java:<span class="number">104</span>)</span><br><span class="line">jvm <span class="number">1</span>    |      <span class="built_in">at</span> org.apache.activemq.xbean.XBeanBrokerFactory.createApplicationContext(XBeanBrokerFactory.java:<span class="number">104</span>)</span><br><span class="line">jvm <span class="number">1</span>    |      <span class="built_in">at</span> org.apache.activemq.xbean.XBeanBrokerFactory.createBroker(XBeanBrokerFactory.java:<span class="number">67</span>)</span><br><span class="line">jvm <span class="number">1</span>    |      <span class="built_in">at</span> org.apache.activemq.broker.BrokerFactory.createBroker(BrokerFactory.java:<span class="number">71</span>)</span><br><span class="line">jvm <span class="number">1</span>    |      <span class="built_in">at</span> org.apache.activemq.broker.BrokerFactory.createBroker(BrokerFactory.java:<span class="number">54</span>)</span><br><span class="line">jvm <span class="number">1</span>    |      <span class="built_in">at</span> org.apache.activemq.console.command.StartCommand.runTask(StartCommand.java:<span class="number">87</span>)</span><br><span class="line">jvm <span class="number">1</span>    |      <span class="built_in">at</span> org.apache.activemq.console.command.AbstractCommand.execute(AbstractCommand.java:<span class="number">63</span>)</span><br><span class="line">jvm <span class="number">1</span>    |      <span class="built_in">at</span> org.apache.activemq.console.command.ShellCommand.runTask(ShellCommand.java:<span class="number">154</span>)</span><br><span class="line">jvm <span class="number">1</span>    |      <span class="built_in">at</span> org.apache.activemq.console.command.AbstractCommand.execute(AbstractCommand.java:<span class="number">63</span>)</span><br><span class="line">jvm <span class="number">1</span>    |      <span class="built_in">at</span> org.apache.activemq.console.command.ShellCommand.main(ShellCommand.java:<span class="number">104</span>)</span><br><span class="line">jvm <span class="number">1</span>    |      <span class="built_in">at</span> sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)</span><br><span class="line">jvm <span class="number">1</span>    |      <span class="built_in">at</span> sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:<span class="number">62</span>)</span><br><span class="line">jvm <span class="number">1</span>    |      <span class="built_in">at</span> sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:<span class="number">43</span>)</span><br><span class="line">jvm <span class="number">1</span>    |      <span class="built_in">at</span> java.lang.reflect.Method.invoke(Method.java:<span class="number">498</span>)</span><br><span class="line">jvm <span class="number">1</span>    |      <span class="built_in">at</span> org.apache.activemq.console.Main.runTaskClass(Main.java:<span class="number">262</span>)</span><br><span class="line">jvm <span class="number">1</span>    |      <span class="built_in">at</span> org.apache.activemq.console.Main.main(Main.java:<span class="number">115</span>)</span><br><span class="line">jvm <span class="number">1</span>    |      <span class="built_in">at</span> sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)</span><br><span class="line">jvm <span class="number">1</span>    |      <span class="built_in">at</span> sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:<span class="number">62</span>)</span><br><span class="line">jvm <span class="number">1</span>    |      <span class="built_in">at</span> sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:<span class="number">43</span>)</span><br><span class="line">jvm <span class="number">1</span>    |      <span class="built_in">at</span> java.lang.reflect.Method.invoke(Method.java:<span class="number">498</span>)</span><br><span class="line">jvm <span class="number">1</span>    |      <span class="built_in">at</span> org.tanukisoftware.wrapper.WrapperSimpleApp.run(WrapperSimpleApp.java:<span class="number">240</span>)</span><br><span class="line">jvm <span class="number">1</span>    |      <span class="built_in">at</span> java.lang.Thread.run(Thread.java:<span class="number">748</span>)</span><br><span class="line">jvm <span class="number">1</span>    | Caused by: java.io.IOException: Failed to bind to server socket: amqp://<span class="number">0</span>.<span class="number">0</span>.<span class="number">0</span>.<span class="number">0</span>:<span class="number">5672</span>?maximumConnections=<span class="number">1000</span>&amp;wireFormat.maxFrameSize=<span class="number">104857600</span> due to: java.<span class="built_in">net</span>.BindException: Address already <span class="keyword">in</span> use: JVM_Bind</span><br><span class="line">jvm <span class="number">1</span>    |      <span class="built_in">at</span> org.apache.activemq.util.IOExceptionSupport.create(IOExceptionSupport.java:<span class="number">34</span>)</span><br><span class="line">jvm <span class="number">1</span>    |      <span class="built_in">at</span> org.apache.activemq.transport.tcp.TcpTransportServer.bind(TcpTransportServer.java:<span class="number">146</span>)</span><br><span class="line">jvm <span class="number">1</span>    |      <span class="built_in">at</span> org.apache.activemq.transport.tcp.TcpTransportFactory.doBind(TcpTransportFactory.java:<span class="number">62</span>)</span><br><span class="line">jvm <span class="number">1</span>    |      <span class="built_in">at</span> org.apache.activemq.transport.TransportFactorySupport.bind(TransportFactorySupport.java:<span class="number">40</span>)</span><br><span class="line">jvm <span class="number">1</span>    |      <span class="built_in">at</span> org.apache.activemq.broker.TransportConnector.createTransportServer(TransportConnector.java:<span class="number">335</span>)</span><br><span class="line">jvm <span class="number">1</span>    |      <span class="built_in">at</span> org.apache.activemq.broker.TransportConnector.getServer(TransportConnector.java:<span class="number">145</span>)</span><br><span class="line">jvm <span class="number">1</span>    |      <span class="built_in">at</span> org.apache.activemq.broker.TransportConnector.asManagedConnector(TransportConnector.java:<span class="number">110</span>)</span><br><span class="line">jvm <span class="number">1</span>    |      <span class="built_in">at</span> org.apache.activemq.broker.BrokerService.registerConnectorMBean(BrokerService.java:<span class="number">2283</span>)</span><br><span class="line">jvm <span class="number">1</span>    |      ... <span class="number">46</span> <span class="built_in">more</span></span><br><span class="line">jvm <span class="number">1</span>    | Caused by: java.<span class="built_in">net</span>.BindException: Address already <span class="keyword">in</span> use: JVM_Bind</span><br><span class="line">jvm <span class="number">1</span>    |      <span class="built_in">at</span> java.<span class="built_in">net</span>.DualStackPlainSocketImpl.bind0(Native Method)</span><br><span class="line">jvm <span class="number">1</span>    |      <span class="built_in">at</span> java.<span class="built_in">net</span>.DualStackPlainSocketImpl.socketBind(DualStackPlainSocketImpl.java:<span class="number">106</span>)</span><br><span class="line">jvm <span class="number">1</span>    |      <span class="built_in">at</span> java.<span class="built_in">net</span>.AbstractPlainSocketImpl.bind(AbstractPlainSocketImpl.java:<span class="number">387</span>)</span><br><span class="line">jvm <span class="number">1</span>    |      <span class="built_in">at</span> java.<span class="built_in">net</span>.PlainSocketImpl.bind(PlainSocketImpl.java:<span class="number">190</span>)</span><br><span class="line">jvm <span class="number">1</span>    |      <span class="built_in">at</span> java.<span class="built_in">net</span>.ServerSocket.bind(ServerSocket.java:<span class="number">375</span>)</span><br><span class="line">jvm <span class="number">1</span>    |      <span class="built_in">at</span> java.<span class="built_in">net</span>.ServerSocket.&lt;init&gt;(ServerSocket.java:<span class="number">237</span>)</span><br><span class="line">jvm <span class="number">1</span>    |      <span class="built_in">at</span> javax.<span class="built_in">net</span>.DefaultServerSocketFactory.createServerSocket(ServerSocketFactory.java:<span class="number">231</span>)</span><br><span class="line">jvm <span class="number">1</span>    |      <span class="built_in">at</span> org.apache.activemq.transport.tcp.TcpTransportServer.bind(TcpTransportServer.java:<span class="number">143</span>)</span><br><span class="line">jvm <span class="number">1</span>    |      ... <span class="number">52</span> <span class="built_in">more</span></span><br><span class="line">jvm <span class="number">1</span>    |  INFO | Apache ActiveMQ <span class="number">5</span>.<span class="number">16</span>.<span class="number">3</span> (localhost, ID:CZBK-<span class="number">20210302</span>VL-<span class="number">10257</span>-<span class="number">1646035577620</span>-<span class="number">0</span>:<span class="number">1</span>) is shutting down</span><br><span class="line">jvm <span class="number">1</span>    |  INFO | socketQueue interrupted - stopping</span><br><span class="line">jvm <span class="number">1</span>    |  INFO | Connector openwire stopped</span><br><span class="line">jvm <span class="number">1</span>    |  INFO | Could <span class="keyword">not</span> accept connection during shutdown  : null (null)</span><br><span class="line">jvm <span class="number">1</span>    |  INFO | Connector amqp stopped</span><br><span class="line">jvm <span class="number">1</span>    |  INFO | Connector stomp stopped</span><br><span class="line">jvm <span class="number">1</span>    |  INFO | Connector mqtt stopped</span><br><span class="line">jvm <span class="number">1</span>    |  INFO | Connector ws stopped</span><br><span class="line">jvm <span class="number">1</span>    |  INFO | PListStore:[D:\soft\activemq\bin\win64\..\..\data\localhost\tmp_storage] stopped</span><br><span class="line">jvm <span class="number">1</span>    |  INFO | Stopping async queue tasks</span><br><span class="line">jvm <span class="number">1</span>    |  INFO | Stopping async topic tasks</span><br><span class="line">jvm <span class="number">1</span>    |  INFO | Stopped KahaDB</span><br><span class="line">jvm <span class="number">1</span>    |  INFO | Apache ActiveMQ <span class="number">5</span>.<span class="number">16</span>.<span class="number">3</span> (localhost, ID:CZBK-<span class="number">20210302</span>VL-<span class="number">10257</span>-<span class="number">1646035577620</span>-<span class="number">0</span>:<span class="number">1</span>) uptime <span class="number">0</span>.<span class="number">426</span> seconds</span><br><span class="line">jvm <span class="number">1</span>    |  INFO | Apache ActiveMQ <span class="number">5</span>.<span class="number">16</span>.<span class="number">3</span> (localhost, ID:CZBK-<span class="number">20210302</span>VL-<span class="number">10257</span>-<span class="number">1646035577620</span>-<span class="number">0</span>:<span class="number">1</span>) is shutdown</span><br><span class="line">jvm <span class="number">1</span>    |  INFO | Closing org.apache.activemq.xbean.XBeanBrokerFactory$<span class="number">1</span>@<span class="number">2</span>c9392f5: startup <span class="built_in">date</span> [Mon Feb <span class="number">28</span> <span class="number">16</span>:<span class="number">06</span>:<span class="number">16</span> CST <span class="number">2022</span>]; root of context hierarchy</span><br><span class="line">jvm <span class="number">1</span>    |  WARN | Exception encountered during context initialization - cancelling refresh attempt: org.springframework.beans.factory.BeanCreationException: Error creating bean with name &#x27;org.apache.activemq.xbean.XBeanBrokerService#<span class="number">0</span>&#x27; <span class="keyword">defined</span> <span class="keyword">in</span> class <span class="built_in">path</span> resource [activemq.xml]: Invocation of init method failed; nested exception is java.io.IOException: Transport Connector could <span class="keyword">not</span> be registered <span class="keyword">in</span> JMX: java.io.IOException: Failed to bind to server socket: amqp://<span class="number">0</span>.<span class="number">0</span>.<span class="number">0</span>.<span class="number">0</span>:<span class="number">5672</span>?maximumConnections=<span class="number">1000</span>&amp;wireFormat.maxFrameSize=<span class="number">104857600</span> due to: java.<span class="built_in">net</span>.BindException: Address already <span class="keyword">in</span> use: JVM_Bind</span><br><span class="line">jvm <span class="number">1</span>    | ERROR: java.lang.RuntimeException: Failed to execute <span class="built_in">start</span> task. Reason: java.lang.IllegalStateException: BeanFactory <span class="keyword">not</span> initialized or already closed - <span class="keyword">call</span> &#x27;refresh&#x27; before accessing beans via the ApplicationContext</span><br><span class="line">jvm <span class="number">1</span>    | java.lang.RuntimeException: Failed to execute <span class="built_in">start</span> task. Reason: java.lang.IllegalStateException: BeanFactory <span class="keyword">not</span> initialized or already closed - <span class="keyword">call</span> &#x27;refresh&#x27; before accessing beans via the ApplicationContext</span><br><span class="line">jvm <span class="number">1</span>    |      <span class="built_in">at</span> org.apache.activemq.console.command.StartCommand.runTask(StartCommand.java:<span class="number">91</span>)</span><br><span class="line">jvm <span class="number">1</span>    |      <span class="built_in">at</span> org.apache.activemq.console.command.AbstractCommand.execute(AbstractCommand.java:<span class="number">63</span>)</span><br><span class="line">jvm <span class="number">1</span>    |      <span class="built_in">at</span> org.apache.activemq.console.command.ShellCommand.runTask(ShellCommand.java:<span class="number">154</span>)</span><br><span class="line">jvm <span class="number">1</span>    |      <span class="built_in">at</span> org.apache.activemq.console.command.AbstractCommand.execute(AbstractCommand.java:<span class="number">63</span>)</span><br><span class="line">jvm <span class="number">1</span>    |      <span class="built_in">at</span> org.apache.activemq.console.command.ShellCommand.main(ShellCommand.java:<span class="number">104</span>)</span><br><span class="line">jvm <span class="number">1</span>    |      <span class="built_in">at</span> sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)</span><br><span class="line">jvm <span class="number">1</span>    |      <span class="built_in">at</span> sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:<span class="number">62</span>)</span><br><span class="line">jvm <span class="number">1</span>    |      <span class="built_in">at</span> sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:<span class="number">43</span>)</span><br><span class="line">jvm <span class="number">1</span>    |      <span class="built_in">at</span> java.lang.reflect.Method.invoke(Method.java:<span class="number">498</span>)</span><br><span class="line">jvm <span class="number">1</span>    |      <span class="built_in">at</span> org.apache.activemq.console.Main.runTaskClass(Main.java:<span class="number">262</span>)</span><br><span class="line">jvm <span class="number">1</span>    |      <span class="built_in">at</span> org.apache.activemq.console.Main.main(Main.java:<span class="number">115</span>)</span><br><span class="line">jvm <span class="number">1</span>    |      <span class="built_in">at</span> sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)</span><br><span class="line">jvm <span class="number">1</span>    |      <span class="built_in">at</span> sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:<span class="number">62</span>)</span><br><span class="line">jvm <span class="number">1</span>    |      <span class="built_in">at</span> sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:<span class="number">43</span>)</span><br><span class="line">jvm <span class="number">1</span>    |      <span class="built_in">at</span> java.lang.reflect.Method.invoke(Method.java:<span class="number">498</span>)</span><br><span class="line">jvm <span class="number">1</span>    |      <span class="built_in">at</span> org.tanukisoftware.wrapper.WrapperSimpleApp.run(WrapperSimpleApp.java:<span class="number">240</span>)</span><br><span class="line">jvm <span class="number">1</span>    |      <span class="built_in">at</span> java.lang.Thread.run(Thread.java:<span class="number">748</span>)</span><br><span class="line">jvm <span class="number">1</span>    | Caused by: java.lang.IllegalStateException: BeanFactory <span class="keyword">not</span> initialized or already closed - <span class="keyword">call</span> &#x27;refresh&#x27; before accessing beans via the ApplicationContext</span><br><span class="line">jvm <span class="number">1</span>    |      <span class="built_in">at</span> org.springframework.context.support.AbstractRefreshableApplicationContext.getBeanFactory(AbstractRefreshableApplicationContext.java:<span class="number">164</span>)</span><br><span class="line">jvm <span class="number">1</span>    |      <span class="built_in">at</span> org.springframework.context.support.AbstractApplicationContext.destroyBeans(AbstractApplicationContext.java:<span class="number">1034</span>)</span><br><span class="line">jvm <span class="number">1</span>    |      <span class="built_in">at</span> org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:<span class="number">555</span>)</span><br><span class="line">jvm <span class="number">1</span>    |      <span class="built_in">at</span> org.apache.xbean.spring.context.ResourceXmlApplicationContext.&lt;init&gt;(ResourceXmlApplicationContext.java:<span class="number">64</span>)</span><br><span class="line">jvm <span class="number">1</span>    |      <span class="built_in">at</span> org.apache.xbean.spring.context.ResourceXmlApplicationContext.&lt;init&gt;(ResourceXmlApplicationContext.java:<span class="number">52</span>)</span><br><span class="line">jvm <span class="number">1</span>    |      <span class="built_in">at</span> org.apache.activemq.xbean.XBeanBrokerFactory$<span class="number">1</span>.&lt;init&gt;(XBeanBrokerFactory.java:<span class="number">104</span>)</span><br><span class="line">jvm <span class="number">1</span>    |      <span class="built_in">at</span> org.apache.activemq.xbean.XBeanBrokerFactory.createApplicationContext(XBeanBrokerFactory.java:<span class="number">104</span>)</span><br><span class="line">jvm <span class="number">1</span>    |      <span class="built_in">at</span> org.apache.activemq.xbean.XBeanBrokerFactory.createBroker(XBeanBrokerFactory.java:<span class="number">67</span>)</span><br><span class="line">jvm <span class="number">1</span>    |      <span class="built_in">at</span> org.apache.activemq.broker.BrokerFactory.createBroker(BrokerFactory.java:<span class="number">71</span>)</span><br><span class="line">jvm <span class="number">1</span>    |      <span class="built_in">at</span> org.apache.activemq.broker.BrokerFactory.createBroker(BrokerFactory.java:<span class="number">54</span>)</span><br><span class="line">jvm <span class="number">1</span>    |      <span class="built_in">at</span> org.apache.activemq.console.command.StartCommand.runTask(StartCommand.java:<span class="number">87</span>)</span><br><span class="line">jvm <span class="number">1</span>    |      ... <span class="number">16</span> <span class="built_in">more</span></span><br><span class="line">jvm <span class="number">1</span>    | ERROR: java.lang.IllegalStateException: BeanFactory <span class="keyword">not</span> initialized or already closed - <span class="keyword">call</span> &#x27;refresh&#x27; before accessing beans via the ApplicationContext</span><br><span class="line">jvm <span class="number">1</span>    | java.lang.IllegalStateException: BeanFactory <span class="keyword">not</span> initialized or already closed - <span class="keyword">call</span> &#x27;refresh&#x27; before accessing beans via the ApplicationContext</span><br><span class="line">jvm <span class="number">1</span>    |      <span class="built_in">at</span> org.springframework.context.support.AbstractRefreshableApplicationContext.getBeanFactory(AbstractRefreshableApplicationContext.java:<span class="number">164</span>)</span><br><span class="line">jvm <span class="number">1</span>    |      <span class="built_in">at</span> org.springframework.context.support.AbstractApplicationContext.destroyBeans(AbstractApplicationContext.java:<span class="number">1034</span>)</span><br><span class="line">jvm <span class="number">1</span>    |      <span class="built_in">at</span> org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:<span class="number">555</span>)</span><br><span class="line">jvm <span class="number">1</span>    |      <span class="built_in">at</span> org.apache.xbean.spring.context.ResourceXmlApplicationContext.&lt;init&gt;(ResourceXmlApplicationContext.java:<span class="number">64</span>)</span><br><span class="line">jvm <span class="number">1</span>    |      <span class="built_in">at</span> org.apache.xbean.spring.context.ResourceXmlApplicationContext.&lt;init&gt;(ResourceXmlApplicationContext.java:<span class="number">52</span>)</span><br><span class="line">jvm <span class="number">1</span>    |      <span class="built_in">at</span> org.apache.activemq.xbean.XBeanBrokerFactory$<span class="number">1</span>.&lt;init&gt;(XBeanBrokerFactory.java:<span class="number">104</span>)</span><br><span class="line">jvm <span class="number">1</span>    |      <span class="built_in">at</span> org.apache.activemq.xbean.XBeanBrokerFactory.createApplicationContext(XBeanBrokerFactory.java:<span class="number">104</span>)</span><br><span class="line">jvm <span class="number">1</span>    |      <span class="built_in">at</span> org.apache.activemq.xbean.XBeanBrokerFactory.createBroker(XBeanBrokerFactory.java:<span class="number">67</span>)</span><br><span class="line">jvm <span class="number">1</span>    |      <span class="built_in">at</span> org.apache.activemq.broker.BrokerFactory.createBroker(BrokerFactory.java:<span class="number">71</span>)</span><br><span class="line">jvm <span class="number">1</span>    |      <span class="built_in">at</span> org.apache.activemq.broker.BrokerFactory.createBroker(BrokerFactory.java:<span class="number">54</span>)</span><br><span class="line">jvm <span class="number">1</span>    |      <span class="built_in">at</span> org.apache.activemq.console.command.StartCommand.runTask(StartCommand.java:<span class="number">87</span>)</span><br><span class="line">jvm <span class="number">1</span>    |      <span class="built_in">at</span> org.apache.activemq.console.command.AbstractCommand.execute(AbstractCommand.java:<span class="number">63</span>)</span><br><span class="line">jvm <span class="number">1</span>    |      <span class="built_in">at</span> org.apache.activemq.console.command.ShellCommand.runTask(ShellCommand.java:<span class="number">154</span>)</span><br><span class="line">jvm <span class="number">1</span>    |      <span class="built_in">at</span> org.apache.activemq.console.command.AbstractCommand.execute(AbstractCommand.java:<span class="number">63</span>)</span><br><span class="line">jvm <span class="number">1</span>    |      <span class="built_in">at</span> org.apache.activemq.console.command.ShellCommand.main(ShellCommand.java:<span class="number">104</span>)</span><br><span class="line">jvm <span class="number">1</span>    |      <span class="built_in">at</span> sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)</span><br><span class="line">jvm <span class="number">1</span>    |      <span class="built_in">at</span> sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:<span class="number">62</span>)</span><br><span class="line">jvm <span class="number">1</span>    |      <span class="built_in">at</span> sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:<span class="number">43</span>)</span><br><span class="line">jvm <span class="number">1</span>    |      <span class="built_in">at</span> java.lang.reflect.Method.invoke(Method.java:<span class="number">498</span>)</span><br><span class="line">jvm <span class="number">1</span>    |      <span class="built_in">at</span> org.apache.activemq.console.Main.runTaskClass(Main.java:<span class="number">262</span>)</span><br><span class="line">jvm <span class="number">1</span>    |      <span class="built_in">at</span> org.apache.activemq.console.Main.main(Main.java:<span class="number">115</span>)</span><br><span class="line">jvm <span class="number">1</span>    |      <span class="built_in">at</span> sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)</span><br><span class="line">jvm <span class="number">1</span>    |      <span class="built_in">at</span> sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:<span class="number">62</span>)</span><br><span class="line">jvm <span class="number">1</span>    |      <span class="built_in">at</span> sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:<span class="number">43</span>)</span><br><span class="line">jvm <span class="number">1</span>    |      <span class="built_in">at</span> java.lang.reflect.Method.invoke(Method.java:<span class="number">498</span>)</span><br><span class="line">jvm <span class="number">1</span>    |      <span class="built_in">at</span> org.tanukisoftware.wrapper.WrapperSimpleApp.run(WrapperSimpleApp.java:<span class="number">240</span>)</span><br><span class="line">jvm <span class="number">1</span>    |      <span class="built_in">at</span> java.lang.Thread.run(Thread.java:<span class="number">748</span>)</span><br><span class="line">wrapper  | &lt;-- Wrapper Stopped</span><br><span class="line">请按任意键继续. . .</span><br></pre></td></tr></table></figure><h5 id="整合-3"><a href="#整合-3" class="headerlink" title="整合"></a>整合</h5><p>​        做了这么多springboot整合第三方技术，已经摸到门路了，加坐标，做配置，调接口，直接开工</p><p><strong>步骤①</strong>：导入springboot整合ActiveMQ的starter</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.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-activemq<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>步骤②</strong>：配置ActiveMQ的服务器地址</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></pre></td><td class="code"><pre><span class="line"><span class="attr">spring:</span></span><br><span class="line">  <span class="attr">activemq:</span></span><br><span class="line">    <span class="attr">broker-url:</span> <span class="string">tcp://localhost:61616</span></span><br></pre></td></tr></table></figure><p><strong>步骤③</strong>：使用JmsMessagingTemplate操作ActiveMQ</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></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_">MessageServiceActivemqImpl</span> <span class="keyword">implements</span> <span class="title class_">MessageService</span> &#123;</span><br><span class="line">    <span class="meta">@Autowired</span></span><br><span class="line">    <span class="keyword">private</span> JmsMessagingTemplate messagingTemplate;</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_">sendMessage</span><span class="params">(String id)</span> &#123;</span><br><span class="line">        System.out.println(<span class="string">&quot;待发送短信的订单已纳入处理队列，id：&quot;</span>+id);</span><br><span class="line">        messagingTemplate.convertAndSend(<span class="string">&quot;order.queue.id&quot;</span>,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">public</span> String <span class="title function_">doMessage</span><span class="params">()</span> &#123;</span><br><span class="line">        <span class="type">String</span> <span class="variable">id</span> <span class="operator">=</span> messagingTemplate.receiveAndConvert(<span class="string">&quot;order.queue.id&quot;</span>,String.class);</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></pre></td></tr></table></figure><p>​        发送消息需要先将消息的类型转换成字符串，然后再发送，所以是convertAndSend，定义消息发送的位置，和具体的消息内容，此处使用id作为消息内容。</p><p>​        接收消息需要先将消息接收到，然后再转换成指定的数据类型，所以是receiveAndConvert，接收消息除了提供读取的位置，还要给出转换后的数据的具体类型。</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></pre></td><td class="code"><pre><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_">MessageListener</span> &#123;</span><br><span class="line">    <span class="meta">@JmsListener(destination = &quot;order.queue.id&quot;)</span></span><br><span class="line">    <span class="meta">@SendTo(&quot;order.other.queue.id&quot;)</span></span><br><span class="line">    <span class="keyword">public</span> String <span class="title function_">receive</span><span class="params">(String id)</span>&#123;</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> <span class="string">&quot;new:&quot;</span>+id;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>​        使用注解@JmsListener定义当前方法监听ActiveMQ中指定名称的消息队列。</p><p>​        如果当前消息队列处理完还需要继续向下传递当前消息到另一个队列中使用注解@SendTo即可，这样即可构造连续执行的顺序消息队列。</p><p><strong>步骤⑤</strong>：切换消息模型由点对点模型到发布订阅模型，修改jms配置即可</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></pre></td><td class="code"><pre><span class="line"><span class="attr">spring:</span></span><br><span class="line">  <span class="attr">activemq:</span></span><br><span class="line">    <span class="attr">broker-url:</span> <span class="string">tcp://localhost:61616</span></span><br><span class="line">  <span class="attr">jms:</span></span><br><span class="line">    <span class="attr">pub-sub-domain:</span> <span class="literal">true</span></span><br></pre></td></tr></table></figure><p>​        pub-sub-domain默认值为false，即点对点模型，修改为true后就是发布订阅模型。</p><p><strong>总结</strong></p><ol><li>springboot整合ActiveMQ提供了JmsMessagingTemplate对象作为客户端操作消息队列</li><li>操作ActiveMQ需要配置ActiveMQ服务器地址，默认端口61616</li><li>企业开发时通常使用监听器来处理消息队列中的消息，设置监听器使用注解@JmsListener</li><li>配置jms的pub-sub-domain属性可以在点对点模型和发布订阅模型间切换消息模型</li></ol><h4 id="SpringBoot整合RabbitMQ"><a href="#SpringBoot整合RabbitMQ" class="headerlink" title="SpringBoot整合RabbitMQ"></a>SpringBoot整合RabbitMQ</h4><p>​        RabbitMQ是MQ产品中的目前较为流行的产品之一，它遵从AMQP协议。RabbitMQ的底层实现语言使用的是Erlang，所以安装RabbitMQ需要先安装Erlang。</p><p><strong>Erlang安装</strong></p><p>​        windows版安装包下载地址：<a href="https://www.erlang.org/downloads">https</a><a href="https://www.erlang.org/downloads">://www.erlang.org/downloads</a></p><p>​        下载完毕后得到exe安装文件，一键傻瓜式安装，安装完毕需要重启，需要重启，需要重启。</p><p>​        安装的过程中可能会出现依赖Windows组件的提示，根据提示下载安装即可，都是自动执行的，如下：</p><p><img src="img\image-20220228164851551.png" alt="image-20220228164851551"></p><p>​        Erlang安装后需要配置环境变量，否则RabbitMQ将无法找到安装的Erlang。需要配置项如下，作用等同JDK配置环境变量的作用。</p><ul><li>ERLANG_HOME</li><li>PATH</li></ul><h5 id="安装-2"><a href="#安装-2" class="headerlink" title="安装"></a>安装</h5><p>​        windows版安装包下载地址：<a href="https://rabbitmq.com/install-windows.html">https://</a><a href="https://rabbitmq.com/install-windows.html">rabbitmq.com/install-windows.html</a></p><p>​        下载完毕后得到exe安装文件，一键傻瓜式安装，安装完毕后会得到如下文件</p><p><img src="img\image-20220228165151524.png" alt="image-20220228165151524" style="zoom:67%;" /></p><p><strong>启动服务器</strong></p><figure class="highlight cmd"><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">rabbitmq-service.bat <span class="built_in">start</span># 启动服务</span><br><span class="line">rabbitmq-service.bat stop# 停止服务</span><br><span class="line">rabbitmqctl status# 查看服务状态</span><br></pre></td></tr></table></figure><p>​        运行sbin目录下的rabbitmq-service.bat命令即可，start参数表示启动，stop参数表示退出，默认对外服务端口5672。</p><p>​        注意：启动rabbitmq的过程实际上是开启rabbitmq对应的系统服务，需要管理员权限方可执行。</p><p>​        说明：有没有感觉5672的服务端口很熟悉？activemq与rabbitmq有一个端口冲突问题，学习阶段无论操作哪一个？请确保另一个处于关闭状态。</p><p>​        说明：不喜欢命令行的小伙伴可以使用任务管理器中的服务页，找到RabbitMQ服务，使用鼠标右键菜单控制服务的启停。</p><p><img src="img\image-20220228170147193.png" alt="image-20220228170147193" style="zoom:67%;" /></p><p><strong>访问web管理服务</strong></p><p>​        RabbitMQ也提供有web控制台服务，但是此功能是一个插件，需要先启用才可以使用。</p><figure class="highlight cmd"><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">rabbitmq-plugins.bat list# 查看当前所有插件的运行状态</span><br><span class="line">rabbitmq-plugins.bat enable rabbitmq_management# 启动rabbitmq_management插件</span><br></pre></td></tr></table></figure><p>​        启动插件后可以在插件运行状态中查看是否运行，运行后通过浏览器即可打开服务后台管理界面</p><figure class="highlight cmd"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="function">http://<span class="title">localhost</span>:15672</span></span><br></pre></td></tr></table></figure><p>​        web管理服务默认端口15672，访问后可以打开RabbitMQ的管理界面，如下：</p><p><img src="img\image-20220228170504793.png" alt="image-20220228170504793"></p><p>​        首先输入访问用户名和密码，初始化用户名和密码相同，均为：guest，成功登录后进入管理后台界面，如下：</p><p><img src="img\image-20220228170535261.png" alt="image-20220228170535261"></p><h5 id="整合-direct模型"><a href="#整合-direct模型" class="headerlink" title="整合(direct模型)"></a>整合(direct模型)</h5><p>​        RabbitMQ满足AMQP协议，因此不同的消息模型对应的制作不同，先使用最简单的direct模型开发。</p><p><strong>步骤①</strong>：导入springboot整合amqp的starter，amqp协议默认实现为rabbitmq方案</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.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><p><strong>步骤②</strong>：配置RabbitMQ的服务器地址</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="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></pre></td></tr></table></figure><p><strong>步骤③</strong>：初始化直连模式系统设置</p><p>​        由于RabbitMQ不同模型要使用不同的交换机，因此需要先初始化RabbitMQ相关的对象，例如队列，交换机等</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">@Configuration</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">RabbitConfigDirect</span> &#123;</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_">directQueue</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_">Queue</span>(<span class="string">&quot;direct_queue&quot;</span>);</span><br><span class="line">    &#125;</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_">directQueue2</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_">Queue</span>(<span class="string">&quot;direct_queue2&quot;</span>);</span><br><span class="line">    &#125;</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_">directExchange</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;directExchange&quot;</span>);</span><br><span class="line">    &#125;</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_">bindingDirect</span><span class="params">()</span>&#123;</span><br><span class="line">        <span class="keyword">return</span> BindingBuilder.bind(directQueue()).to(directExchange()).with(<span class="string">&quot;direct&quot;</span>);</span><br><span class="line">    &#125;</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_">bindingDirect2</span><span class="params">()</span>&#123;</span><br><span class="line">        <span class="keyword">return</span> BindingBuilder.bind(directQueue2()).to(directExchange()).with(<span class="string">&quot;direct2&quot;</span>);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>​        队列Queue与直连交换机DirectExchange创建后，还需要绑定他们之间的关系Binding，这样就可以通过交换机操作对应队列。</p><p><strong>步骤④</strong>：使用AmqpTemplate操作RabbitMQ</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="meta">@Service</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">MessageServiceRabbitmqDirectImpl</span> <span class="keyword">implements</span> <span class="title class_">MessageService</span> &#123;</span><br><span class="line">    <span class="meta">@Autowired</span></span><br><span class="line">    <span class="keyword">private</span> AmqpTemplate amqpTemplate;</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_">sendMessage</span><span class="params">(String id)</span> &#123;</span><br><span class="line">        System.out.println(<span class="string">&quot;待发送短信的订单已纳入处理队列（rabbitmq direct），id：&quot;</span>+id);</span><br><span class="line">        amqpTemplate.convertAndSend(<span class="string">&quot;directExchange&quot;</span>,<span class="string">&quot;direct&quot;</span>,id);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>​        amqp协议中的操作API接口名称看上去和jms规范的操作API接口很相似，但是传递参数差异很大。</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></pre></td><td class="code"><pre><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_">MessageListener</span> &#123;</span><br><span class="line">    <span class="meta">@RabbitListener(queues = &quot;direct_queue&quot;)</span></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">receive</span><span class="params">(String id)</span>&#123;</span><br><span class="line">        System.out.println(<span class="string">&quot;已完成短信发送业务(rabbitmq direct)，id：&quot;</span>+id);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>​        使用注解@RabbitListener定义当前方法监听RabbitMQ中指定名称的消息队列。</p><h5 id="整合-topic模型"><a href="#整合-topic模型" class="headerlink" title="整合(topic模型)"></a>整合(topic模型)</h5><p><strong>步骤①</strong>：同上</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><span class="line">22</span><br><span class="line">23</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_">RabbitConfigTopic</span> &#123;</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_">topicQueue</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_">Queue</span>(<span class="string">&quot;topic_queue&quot;</span>);</span><br><span class="line">    &#125;</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_">topicQueue2</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_">Queue</span>(<span class="string">&quot;topic_queue2&quot;</span>);</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="meta">@Bean</span></span><br><span class="line">    <span class="keyword">public</span> TopicExchange <span class="title function_">topicExchange</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_">TopicExchange</span>(<span class="string">&quot;topicExchange&quot;</span>);</span><br><span class="line">    &#125;</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_">bindingTopic</span><span class="params">()</span>&#123;</span><br><span class="line">        <span class="keyword">return</span> BindingBuilder.bind(topicQueue()).to(topicExchange()).with(<span class="string">&quot;topic.*.id&quot;</span>);</span><br><span class="line">    &#125;</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_">bindingTopic2</span><span class="params">()</span>&#123;</span><br><span class="line">        <span class="keyword">return</span> BindingBuilder.bind(topicQueue2()).to(topicExchange()).with(<span class="string">&quot;topic.orders.*&quot;</span>);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>​        主题模式支持routingKey匹配模式，*表示匹配一个单词，#表示匹配任意内容，这样就可以通过主题交换机将消息分发到不同的队列中，详细内容请参看RabbitMQ系列课程。    </p><div class="table-container"><table><thead><tr><th><strong>匹配键</strong></th><th><strong>topic.*.*</strong></th><th><strong>topic.#</strong></th></tr></thead><tbody><tr><td>topic.order.id</td><td>true</td><td>true</td></tr><tr><td>order.topic.id</td><td>false</td><td>false</td></tr><tr><td>topic.sm.order.id</td><td>false</td><td>true</td></tr><tr><td>topic.sm.id</td><td>false</td><td>true</td></tr><tr><td>topic.id.order</td><td>true</td><td>true</td></tr><tr><td>topic.id</td><td>false</td><td>true</td></tr><tr><td>topic.order</td><td>false</td><td>true</td></tr></tbody></table></div><p><strong>步骤④</strong>：使用AmqpTemplate操作RabbitMQ</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="meta">@Service</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">MessageServiceRabbitmqTopicImpl</span> <span class="keyword">implements</span> <span class="title class_">MessageService</span> &#123;</span><br><span class="line">    <span class="meta">@Autowired</span></span><br><span class="line">    <span class="keyword">private</span> AmqpTemplate amqpTemplate;</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_">sendMessage</span><span class="params">(String id)</span> &#123;</span><br><span class="line">        System.out.println(<span class="string">&quot;待发送短信的订单已纳入处理队列（rabbitmq topic），id：&quot;</span>+id);</span><br><span class="line">        amqpTemplate.convertAndSend(<span class="string">&quot;topicExchange&quot;</span>,<span class="string">&quot;topic.orders.id&quot;</span>,id);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>​        发送消息后，根据当前提供的routingKey与绑定交换机时设定的routingKey进行匹配，规则匹配成功消息才会进入到对应的队列中。</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></pre></td><td class="code"><pre><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_">MessageListener</span> &#123;</span><br><span class="line">    <span class="meta">@RabbitListener(queues = &quot;topic_queue&quot;)</span></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">receive</span><span class="params">(String id)</span>&#123;</span><br><span class="line">        System.out.println(<span class="string">&quot;已完成短信发送业务(rabbitmq topic 1)，id：&quot;</span>+id);</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="meta">@RabbitListener(queues = &quot;topic_queue2&quot;)</span></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">receive2</span><span class="params">(String id)</span>&#123;</span><br><span class="line">        System.out.println(<span class="string">&quot;已完成短信发送业务(rabbitmq topic 22222222)，id：&quot;</span>+id);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>​        使用注解@RabbitListener定义当前方法监听RabbitMQ中指定名称的消息队列。</p><p><strong>总结</strong></p><ol><li>springboot整合RabbitMQ提供了AmqpTemplate对象作为客户端操作消息队列</li><li>操作ActiveMQ需要配置ActiveMQ服务器地址，默认端口5672</li><li>企业开发时通常使用监听器来处理消息队列中的消息，设置监听器使用注解@RabbitListener</li><li>RabbitMQ有5种消息模型，使用的队列相同，但是交换机不同。交换机不同，对应的消息进入的策略也不同</li></ol><h4 id="SpringBoot整合RocketMQ"><a href="#SpringBoot整合RocketMQ" class="headerlink" title="SpringBoot整合RocketMQ"></a>SpringBoot整合RocketMQ</h4><p>​        RocketMQ由阿里研发，后捐赠给apache基金会，目前是apache基金会顶级项目之一，也是目前市面上的MQ产品中较为流行的产品之一，它遵从AMQP协议。</p><h5 id="安装-3"><a href="#安装-3" class="headerlink" title="安装"></a>安装</h5><p>​        windows版安装包下载地址：<a href="https://rocketmq.apache.org/">https://rocketmq.apache.org</a><a href="https://rocketmq.apache.org/">/</a></p><p>​        下载完毕后得到zip压缩文件，解压缩即可使用，解压后得到如下文件</p><p><img src="img\image-20220228174453471.png" alt="image-20220228174453471"></p><p>​        RocketMQ安装后需要配置环境变量，具体如下：</p><ul><li>ROCKETMQ_HOME</li><li>PATH</li><li>NAMESRV_ADDR （建议）： 127.0.0.1:9876</li></ul><p>​        关于NAMESRV_ADDR对于初学者来说建议配置此项，也可以通过命令设置对应值，操作略显繁琐，建议配置。系统学习RocketMQ知识后即可灵活控制该项。</p><p><strong>RocketMQ工作模式</strong></p><p>​        在RocketMQ中，处理业务的服务器称为broker，生产者与消费者不是直接与broker联系的，而是通过命名服务器进行通信。broker启动后会通知命名服务器自己已经上线，这样命名服务器中就保存有所有的broker信息。当生产者与消费者需要连接broker时，通过命名服务器找到对应的处理业务的broker，因此命名服务器在整套结构中起到一个信息中心的作用。并且broker启动前必须保障命名服务器先启动。</p><p><img src="img\image-20220228175123790.png" alt="image-20220228175123790" style="zoom:80%;" /></p><p><strong>启动服务器</strong></p><figure class="highlight cmd"><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">mqnamesrv# 启动命名服务器</span><br><span class="line">mqbroker# 启动broker</span><br></pre></td></tr></table></figure><p>​        运行bin目录下的mqnamesrv命令即可启动命名服务器，默认对外服务端口9876。</p><p>​        运行bin目录下的mqbroker命令即可启动broker服务器，如果环境变量中没有设置NAMESRV_ADDR则需要在运行mqbroker指令前通过set指令设置NAMESRV_ADDR的值，并且每次开启均需要设置此项。</p><p><strong>测试服务器启动状态</strong></p><p>​        RocketMQ提供有一套测试服务器功能的测试程序，运行bin目录下的tools命令即可使用。</p><figure class="highlight cmd"><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">tools org.apache.rocketmq.example.quickstart.Producer# 生产消息</span><br><span class="line">tools org.apache.rocketmq.example.quickstart.Consumer# 消费消息</span><br></pre></td></tr></table></figure><h5 id="整合（异步消息）"><a href="#整合（异步消息）" class="headerlink" title="整合（异步消息）"></a>整合（异步消息）</h5><p><strong>步骤①</strong>：导入springboot整合RocketMQ的starter，此坐标不由springboot维护版本</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>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.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>：配置RocketMQ的服务器地址</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="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">group_rocketmq</span></span><br></pre></td></tr></table></figure><p>​        设置默认的生产者消费者所属组group。</p><p><strong>步骤③</strong>：使用RocketMQTemplate操作RocketMQ</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="meta">@Service</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">MessageServiceRocketmqImpl</span> <span class="keyword">implements</span> <span class="title class_">MessageService</span> &#123;</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="meta">@Override</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 id)</span> &#123;</span><br><span class="line">        System.out.println(<span class="string">&quot;待发送短信的订单已纳入处理队列（rocketmq），id：&quot;</span>+id);</span><br><span class="line">        <span class="type">SendCallback</span> <span class="variable">callback</span> <span class="operator">=</span> <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 sendResult)</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 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>);</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;;</span><br><span class="line">        rocketMQTemplate.asyncSend(<span class="string">&quot;order_id&quot;</span>,id,callback);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br></pre></td></tr></table></figure><p>​        使用asyncSend方法发送异步消息。</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></pre></td><td class="code"><pre><span class="line"><span class="meta">@Component</span></span><br><span class="line"><span class="meta">@RocketMQMessageListener(topic = &quot;order_id&quot;,consumerGroup = &quot;group_rocketmq&quot;)</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">MessageListener</span> <span class="keyword">implements</span> <span class="title class_">RocketMQListener</span>&lt;String&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">(String id)</span> &#123;</span><br><span class="line">        System.out.println(<span class="string">&quot;已完成短信发送业务(rocketmq)，id：&quot;</span>+id);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>​        RocketMQ的监听器必须按照标准格式开发，实现RocketMQListener接口，泛型为消息类型。</p><p>​        使用注解@RocketMQMessageListener定义当前类监听RabbitMQ中指定组、指定名称的消息队列。</p><p><strong>总结</strong></p><ol><li>springboot整合RocketMQ使用RocketMQTemplate对象作为客户端操作消息队列</li><li>操作RocketMQ需要配置RocketMQ服务器地址，默认端口9876</li><li>企业开发时通常使用监听器来处理消息队列中的消息，设置监听器使用注解@RocketMQMessageListener</li></ol><h4 id="SpringBoot整合Kafka"><a href="#SpringBoot整合Kafka" class="headerlink" title="SpringBoot整合Kafka"></a>SpringBoot整合Kafka</h4><h5 id="安装-4"><a href="#安装-4" class="headerlink" title="安装"></a>安装</h5><p>​        windows版安装包下载地址：<a href="https://kafka.apache.org/downloads">https://</a><a href="https://kafka.apache.org/downloads">kafka.apache.org/downloads</a></p><p>​        下载完毕后得到tgz压缩文件，使用解压缩软件解压缩即可使用，解压后得到如下文件</p><p><img src="img\image-20220228181442155.png" alt="image-20220228181442155"></p><p>​        建议使用windows版2.8.1版本。</p><p><strong>启动服务器</strong></p><p>​        kafka服务器的功能相当于RocketMQ中的broker，kafka运行还需要一个类似于命名服务器的服务。在kafka安装目录中自带一个类似于命名服务器的工具，叫做zookeeper，它的作用是注册中心，相关知识请到对应课程中学习。</p><figure class="highlight cmd"><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">zookeeper-server-<span class="built_in">start</span>.bat ..\..\config\zookeeper.properties# 启动zookeeper</span><br><span class="line">kafka-server-<span class="built_in">start</span>.bat ..\..\config\server.properties# 启动kafka</span><br></pre></td></tr></table></figure><p>​        运行bin目录下的windows目录下的zookeeper-server-start命令即可启动注册中心，默认对外服务端口2181。</p><p>​        运行bin目录下的windows目录下的kafka-server-start命令即可启动kafka服务器，默认对外服务端口9092。</p><p><strong>创建主题</strong></p><p>​        和之前操作其他MQ产品相似，kakfa也是基于主题操作，操作之前需要先初始化topic。</p><figure class="highlight cmd"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><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"># 创建topic</span><br><span class="line">kafka-topics.bat --create --zookeeper localhost:<span class="number">2181</span> --replication-factor <span class="number">1</span> --partitions <span class="number">1</span> --topic itheima</span><br><span class="line"># 查询topic</span><br><span class="line">kafka-topics.bat --zookeeper <span class="number">127</span>.<span class="number">0</span>.<span class="number">0</span>.<span class="number">1</span>:<span class="number">2181</span> --list</span><br><span class="line"># 删除topic</span><br><span class="line">kafka-topics.bat --delete --zookeeper localhost:<span class="number">2181</span> --topic itheima</span><br></pre></td></tr></table></figure><p><strong>测试服务器启动状态</strong></p><p>​        Kafka提供有一套测试服务器功能的测试程序，运行bin目录下的windows目录下的命令即可使用。</p><figure class="highlight cmd"><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">kafka-console-producer.bat --broker-list localhost:<span class="number">9092</span> --topic itheima# 测试生产消息</span><br><span class="line">kafka-console-consumer.bat --bootstrap-server localhost:<span class="number">9092</span> --topic itheima --from-beginning# 测试消息消费</span><br></pre></td></tr></table></figure><h5 id="整合-4"><a href="#整合-4" class="headerlink" title="整合"></a>整合</h5><p><strong>步骤①</strong>：导入springboot整合Kafka的starter，此坐标由springboot维护版本</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.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><p><strong>步骤②</strong>：配置Kafka的服务器地址</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></pre></td><td class="code"><pre><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">consumer:</span></span><br><span class="line">      <span class="attr">group-id:</span> <span class="string">order</span></span><br></pre></td></tr></table></figure><p>​        设置默认的生产者消费者所属组id。</p><p><strong>步骤③</strong>：使用KafkaTemplate操作Kafka</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="meta">@Service</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">MessageServiceKafkaImpl</span> <span class="keyword">implements</span> <span class="title class_">MessageService</span> &#123;</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="meta">@Override</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 id)</span> &#123;</span><br><span class="line">        System.out.println(<span class="string">&quot;待发送短信的订单已纳入处理队列（kafka），id：&quot;</span>+id);</span><br><span class="line">        kafkaTemplate.send(<span class="string">&quot;itheima2022&quot;</span>,id);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>​        使用send方法发送消息，需要传入topic名称。</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></pre></td><td class="code"><pre><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_">MessageListener</span> &#123;</span><br><span class="line">    <span class="meta">@KafkaListener(topics = &quot;itheima2022&quot;)</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">(ConsumerRecord&lt;String,String&gt; record)</span>&#123;</span><br><span class="line">        System.out.println(<span class="string">&quot;已完成短信发送业务(kafka)，id：&quot;</span>+record.value());</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>​        使用注解@KafkaListener定义当前方法监听Kafka中指定topic的消息，接收到的消息封装在对象ConsumerRecord中，获取数据从ConsumerRecord对象中获取即可。</p><p><strong>总结</strong></p><ol><li><p>springboot整合Kafka使用KafkaTemplate对象作为客户端操作消息队列</p></li><li><p>操作Kafka需要配置Kafka服务器地址，默认端口9092</p></li><li><p>企业开发时通常使用监听器来处理消息队列中的消息，设置监听器使用注解@KafkaListener。接收消息保存在形参ConsumerRecord对象中</p></li></ol><h2 id="KF-6-监控"><a href="#KF-6-监控" class="headerlink" title="KF-6.监控"></a>KF-6.监控</h2><p>​        在说监控之前，需要回顾一下软件业的发展史。最早的软件完成一些非常简单的功能，代码不多，错误也少。随着软件功能的逐步完善，软件的功能变得越来越复杂，功能不能得到有效的保障，这个阶段出现了针对软件功能的检测，也就是软件测试。伴随着计算机操作系统的逐步升级，软件的运行状态也变得开始让人捉摸不透，出现了不稳定的状况。伴随着计算机网络的发展，程序也从单机状态切换成基于计算机网络的程序，应用于网络的程序开始出现，由于网络的不稳定性，程序的运行状态让使用者更加堪忧。互联网的出现彻底打破了软件的思维模式，随之而来的互联网软件就更加凸显出应对各种各样复杂的网络情况之下的弱小。计算机软件的运行状况已经成为了软件运行的一个大话题，针对软件的运行状况就出现了全新的思维，建立起了初代的软件运行状态监控。</p><p>​        什么是监控？就是通过软件的方式展示另一个软件的运行情况，运行的情况则通过各种各样的指标数据反馈给监控人员。例如网络是否顺畅、服务器是否在运行、程序的功能是否能够整百分百运行成功，内存是否够用，等等等等。</p><p>​        本章要讲解的监控就是对软件的运行情况进行监督，但是springboot程序与非springboot程序的差异还是很大的，为了方便监控软件的开发，springboot提供了一套功能接口，为开发者加速开发过程。</p><h3 id="KF-6-1-监控的意义"><a href="#KF-6-1-监控的意义" class="headerlink" title="KF-6-1.监控的意义"></a>KF-6-1.监控的意义</h3><p>​        对于现代的互联网程序来说，规模越来越大，功能越来越复杂，还要追求更好的客户体验，因此要监控的信息量也就比较大了。由于现在的互联网程序大部分都是基于微服务的程序，一个程序的运行需要若干个服务来保障，因此第一个要监控的指标就是服务是否正常运行，也就是<strong>监控服务状态是否处理宕机状态</strong>。一旦发现某个服务宕机了，必须马上给出对应的解决方案，避免整体应用功能受影响。其次，由于互联网程序服务的客户量是巨大的，当客户的请求在短时间内集中达到服务器后，就会出现各种程序运行指标的波动。比如内存占用严重，请求无法及时响应处理等，这就是第二个要监控的重要指标，<strong>监控服务运行指标</strong>。虽然软件是对外提供用户的访问需求，完成对应功能的，但是后台的运行是否平稳，是否出现了不影响客户使用的功能隐患，这些也是要密切监控的，此时就需要在不停机的情况下，监控系统运行情况，日志是一个不错的手段。如果在众多日志中找到开发者或运维人员所关注的日志信息，简单快速有效的过滤出要看的日志也是监控系统需要考虑的问题，这就是第三个要监控的指标，<strong>监控程序运行日志</strong>。虽然我们期望程序一直平稳运行，但是由于突发情况的出现，例如服务器被攻击、服务器内存溢出等情况造成了服务器宕机，此时当前服务不能满足使用需要，就要将其重启甚至关闭，如果快速控制服务器的启停也是程序运行过程中不可回避的问题，这就是第四个监控项，<strong>管理服务状态</strong>。以上这些仅仅是从大的方面来思考监控这个问题，还有很多的细节点，例如上线了一个新功能，定时提醒用户续费，这种功能不是上线后马上就运行的，但是当前功能是否真的启动，如果快速的查询到这个功能已经开启，这也是监控中要解决的问题，等等。看来监控真的是一项非常重要的工作。</p><p>​        通过上述描述，可以看出监控很重要。那具体的监控要如何开展呢？还要从实际的程序运行角度出发。比如现在有3个服务支撑着一个程序的运行，每个服务都有自己的运行状态。</p><p><img src="img\image-20220301093704396.png" alt="image-20220301093704396" style="zoom:50%;" /></p><p>​        此时被监控的信息就要在三个不同的程序中去查询并展示，但是三个服务是服务于一个程序的运行的，如果不能合并到一个平台上展示，监控工作量巨大，而且信息对称性差，要不停的在三个监控端查看数据。如果将业务放大成30个，300个，3000个呢？看来必须有一个单独的平台，将多个被监控的服务对应的监控指标信息汇总在一起，这样更利于监控工作的开展。</p><p><img src="img\image-20220301094001896.png" alt="image-20220301094001896" style="zoom:50%;" /></p><p>​        新的程序专门用来监控，新的问题就出现了，是被监控程序主动上报信息还是监控程序主动获取信息？如果监控程序不能主动获取信息，这就意味着监控程序有可能看到的是很久之前被监控程序上报的信息，万一被监控程序宕机了，监控程序就无法区分究竟是好久没法信息了，还是已经下线了。所以监控程序必须具有主动发起请求获取被监控服务信息的能力。</p><p><img src="img\image-20220301094259844.png" alt="image-20220301094259844" style="zoom:50%;" /></p><p>​        如果监控程序要监控服务时，主动获取对方的信息。那监控程序如何知道哪些程序被自己监控呢？不可能在监控程序中设置我监控谁，这样互联网上的所有程序岂不是都可以被监控到，这样的话信息安全将无法得到保障。合理的做法只能是在被监控程序启动时上报监控程序，告诉监控程序你可以监控我了。看来需要在被监控程序端做主动上报的操作，这就要求被监控程序中配置对应的监控程序是谁。</p><p><img src="img\image-20220301094547748.png" alt="image-20220301094547748" style="zoom:50%;" /></p><p>​        被监控程序可以提供各种各样的指标数据给监控程序看，但是每一个指标都代表着公司的机密信息，并不是所有的指标都可以给任何人看的，乃至运维人员，所以对被监控指标的是否开放出来给监控系统看，也需要做详细的设定。</p><p>​        以上描述的整个过程就是一个监控系统的基本流程。</p><p><strong>总结</strong></p><ol><li>监控是一个非常重要的工作，是保障程序正常运行的基础手段</li><li>监控的过程通过一个监控程序进行，它汇总所有被监控的程序的信息集中统一展示</li><li>被监控程序需要主动上报自己被监控，同时要设置哪些指标被监控</li></ol><p><strong>思考</strong></p><p>​        下面就要开始做监控了，新的问题就来了，监控程序怎么做呢？难道要自己写吗？肯定是不现实的，如何进行监控，咱们下节再讲。</p><h3 id="KF-6-2-可视化监控平台"><a href="#KF-6-2-可视化监控平台" class="headerlink" title="KF-6-2.可视化监控平台"></a>KF-6-2.可视化监控平台</h3><p>​        springboot抽取了大部分监控系统的常用指标，提出了监控的总思想。然后就有好心的同志根据监控的总思想，制作了一个通用性很强的监控系统，因为是基于springboot监控的核心思想制作的，所以这个程序被命名为<strong>Spring Boot Admin</strong>。</p><p>​        Spring Boot Admin，这是一个开源社区项目，用于管理和监控SpringBoot应用程序。这个项目中包含有客户端和服务端两部分，而监控平台指的就是服务端。我们做的程序如果需要被监控，将我们做的程序制作成客户端，然后配置服务端地址后，服务端就可以通过HTTP请求的方式从客户端获取对应的信息，并通过UI界面展示对应信息。</p><p>​        下面就来开发这套监控程序，先制作服务端，其实服务端可以理解为是一个web程序，收到一些信息后展示这些信息。</p><p><strong>服务端开发</strong></p><p><strong>步骤①</strong>：导入springboot admin对应的starter，版本与当前使用的springboot版本保持一致，并将其配置成web工程</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="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>de.codecentric<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-admin-starter-server<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.5.4<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="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></pre></td></tr></table></figure><p>​        上述过程可以通过创建项目时使用勾选的形式完成。</p><p><img src="img\image-20220301102432817.png" alt="image-20220301102432817" style="zoom:50%;" /></p><p><strong>步骤②</strong>：在引导类上添加注解@EnableAdminServer，声明当前应用启动后作为SpringBootAdmin的服务器使用</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="meta">@SpringBootApplication</span></span><br><span class="line"><span class="meta">@EnableAdminServer</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">Springboot25AdminServerApplication</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(Springboot25AdminServerApplication.class, args);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>​        做到这里，这个服务器就开发好了，启动后就可以访问当前程序了，界面如下。</p><p><img src="img\image-20220301103028468.png" alt="image-20220301103028468" style="zoom: 50%;" /></p><p>​        由于目前没有启动任何被监控的程序，所以里面什么信息都没有。下面制作一个被监控的客户端程序。</p><p><strong>客户端开发</strong></p><p>​        客户端程序开发其实和服务端开发思路基本相似，多了一些配置而已。</p><p><strong>步骤①</strong>：导入springboot admin对应的starter，版本与当前使用的springboot版本保持一致，并将其配置成web工程</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="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>de.codecentric<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-admin-starter-client<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.5.4<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="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></pre></td></tr></table></figure><p>​        上述过程也可以通过创建项目时使用勾选的形式完成，不过一定要小心，端口配置成不一样的，否则会冲突。</p><p><strong>步骤②</strong>：设置当前客户端将信息上传到哪个服务器上，通过yml文件配置</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></pre></td><td class="code"><pre><span class="line"><span class="attr">spring:</span></span><br><span class="line">  <span class="attr">boot:</span></span><br><span class="line">    <span class="attr">admin:</span></span><br><span class="line">      <span class="attr">client:</span></span><br><span class="line">        <span class="attr">url:</span> <span class="string">http://localhost:8080</span></span><br></pre></td></tr></table></figure><p>​        做到这里，这个客户端就可以启动了。启动后再次访问服务端程序，界面如下。</p><p><img src="img\image-20220301103838079.png" alt="image-20220301103838079" style="zoom: 50%;" /></p><p>​        可以看到，当前监控了1个程序，点击进去查看详细信息。</p><p><img src="img\image-20220301103936386.png" alt="image-20220301103936386" style="zoom: 50%;" /></p><p>​        由于当前没有设置开放哪些信息给监控服务器，所以目前看不到什么有效的信息。下面需要做两组配置就可以看到信息了。</p><ol><li><p>开放指定信息给服务器看</p></li><li><p>允许服务器以HTTP请求的方式获取对应的信息</p><p>配置如下：</p></li></ol><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="attr">server:</span></span><br><span class="line">  <span class="attr">port:</span> <span class="number">80</span></span><br><span class="line"><span class="attr">spring:</span></span><br><span class="line">  <span class="attr">boot:</span></span><br><span class="line">    <span class="attr">admin:</span></span><br><span class="line">      <span class="attr">client:</span></span><br><span class="line">        <span class="attr">url:</span> <span class="string">http://localhost:8080</span></span><br><span class="line"><span class="attr">management:</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">always</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">&quot;*&quot;</span></span><br></pre></td></tr></table></figure><p>​        上述配置对于初学者来说比较容易混淆。简单解释一下，到下一节再做具体的讲解。springbootadmin的客户端默认开放了13组信息给服务器，但是这些信息除了一个之外，其他的信息都不让通过HTTP请求查看。所以你看到的信息基本上就没什么内容了，只能看到一个内容，就是下面的健康信息。</p><p><img src="img\image-20220301104742563.png" alt="image-20220301104742563" style="zoom: 50%;" /></p><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></pre></td><td class="code"><pre><span class="line"><span class="attr">management:</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">always</span></span><br></pre></td></tr></table></figure><p>​        健康明细信息如下：</p><p><img src="img\image-20220301105116554.png" alt="image-20220301105116554" style="zoom: 50%;" /></p><p>​        目前除了健康信息，其他信息都查阅不了。原因在于其他12种信息是默认不提供给服务器通过HTTP请求查阅的，所以需要开启查阅的内容项，使用*表示查阅全部。记得带引号。</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="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">&quot;*&quot;</span></span><br></pre></td></tr></table></figure><p>​        配置后再刷新服务器页面，就可以看到所有的信息了。</p><p><img src="img\image-20220301105554494.png" alt="image-20220301105554494" style="zoom: 50%;" /></p><p>​        以上界面中展示的信息量就非常大了，包含了13组信息，有性能指标监控，加载的bean列表，加载的系统属性，日志的显示控制等等。</p><p><strong>配置多个客户端</strong></p><p>​        可以通过配置客户端的方式在其他的springboot程序中添加客户端坐标，这样当前服务器就可以监控多个客户端程序了。每个客户端展示不同的监控信息。</p><p><img src="img\image-20220301110352170.png" alt="image-20220301110352170" style="zoom: 50%;" /></p><p>​        进入监控面板，如果你加载的应用具有功能，在监控面板中可以看到3组信息展示的与之前加载的空工程不一样。</p><ul><li>类加载面板中可以查阅到开发者自定义的类，如左图</li></ul><p>​                        <img src="img\image-20220301161246835.png" alt="image-20220301161246835" style="zoom:33%;" /><img src="img\image-20220301161949431.png" alt="image-20220301161949431" style="zoom:33%;" /></p><ul><li>映射中可以查阅到当前应用配置的所有请求</li></ul><p>​                        <img src="img\image-20220301161418791.png" alt="image-20220301161418791" style="zoom: 33%;" /><img src="img\image-20220301162008737.png" alt="image-20220301162008737" style="zoom:33%;" /></p><ul><li>性能指标中可以查阅当前应用独有的请求路径统计数据</li></ul><p>​                        <img src="img\image-20220301161906949.png" alt="image-20220301161906949" style="zoom: 33%;" /><img src="img\image-20220301162040670.png" alt="image-20220301162040670" style="zoom: 33%;" /></p><p><strong>总结</strong></p><ol><li>开发监控服务端需要导入坐标，然后在引导类上添加注解@EnableAdminServer，并将其配置成web程序即可</li><li>开发被监控的客户端需要导入坐标，然后配置服务端服务器地址，并做开放指标的设定即可</li><li>在监控平台中可以查阅到各种各样被监控的指标，前提是客户端开放了被监控的指标</li></ol><p><strong>思考</strong></p><p>​        之前说过，服务端要想监控客户端，需要主动的获取到对应信息并展示出来。但是目前我们并没有在客户端开发任何新的功能，但是服务端确可以获取监控信息，谁帮我们做的这些功能呢？咱们下一节再讲。</p><h3 id="KF-6-3-监控原理"><a href="#KF-6-3-监控原理" class="headerlink" title="KF-6-3.监控原理"></a>KF-6-3.监控原理</h3><p>​        通过查阅监控中的映射指标，可以看到当前系统中可以运行的所有请求路径，其中大部分路径以/actuator开头</p><p><img src="img\image-20220301170214076.png" alt="image-20220301170214076" style="zoom: 50%;" /></p><p>​        首先这些请求路径不是开发者自己编写的，其次这个路径代表什么含义呢？既然这个路径可以访问，就可以通过浏览器发送该请求看看究竟可以得到什么信息。</p><p><img src="img\image-20220301170723057.png" alt="image-20220301170723057"></p><p>​        通过发送请求，可以得到一组json信息，如下</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><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><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></pre></td><td class="code"><pre><span class="line"><span class="punctuation">&#123;</span></span><br><span class="line">    <span class="attr">&quot;_links&quot;</span><span class="punctuation">:</span> <span class="punctuation">&#123;</span></span><br><span class="line">        <span class="attr">&quot;self&quot;</span><span class="punctuation">:</span> <span class="punctuation">&#123;</span></span><br><span class="line">            <span class="attr">&quot;href&quot;</span><span class="punctuation">:</span> <span class="string">&quot;http://localhost:81/actuator&quot;</span><span class="punctuation">,</span></span><br><span class="line">            <span class="attr">&quot;templated&quot;</span><span class="punctuation">:</span> <span class="literal"><span class="keyword">false</span></span></span><br><span class="line">        <span class="punctuation">&#125;</span><span class="punctuation">,</span></span><br><span class="line">        <span class="attr">&quot;beans&quot;</span><span class="punctuation">:</span> <span class="punctuation">&#123;</span></span><br><span class="line">            <span class="attr">&quot;href&quot;</span><span class="punctuation">:</span> <span class="string">&quot;http://localhost:81/actuator/beans&quot;</span><span class="punctuation">,</span></span><br><span class="line">            <span class="attr">&quot;templated&quot;</span><span class="punctuation">:</span> <span class="literal"><span class="keyword">false</span></span></span><br><span class="line">        <span class="punctuation">&#125;</span><span class="punctuation">,</span></span><br><span class="line">        <span class="attr">&quot;caches-cache&quot;</span><span class="punctuation">:</span> <span class="punctuation">&#123;</span></span><br><span class="line">            <span class="attr">&quot;href&quot;</span><span class="punctuation">:</span> <span class="string">&quot;http://localhost:81/actuator/caches/&#123;cache&#125;&quot;</span><span class="punctuation">,</span></span><br><span class="line">            <span class="attr">&quot;templated&quot;</span><span class="punctuation">:</span> <span class="literal"><span class="keyword">true</span></span></span><br><span class="line">        <span class="punctuation">&#125;</span><span class="punctuation">,</span></span><br><span class="line">        <span class="attr">&quot;caches&quot;</span><span class="punctuation">:</span> <span class="punctuation">&#123;</span></span><br><span class="line">            <span class="attr">&quot;href&quot;</span><span class="punctuation">:</span> <span class="string">&quot;http://localhost:81/actuator/caches&quot;</span><span class="punctuation">,</span></span><br><span class="line">            <span class="attr">&quot;templated&quot;</span><span class="punctuation">:</span> <span class="literal"><span class="keyword">false</span></span></span><br><span class="line">        <span class="punctuation">&#125;</span><span class="punctuation">,</span></span><br><span class="line">        <span class="attr">&quot;health&quot;</span><span class="punctuation">:</span> <span class="punctuation">&#123;</span></span><br><span class="line">            <span class="attr">&quot;href&quot;</span><span class="punctuation">:</span> <span class="string">&quot;http://localhost:81/actuator/health&quot;</span><span class="punctuation">,</span></span><br><span class="line">            <span class="attr">&quot;templated&quot;</span><span class="punctuation">:</span> <span class="literal"><span class="keyword">false</span></span></span><br><span class="line">        <span class="punctuation">&#125;</span><span class="punctuation">,</span></span><br><span class="line">        <span class="attr">&quot;health-path&quot;</span><span class="punctuation">:</span> <span class="punctuation">&#123;</span></span><br><span class="line">            <span class="attr">&quot;href&quot;</span><span class="punctuation">:</span> <span class="string">&quot;http://localhost:81/actuator/health/&#123;*path&#125;&quot;</span><span class="punctuation">,</span></span><br><span class="line">            <span class="attr">&quot;templated&quot;</span><span class="punctuation">:</span> <span class="literal"><span class="keyword">true</span></span></span><br><span class="line">        <span class="punctuation">&#125;</span><span class="punctuation">,</span></span><br><span class="line">        <span class="attr">&quot;info&quot;</span><span class="punctuation">:</span> <span class="punctuation">&#123;</span></span><br><span class="line">            <span class="attr">&quot;href&quot;</span><span class="punctuation">:</span> <span class="string">&quot;http://localhost:81/actuator/info&quot;</span><span class="punctuation">,</span></span><br><span class="line">            <span class="attr">&quot;templated&quot;</span><span class="punctuation">:</span> <span class="literal"><span class="keyword">false</span></span></span><br><span class="line">        <span class="punctuation">&#125;</span><span class="punctuation">,</span></span><br><span class="line">        <span class="attr">&quot;conditions&quot;</span><span class="punctuation">:</span> <span class="punctuation">&#123;</span></span><br><span class="line">            <span class="attr">&quot;href&quot;</span><span class="punctuation">:</span> <span class="string">&quot;http://localhost:81/actuator/conditions&quot;</span><span class="punctuation">,</span></span><br><span class="line">            <span class="attr">&quot;templated&quot;</span><span class="punctuation">:</span> <span class="literal"><span class="keyword">false</span></span></span><br><span class="line">        <span class="punctuation">&#125;</span><span class="punctuation">,</span></span><br><span class="line">        <span class="attr">&quot;shutdown&quot;</span><span class="punctuation">:</span> <span class="punctuation">&#123;</span></span><br><span class="line">            <span class="attr">&quot;href&quot;</span><span class="punctuation">:</span> <span class="string">&quot;http://localhost:81/actuator/shutdown&quot;</span><span class="punctuation">,</span></span><br><span class="line">            <span class="attr">&quot;templated&quot;</span><span class="punctuation">:</span> <span class="literal"><span class="keyword">false</span></span></span><br><span class="line">        <span class="punctuation">&#125;</span><span class="punctuation">,</span></span><br><span class="line">        <span class="attr">&quot;configprops&quot;</span><span class="punctuation">:</span> <span class="punctuation">&#123;</span></span><br><span class="line">            <span class="attr">&quot;href&quot;</span><span class="punctuation">:</span> <span class="string">&quot;http://localhost:81/actuator/configprops&quot;</span><span class="punctuation">,</span></span><br><span class="line">            <span class="attr">&quot;templated&quot;</span><span class="punctuation">:</span> <span class="literal"><span class="keyword">false</span></span></span><br><span class="line">        <span class="punctuation">&#125;</span><span class="punctuation">,</span></span><br><span class="line">        <span class="attr">&quot;configprops-prefix&quot;</span><span class="punctuation">:</span> <span class="punctuation">&#123;</span></span><br><span class="line">            <span class="attr">&quot;href&quot;</span><span class="punctuation">:</span> <span class="string">&quot;http://localhost:81/actuator/configprops/&#123;prefix&#125;&quot;</span><span class="punctuation">,</span></span><br><span class="line">            <span class="attr">&quot;templated&quot;</span><span class="punctuation">:</span> <span class="literal"><span class="keyword">true</span></span></span><br><span class="line">        <span class="punctuation">&#125;</span><span class="punctuation">,</span></span><br><span class="line">        <span class="attr">&quot;env&quot;</span><span class="punctuation">:</span> <span class="punctuation">&#123;</span></span><br><span class="line">            <span class="attr">&quot;href&quot;</span><span class="punctuation">:</span> <span class="string">&quot;http://localhost:81/actuator/env&quot;</span><span class="punctuation">,</span></span><br><span class="line">            <span class="attr">&quot;templated&quot;</span><span class="punctuation">:</span> <span class="literal"><span class="keyword">false</span></span></span><br><span class="line">        <span class="punctuation">&#125;</span><span class="punctuation">,</span></span><br><span class="line">        <span class="attr">&quot;env-toMatch&quot;</span><span class="punctuation">:</span> <span class="punctuation">&#123;</span></span><br><span class="line">            <span class="attr">&quot;href&quot;</span><span class="punctuation">:</span> <span class="string">&quot;http://localhost:81/actuator/env/&#123;toMatch&#125;&quot;</span><span class="punctuation">,</span></span><br><span class="line">            <span class="attr">&quot;templated&quot;</span><span class="punctuation">:</span> <span class="literal"><span class="keyword">true</span></span></span><br><span class="line">        <span class="punctuation">&#125;</span><span class="punctuation">,</span></span><br><span class="line">        <span class="attr">&quot;loggers&quot;</span><span class="punctuation">:</span> <span class="punctuation">&#123;</span></span><br><span class="line">            <span class="attr">&quot;href&quot;</span><span class="punctuation">:</span> <span class="string">&quot;http://localhost:81/actuator/loggers&quot;</span><span class="punctuation">,</span></span><br><span class="line">            <span class="attr">&quot;templated&quot;</span><span class="punctuation">:</span> <span class="literal"><span class="keyword">false</span></span></span><br><span class="line">        <span class="punctuation">&#125;</span><span class="punctuation">,</span></span><br><span class="line">        <span class="attr">&quot;loggers-name&quot;</span><span class="punctuation">:</span> <span class="punctuation">&#123;</span></span><br><span class="line">            <span class="attr">&quot;href&quot;</span><span class="punctuation">:</span> <span class="string">&quot;http://localhost:81/actuator/loggers/&#123;name&#125;&quot;</span><span class="punctuation">,</span></span><br><span class="line">            <span class="attr">&quot;templated&quot;</span><span class="punctuation">:</span> <span class="literal"><span class="keyword">true</span></span></span><br><span class="line">        <span class="punctuation">&#125;</span><span class="punctuation">,</span></span><br><span class="line">        <span class="attr">&quot;heapdump&quot;</span><span class="punctuation">:</span> <span class="punctuation">&#123;</span></span><br><span class="line">            <span class="attr">&quot;href&quot;</span><span class="punctuation">:</span> <span class="string">&quot;http://localhost:81/actuator/heapdump&quot;</span><span class="punctuation">,</span></span><br><span class="line">            <span class="attr">&quot;templated&quot;</span><span class="punctuation">:</span> <span class="literal"><span class="keyword">false</span></span></span><br><span class="line">        <span class="punctuation">&#125;</span><span class="punctuation">,</span></span><br><span class="line">        <span class="attr">&quot;threaddump&quot;</span><span class="punctuation">:</span> <span class="punctuation">&#123;</span></span><br><span class="line">            <span class="attr">&quot;href&quot;</span><span class="punctuation">:</span> <span class="string">&quot;http://localhost:81/actuator/threaddump&quot;</span><span class="punctuation">,</span></span><br><span class="line">            <span class="attr">&quot;templated&quot;</span><span class="punctuation">:</span> <span class="literal"><span class="keyword">false</span></span></span><br><span class="line">        <span class="punctuation">&#125;</span><span class="punctuation">,</span></span><br><span class="line">        <span class="attr">&quot;metrics-requiredMetricName&quot;</span><span class="punctuation">:</span> <span class="punctuation">&#123;</span></span><br><span class="line">            <span class="attr">&quot;href&quot;</span><span class="punctuation">:</span> <span class="string">&quot;http://localhost:81/actuator/metrics/&#123;requiredMetricName&#125;&quot;</span><span class="punctuation">,</span></span><br><span class="line">            <span class="attr">&quot;templated&quot;</span><span class="punctuation">:</span> <span class="literal"><span class="keyword">true</span></span></span><br><span class="line">        <span class="punctuation">&#125;</span><span class="punctuation">,</span></span><br><span class="line">        <span class="attr">&quot;metrics&quot;</span><span class="punctuation">:</span> <span class="punctuation">&#123;</span></span><br><span class="line">            <span class="attr">&quot;href&quot;</span><span class="punctuation">:</span> <span class="string">&quot;http://localhost:81/actuator/metrics&quot;</span><span class="punctuation">,</span></span><br><span class="line">            <span class="attr">&quot;templated&quot;</span><span class="punctuation">:</span> <span class="literal"><span class="keyword">false</span></span></span><br><span class="line">        <span class="punctuation">&#125;</span><span class="punctuation">,</span></span><br><span class="line">        <span class="attr">&quot;scheduledtasks&quot;</span><span class="punctuation">:</span> <span class="punctuation">&#123;</span></span><br><span class="line">            <span class="attr">&quot;href&quot;</span><span class="punctuation">:</span> <span class="string">&quot;http://localhost:81/actuator/scheduledtasks&quot;</span><span class="punctuation">,</span></span><br><span class="line">            <span class="attr">&quot;templated&quot;</span><span class="punctuation">:</span> <span class="literal"><span class="keyword">false</span></span></span><br><span class="line">        <span class="punctuation">&#125;</span><span class="punctuation">,</span></span><br><span class="line">        <span class="attr">&quot;mappings&quot;</span><span class="punctuation">:</span> <span class="punctuation">&#123;</span></span><br><span class="line">            <span class="attr">&quot;href&quot;</span><span class="punctuation">:</span> <span class="string">&quot;http://localhost:81/actuator/mappings&quot;</span><span class="punctuation">,</span></span><br><span class="line">            <span class="attr">&quot;templated&quot;</span><span class="punctuation">:</span> <span class="literal"><span class="keyword">false</span></span></span><br><span class="line">        <span class="punctuation">&#125;</span></span><br><span class="line">    <span class="punctuation">&#125;</span></span><br><span class="line"><span class="punctuation">&#125;</span></span><br></pre></td></tr></table></figure><p>​        其中每一组数据都有一个请求路径，而在这里请求路径中有之前看到过的health，发送此请求又得到了一组信息</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><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><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="punctuation">&#123;</span></span><br><span class="line">    <span class="attr">&quot;status&quot;</span><span class="punctuation">:</span> <span class="string">&quot;UP&quot;</span><span class="punctuation">,</span></span><br><span class="line">    <span class="attr">&quot;components&quot;</span><span class="punctuation">:</span> <span class="punctuation">&#123;</span></span><br><span class="line">        <span class="attr">&quot;diskSpace&quot;</span><span class="punctuation">:</span> <span class="punctuation">&#123;</span></span><br><span class="line">            <span class="attr">&quot;status&quot;</span><span class="punctuation">:</span> <span class="string">&quot;UP&quot;</span><span class="punctuation">,</span></span><br><span class="line">            <span class="attr">&quot;details&quot;</span><span class="punctuation">:</span> <span class="punctuation">&#123;</span></span><br><span class="line">                <span class="attr">&quot;total&quot;</span><span class="punctuation">:</span> <span class="number">297042808832</span><span class="punctuation">,</span></span><br><span class="line">                <span class="attr">&quot;free&quot;</span><span class="punctuation">:</span> <span class="number">72284409856</span><span class="punctuation">,</span></span><br><span class="line">                <span class="attr">&quot;threshold&quot;</span><span class="punctuation">:</span> <span class="number">10485760</span><span class="punctuation">,</span></span><br><span class="line">                <span class="attr">&quot;exists&quot;</span><span class="punctuation">:</span> <span class="literal"><span class="keyword">true</span></span></span><br><span class="line">            <span class="punctuation">&#125;</span></span><br><span class="line">        <span class="punctuation">&#125;</span><span class="punctuation">,</span></span><br><span class="line">        <span class="attr">&quot;ping&quot;</span><span class="punctuation">:</span> <span class="punctuation">&#123;</span></span><br><span class="line">            <span class="attr">&quot;status&quot;</span><span class="punctuation">:</span> <span class="string">&quot;UP&quot;</span></span><br><span class="line">        <span class="punctuation">&#125;</span></span><br><span class="line">    <span class="punctuation">&#125;</span></span><br><span class="line"><span class="punctuation">&#125;</span></span><br></pre></td></tr></table></figure><p>​        当前信息与监控面板中的数据存在着对应关系</p><p><img src="img\image-20220301171025615.png" alt="image-20220301171025615" style="zoom:50%;" /></p><p>​        原来监控中显示的信息实际上是通过发送请求后得到json数据，然后展示出来。按照上述操作，可以发送更多的以/actuator开头的链接地址，获取更多的数据，这些数据汇总到一起组成了监控平台显示的所有数据。</p><p>​        到这里我们得到了一个核心信息，监控平台中显示的信息实际上是通过对被监控的应用发送请求得到的。那这些请求谁开发的呢？打开被监控应用的pom文件，其中导入了springboot admin的对应的client，在这个资源中导入了一个名称叫做actuator的包。被监控的应用之所以可以对外提供上述请求路径，就是因为添加了这个包。</p><p><img src="img\image-20220301171437817.png" alt="image-20220301171437817"></p><p>​        这个actuator是什么呢？这就是本节要讲的核心内容，监控的端点。</p><p>​        Actuator，可以称为端点，描述了一组监控信息，SpringBootAdmin提供了多个内置端点，通过访问端点就可以获取对应的监控信息，也可以根据需要自定义端点信息。通过发送请求路劲<strong>/actuator</strong>可以访问应用所有端点信息，如果端点中还有明细信息可以发送请求<strong>/actuator/端点名称</strong>来获取详细信息。以下列出了所有端点信息说明：</p><div class="table-container"><table><thead><tr><th>ID</th><th>描述</th><th>默认启用</th></tr></thead><tbody><tr><td>auditevents</td><td>暴露当前应用程序的审计事件信息。</td><td>是</td></tr><tr><td>beans</td><td>显示应用程序中所有 Spring bean 的完整列表。</td><td>是</td></tr><tr><td>caches</td><td>暴露可用的缓存。</td><td>是</td></tr><tr><td>conditions</td><td>显示在配置和自动配置类上评估的条件以及它们匹配或不匹配的原因。</td><td>是</td></tr><tr><td>configprops</td><td>显示所有 @ConfigurationProperties 的校对清单。</td><td>是</td></tr><tr><td>env</td><td>暴露 Spring ConfigurableEnvironment 中的属性。</td><td>是</td></tr><tr><td>flyway</td><td>显示已应用的 Flyway 数据库迁移。</td><td>是</td></tr><tr><td>health</td><td>显示应用程序健康信息</td><td>是</td></tr><tr><td>httptrace</td><td>显示 HTTP 追踪信息（默认情况下，最后 100 个  HTTP 请求/响应交换）。</td><td>是</td></tr><tr><td>info</td><td>显示应用程序信息。</td><td>是</td></tr><tr><td>integrationgraph</td><td>显示 Spring Integration 图。</td><td>是</td></tr><tr><td>loggers</td><td>显示和修改应用程序中日志记录器的配置。</td><td>是</td></tr><tr><td>liquibase</td><td>显示已应用的 Liquibase 数据库迁移。</td><td>是</td></tr><tr><td>metrics</td><td>显示当前应用程序的指标度量信息。</td><td>是</td></tr><tr><td>mappings</td><td>显示所有 @RequestMapping 路径的整理清单。</td><td>是</td></tr><tr><td>scheduledtasks</td><td>显示应用程序中的调度任务。</td><td>是</td></tr><tr><td>sessions</td><td>允许从 Spring Session 支持的会话存储中检索和删除用户会话。当使用 Spring Session 的响应式 Web 应用程序支持时不可用。</td><td>是</td></tr><tr><td>shutdown</td><td>正常关闭应用程序。</td><td>否</td></tr><tr><td>threaddump</td><td>执行线程 dump。</td><td>是</td></tr><tr><td>heapdump</td><td>返回一个 hprof 堆 dump 文件。</td><td>是</td></tr><tr><td>jolokia</td><td>通过 HTTP 暴露 JMX bean（当  Jolokia 在 classpath 上时，不适用于 WebFlux）。</td><td>是</td></tr><tr><td>logfile</td><td>返回日志文件的内容（如果已设置 logging.file 或 logging.path 属性）。支持使用 HTTP Range 头来检索部分日志文件的内容。</td><td>是</td></tr><tr><td>prometheus</td><td>以可以由 Prometheus 服务器抓取的格式暴露指标。</td><td>是</td></tr></tbody></table></div><p>​        上述端点每一项代表被监控的指标，如果对外开放则监控平台可以查询到对应的端点信息，如果未开放则无法查询对应的端点信息。通过配置可以设置端点是否对外开放功能。使用enable属性控制端点是否对外开放。其中health端点为默认端点，不能关闭。</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></pre></td><td class="code"><pre><span class="line"><span class="attr">management:</span></span><br><span class="line">  <span class="attr">endpoint:</span></span><br><span class="line">    <span class="attr">health:</span><span class="comment"># 端点名称</span></span><br><span class="line">      <span class="attr">show-details:</span> <span class="string">always</span></span><br><span class="line">    <span class="attr">info:</span><span class="comment"># 端点名称</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>​        为了方便开发者快速配置端点，springboot admin设置了13个较为常用的端点作为默认开放的端点，如果需要控制默认开放的端点的开放状态，可以通过配置设置，如下：</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></pre></td><td class="code"><pre><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">enabled-by-default:</span> <span class="literal">true</span><span class="comment"># 是否开启默认端点，默认值true</span></span><br></pre></td></tr></table></figure><p>​        上述端点开启后，就可以通过端点对应的路径查看对应的信息了。但是此时还不能通过HTTP请求查询此信息，还需要开启通过HTTP请求查询的端点名称，使用“*”可以简化配置成开放所有端点的WEB端HTTP请求权限。</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></pre></td><td class="code"><pre><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">&quot;*&quot;</span></span><br></pre></td></tr></table></figure><p>​        整体上来说，对于端点的配置有两组信息，一组是endpoints开头的，对所有端点进行配置，一组是endpoint开头的，对具体端点进行配置。</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></pre></td><td class="code"><pre><span class="line"><span class="attr">management:</span></span><br><span class="line">  <span class="attr">endpoint:</span><span class="comment"># 具体端点的配置</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">always</span></span><br><span class="line">    <span class="attr">info:</span></span><br><span class="line">      <span class="attr">enabled:</span> <span class="literal">true</span></span><br><span class="line">  <span class="attr">endpoints:</span><span class="comment"># 全部端点的配置</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">&quot;*&quot;</span></span><br><span class="line">    <span class="attr">enabled-by-default:</span> <span class="literal">true</span></span><br></pre></td></tr></table></figure><p><strong>总结</strong></p><ol><li><p>被监控客户端通过添加actuator的坐标可以对外提供被访问的端点功能</p></li><li><p>端点功能的开放与关闭可以通过配置进行控制</p></li><li><p>web端默认无法获取所有端点信息，通过配置开放端点功能</p></li></ol><h3 id="KF-6-4-自定义监控指标"><a href="#KF-6-4-自定义监控指标" class="headerlink" title="KF-6-4.自定义监控指标"></a>KF-6-4.自定义监控指标</h3><p>​        端点描述了被监控的信息，除了系统默认的指标，还可以自行添加显示的指标，下面就通过3种不同的端点的指标自定义方式来学习端点信息的二次开发。</p><p><strong>INFO端点</strong></p><p>​        info端点描述了当前应用的基本信息，可以通过两种形式快速配置info端点的信息</p><ul><li><p>配置形式</p><p>在yml文件中通过设置info节点的信息就可以快速配置端点信息</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></pre></td><td class="code"><pre><span class="line"><span class="attr">info:</span></span><br><span class="line">  <span class="attr">appName:</span> <span class="string">@project.artifactId@</span></span><br><span class="line">  <span class="attr">version:</span> <span class="string">@project.version@</span></span><br><span class="line">  <span class="attr">company:</span> <span class="string">传智教育</span></span><br><span class="line">  <span class="attr">author:</span> <span class="string">itheima</span></span><br></pre></td></tr></table></figure><p>配置完毕后，对应信息显示在监控平台上</p><p><img src="img\image-20220301174133248.png" alt="image-20220301174133248" style="zoom:50%;" /></p><p>也可以通过请求端点信息路径获取对应json信息</p><p><img src="img\image-20220301174241310.png" alt="image-20220301174241310" style="zoom:50%;" /></p></li><li><p>编程形式</p><p>通过配置的形式只能添加固定的数据，如果需要动态数据还可以通过配置bean的方式为info端点添加信息，此信息与配置信息共存</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="keyword">public</span> <span class="keyword">class</span> <span class="title class_">InfoConfig</span> <span class="keyword">implements</span> <span class="title class_">InfoContributor</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_">contribute</span><span class="params">(Info.Builder builder)</span> &#123;</span><br><span class="line">        builder.withDetail(<span class="string">&quot;runTime&quot;</span>,System.currentTimeMillis());<span class="comment">//添加单个信息</span></span><br><span class="line">        <span class="type">Map</span> <span class="variable">infoMap</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">HashMap</span>();</span><br><span class="line">        infoMap.put(<span class="string">&quot;buildTime&quot;</span>,<span class="string">&quot;2006&quot;</span>);</span><br><span class="line">        builder.withDetails(infoMap);<span class="comment">//添加一组信息</span></span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></li></ul><p><strong>Health端点</strong></p><p>​        health端点描述当前应用的运行健康指标，即应用的运行是否成功。通过编程的形式可以扩展指标信息。</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="meta">@Component</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">HealthConfig</span> <span class="keyword">extends</span> <span class="title class_">AbstractHealthIndicator</span> &#123;</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_">doHealthCheck</span><span class="params">(Health.Builder builder)</span> <span class="keyword">throws</span> Exception &#123;</span><br><span class="line">        <span class="type">boolean</span> <span class="variable">condition</span> <span class="operator">=</span> <span class="literal">true</span>;</span><br><span class="line">        <span class="keyword">if</span>(condition) &#123;</span><br><span class="line">            builder.status(Status.UP);<span class="comment">//设置运行状态为启动状态</span></span><br><span class="line">            builder.withDetail(<span class="string">&quot;runTime&quot;</span>, System.currentTimeMillis());</span><br><span class="line">            <span class="type">Map</span> <span class="variable">infoMap</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">HashMap</span>();</span><br><span class="line">            infoMap.put(<span class="string">&quot;buildTime&quot;</span>, <span class="string">&quot;2006&quot;</span>);</span><br><span class="line">            builder.withDetails(infoMap);</span><br><span class="line">        &#125;<span class="keyword">else</span>&#123;</span><br><span class="line">            builder.status(Status.OUT_OF_SERVICE);<span class="comment">//设置运行状态为不在服务状态</span></span><br><span class="line">            builder.withDetail(<span class="string">&quot;上线了吗？&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><p>​        当任意一个组件状态不为UP时，整体应用对外服务状态为非UP状态。</p><p><img src="img\image-20220301174751845.png" alt="image-20220301174751845" style="zoom:50%;" /></p><p><strong>Metrics端点</strong></p><p>​        metrics端点描述了性能指标，除了系统自带的监控性能指标，还可以自定义性能指标。</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></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_">BookServiceImpl</span> <span class="keyword">extends</span> <span class="title class_">ServiceImpl</span>&lt;BookDao, Book&gt; <span class="keyword">implements</span> <span class="title class_">IBookService</span> &#123;</span><br><span class="line">    <span class="meta">@Autowired</span></span><br><span class="line">    <span class="keyword">private</span> BookDao bookDao;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">private</span> Counter counter;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">public</span> <span class="title function_">BookServiceImpl</span><span class="params">(MeterRegistry meterRegistry)</span>&#123;</span><br><span class="line">        counter = meterRegistry.counter(<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">public</span> <span class="type">boolean</span> <span class="title function_">delete</span><span class="params">(Integer id)</span> &#123;</span><br><span class="line">        <span class="comment">//每次执行删除业务等同于执行了付费业务</span></span><br><span class="line">        counter.increment();</span><br><span class="line">        <span class="keyword">return</span> bookDao.deleteById(id) &gt; <span class="number">0</span>;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>​        在性能指标中就出现了自定义的性能指标监控项</p><p><img src="img\image-20220301175101812.png" alt="image-20220301175101812" style="zoom:50%;" /></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></pre></td><td class="code"><pre><span class="line"><span class="meta">@Component</span></span><br><span class="line"><span class="meta">@Endpoint(id=&quot;pay&quot;,enableByDefault = true)</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">PayEndpoint</span> &#123;</span><br><span class="line">    <span class="meta">@ReadOperation</span></span><br><span class="line">    <span class="keyword">public</span> Object <span class="title function_">getPay</span><span class="params">()</span>&#123;</span><br><span class="line">        <span class="type">Map</span> <span class="variable">payMap</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">HashMap</span>();</span><br><span class="line">        payMap.put(<span class="string">&quot;level 1&quot;</span>,<span class="string">&quot;300&quot;</span>);</span><br><span class="line">        payMap.put(<span class="string">&quot;level 2&quot;</span>,<span class="string">&quot;291&quot;</span>);</span><br><span class="line">        payMap.put(<span class="string">&quot;level 3&quot;</span>,<span class="string">&quot;666&quot;</span>);</span><br><span class="line">        <span class="keyword">return</span> payMap;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>​        由于此端点数据spirng boot admin无法预知该如何展示，所以通过界面无法看到此数据，通过HTTP请求路径可以获取到当前端点的信息，但是需要先开启当前端点对外功能，或者设置当前端点为默认开发的端点。</p><p><img src="img\image-20220301175355482.png" alt="image-20220301175355482" style="zoom:50%;" /></p><p><strong>总结</strong></p><ol><li>端点的指标可以自定义，但是每种不同的指标根据其功能不同，自定义方式不同</li><li>info端点通过配置和编程的方式都可以添加端点指标</li><li>health端点通过编程的方式添加端点指标，需要注意要为对应指标添加启动状态的逻辑设定</li><li>metrics指标通过在业务中添加监控操作设置指标</li><li>可以自定义端点添加更多的指标</li></ol><h2 id="开发实用篇完结"><a href="#开发实用篇完结" class="headerlink" title="开发实用篇完结"></a>开发实用篇完结</h2><p>​        开发实用篇到这里就暂时完结了，在开发实用篇中我们讲解了大量的第三方技术的整合方案，选择的方案都是市面上比较流行的常用方案，还有一些国内流行度较低的方案目前还没讲，留到番外篇中慢慢讲吧。</p><p>​        整体开发实用篇中讲解的内容可以分为两大类知识：实用性知识与经验性知识。</p><p>​        实用性知识就是新知识了，springboot整合各种技术，每种技术整合中都有一些特殊操作，整体来说其实就是三句话。加坐标做配置调接口。经验性知识是对前面两篇中出现的一些知识的补充，在学习基础篇时如果将精力放在这些东西上就有点学偏了，容易钻牛角尖，放到实用开发篇中结合实际开发说一些</p>]]></content>
    
    
    <summary type="html">SpringBoot2黑马课程介绍</summary>
    
    
    
    <category term="Springboot" scheme="https://silvan.chat/categories/Springboot/"/>
    
    
    <category term="Springboot" scheme="https://silvan.chat/tags/Springboot/"/>
    
  </entry>
  
  <entry>
    <title>Spring(3)</title>
    <link href="https://silvan.chat/2025/10/03/Spring-Spring-3/"/>
    <id>https://silvan.chat/2025/10/03/Spring-Spring-3/</id>
    <published>2025-10-03T06:58:27.256Z</published>
    <updated>2025-10-05T07:45:00.383Z</updated>
    
    <content type="html"><![CDATA[<h1 id="Spring-Ioc注解式开发"><a href="#Spring-Ioc注解式开发" class="headerlink" title="Spring Ioc注解式开发"></a>Spring Ioc注解式开发</h1><h2 id="回顾注解"><a href="#回顾注解" class="headerlink" title="回顾注解"></a>回顾注解</h2><div class="note danger no-icon flat"><p>注解的存在主要是为了简化XML配置，方便开发。<strong>Spring6倡导全注解开发</strong></p></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></pre></td><td class="code"><pre><span class="line"><span class="meta">@Target(value = &#123;ElementType.TYPE&#125;)</span></span><br><span class="line"><span class="meta">@Retention(value = RetentionPolicy.RUNTIME)</span></span><br><span class="line"><span class="keyword">public</span> <span class="meta">@interface</span> Component &#123;</span><br><span class="line">    String <span class="title function_">value</span><span class="params">()</span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><ul><li>以上就是自定义了一个简单注解：<code>Component</code></li><li>该注解上修饰的注解包括：<code>Target注解</code>和<code>Retention注解</code>，这两个注解又称为<code>元注解</code>。<ul><li>Target注解来设置Component注解可以出现的位置，以上设置表示Component注解只能修饰类和接口上</li><li>Retention注解来设置Component注解的保留位置，以上设置表示Component注解保留在运行时，<br>可以通过反射读取到该注解。<ul><li>注解的属性怎么定义？</li></ul></li></ul></li><li>value是Component注解中的一个属性，该属性类型为String，属性名是value。<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></pre></td><td class="code"><pre><span class="line"><span class="meta">@Component(value = &quot;userBean&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">&#125;</span><br></pre></td></tr></table></figure>注解用法简单，直接放在修饰的符号上<br>userBean为什么使用双引号括起来，因为value属性是String类型，字符串。<br>另外如果属性名是value，则在使用的时候可以省略属性名，例如：<figure class="highlight java"><table><tr><td class="gutter"><pre><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">//@Component(value = &quot;userBean&quot;)</span></span><br><span class="line"><span class="meta">@Component(&quot;userBean&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">&#125;</span><br></pre></td></tr></table></figure><h3 id="怎么通过反射读取注解？"><a href="#怎么通过反射读取注解？" class="headerlink" title="怎么通过反射读取注解？"></a>怎么通过反射读取注解？</h3></li><li>写一段程序完成该功能：当Bean类上Component注解时，实例化Bean对象，如果没有则不实例化对象</li></ul><div class="tabs" id="通过反射读取注解"><ul class="nav-tabs"><li class="tab active"><button type="button" data-href="#通过反射读取注解-1">有注解的Bean</button></li><li class="tab"><button type="button" data-href="#通过反射读取注解-2">无注解的Bean</button></li></ul><div class="tab-contents"><div class="tab-item-content active" id="通过反射读取注解-1"><figure class="highlight java"><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="meta">@Component(&quot;userBean&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">&#125;</span><br></pre></td></tr></table></figure><button type="button" class="tab-to-top" aria-label="scroll to top"><i class="fas fa-arrow-up"></i></button></div><div class="tab-item-content" id="通过反射读取注解-2"><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="keyword">public</span> <span class="keyword">class</span> <span class="title class_">Vip</span> &#123;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><button type="button" class="tab-to-top" aria-label="scroll to top"><i class="fas fa-arrow-up"></i></button></div></div></div><ul><li>假设当前只知道包名，包下有多少个Bean类，哪些Bean有注解，哪些Bean没有注解都不知道。<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><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_">Test</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">// 存放Bean的Map集合。key存储beanId。value存储Bean。</span></span><br><span class="line">        Map&lt;String,Object&gt; beanMap = <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="type">String</span> <span class="variable">packageName</span> <span class="operator">=</span> <span class="string">&quot;com.powernode.bean&quot;</span>;</span><br><span class="line">        <span class="type">String</span> <span class="variable">path</span> <span class="operator">=</span> packageName.replaceAll(<span class="string">&quot;\\.&quot;</span>, <span class="string">&quot;/&quot;</span>);</span><br><span class="line">        <span class="type">URL</span> <span class="variable">url</span> <span class="operator">=</span> ClassLoader.getSystemClassLoader().getResource(path);</span><br><span class="line">        <span class="type">File</span> <span class="variable">file</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">File</span>(url.getPath());</span><br><span class="line">        File[] files = file.listFiles();</span><br><span class="line">        Arrays.stream(files).forEach(f -&gt; &#123;</span><br><span class="line">            <span class="type">String</span> <span class="variable">className</span> <span class="operator">=</span> packageName + <span class="string">&quot;.&quot;</span> + f.getName().split(<span class="string">&quot;\\.&quot;</span>)[<span class="number">0</span>];</span><br><span class="line">            <span class="keyword">try</span> &#123;</span><br><span class="line">                Class&lt;?&gt; clazz = Class.forName(className);</span><br><span class="line">                <span class="keyword">if</span> (clazz.isAnnotationPresent(Component.class)) &#123;</span><br><span class="line">                    <span class="type">Component</span> <span class="variable">component</span> <span class="operator">=</span> clazz.getAnnotation(Component.class);</span><br><span class="line">                    <span class="type">String</span> <span class="variable">beanId</span> <span class="operator">=</span> component.value();</span><br><span class="line">                    <span class="type">Object</span> <span class="variable">bean</span> <span class="operator">=</span> clazz.newInstance();</span><br><span class="line">                    beanMap.put(beanId, bean);</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">                e.printStackTrace();</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;);</span><br><span class="line"></span><br><span class="line">        System.out.println(beanMap);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h2 id="声明Bean的注解"><a href="#声明Bean的注解" class="headerlink" title="声明Bean的注解"></a>声明Bean的注解</h2>负责声明Bean的注解，常见的包括四个：</li></ul><ul><li>@Component</li><li>@Controller</li><li>@Service</li><li>@Repository</li></ul><div class="tabs" id="声明bean的注解"><ul class="nav-tabs"><li class="tab active"><button type="button" data-href="#声明bean的注解-1">Component</button></li><li class="tab"><button type="button" data-href="#声明bean的注解-2">Contorller</button></li><li class="tab"><button type="button" data-href="#声明bean的注解-3">Service</button></li><li class="tab"><button type="button" data-href="#声明bean的注解-4">Repository</button></li></ul><div class="tab-contents"><div class="tab-item-content active" id="声明bean的注解-1"><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><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">package</span> com.powernode.annotation;</span><br><span class="line"></span><br><span class="line"><span class="keyword">import</span> java.lang.annotation.ElementType;</span><br><span class="line"><span class="keyword">import</span> java.lang.annotation.Retention;</span><br><span class="line"><span class="keyword">import</span> java.lang.annotation.RetentionPolicy;</span><br><span class="line"><span class="keyword">import</span> java.lang.annotation.Target;</span><br><span class="line"></span><br><span class="line"><span class="meta">@Target(value = &#123;ElementType.TYPE&#125;)</span></span><br><span class="line"><span class="meta">@Retention(value = RetentionPolicy.RUNTIME)</span></span><br><span class="line"><span class="keyword">public</span> <span class="meta">@interface</span> Component &#123;</span><br><span class="line">    String <span class="title function_">value</span><span class="params">()</span>;</span><br><span class="line">&#125;</span><br><span class="line"></span><br></pre></td></tr></table></figure><button type="button" class="tab-to-top" aria-label="scroll to top"><i class="fas fa-arrow-up"></i></button></div><div class="tab-item-content" id="声明bean的注解-2"><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><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">package</span> org.springframework.stereotype;</span><br><span class="line"></span><br><span class="line"><span class="keyword">import</span> java.lang.annotation.Documented;</span><br><span class="line"><span class="keyword">import</span> java.lang.annotation.ElementType;</span><br><span class="line"><span class="keyword">import</span> java.lang.annotation.Retention;</span><br><span class="line"><span class="keyword">import</span> java.lang.annotation.RetentionPolicy;</span><br><span class="line"><span class="keyword">import</span> java.lang.annotation.Target;</span><br><span class="line"><span class="keyword">import</span> org.springframework.core.annotation.AliasFor;</span><br><span class="line"></span><br><span class="line"><span class="meta">@Target(&#123;ElementType.TYPE&#125;)</span></span><br><span class="line"><span class="meta">@Retention(RetentionPolicy.RUNTIME)</span></span><br><span class="line"><span class="meta">@Documented</span></span><br><span class="line"><span class="meta">@Component</span></span><br><span class="line"><span class="keyword">public</span> <span class="meta">@interface</span> Controller &#123;</span><br><span class="line">    <span class="meta">@AliasFor(</span></span><br><span class="line"><span class="meta">        annotation = Component.class</span></span><br><span class="line"><span class="meta">    )</span></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><br><span class="line">&#125;</span><br><span class="line"></span><br></pre></td></tr></table></figure><button type="button" class="tab-to-top" aria-label="scroll to top"><i class="fas fa-arrow-up"></i></button></div><div class="tab-item-content" id="声明bean的注解-3"><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><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">package</span> org.springframework.stereotype;</span><br><span class="line"></span><br><span class="line"><span class="keyword">import</span> java.lang.annotation.Documented;</span><br><span class="line"><span class="keyword">import</span> java.lang.annotation.ElementType;</span><br><span class="line"><span class="keyword">import</span> java.lang.annotation.Retention;</span><br><span class="line"><span class="keyword">import</span> java.lang.annotation.RetentionPolicy;</span><br><span class="line"><span class="keyword">import</span> java.lang.annotation.Target;</span><br><span class="line"><span class="keyword">import</span> org.springframework.core.annotation.AliasFor;</span><br><span class="line"></span><br><span class="line"><span class="meta">@Target(&#123;ElementType.TYPE&#125;)</span></span><br><span class="line"><span class="meta">@Retention(RetentionPolicy.RUNTIME)</span></span><br><span class="line"><span class="meta">@Documented</span></span><br><span class="line"><span class="meta">@Component</span></span><br><span class="line"><span class="keyword">public</span> <span class="meta">@interface</span> Service &#123;</span><br><span class="line">    <span class="meta">@AliasFor(</span></span><br><span class="line"><span class="meta">        annotation = Component.class</span></span><br><span class="line"><span class="meta">    )</span></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><br><span class="line">&#125;</span><br><span class="line"></span><br></pre></td></tr></table></figure><button type="button" class="tab-to-top" aria-label="scroll to top"><i class="fas fa-arrow-up"></i></button></div><div class="tab-item-content" id="声明bean的注解-4"><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><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">package</span> org.springframework.stereotype;</span><br><span class="line"></span><br><span class="line"><span class="keyword">import</span> java.lang.annotation.Documented;</span><br><span class="line"><span class="keyword">import</span> java.lang.annotation.ElementType;</span><br><span class="line"><span class="keyword">import</span> java.lang.annotation.Retention;</span><br><span class="line"><span class="keyword">import</span> java.lang.annotation.RetentionPolicy;</span><br><span class="line"><span class="keyword">import</span> java.lang.annotation.Target;</span><br><span class="line"><span class="keyword">import</span> org.springframework.core.annotation.AliasFor;</span><br><span class="line"></span><br><span class="line"><span class="meta">@Target(&#123;ElementType.TYPE&#125;)</span></span><br><span class="line"><span class="meta">@Retention(RetentionPolicy.RUNTIME)</span></span><br><span class="line"><span class="meta">@Documented</span></span><br><span class="line"><span class="meta">@Component</span></span><br><span class="line"><span class="keyword">public</span> <span class="meta">@interface</span> Repository &#123;</span><br><span class="line">    <span class="meta">@AliasFor(</span></span><br><span class="line"><span class="meta">        annotation = Component.class</span></span><br><span class="line"><span class="meta">    )</span></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><br><span class="line">&#125;</span><br><span class="line"></span><br></pre></td></tr></table></figure><button type="button" class="tab-to-top" aria-label="scroll to top"><i class="fas fa-arrow-up"></i></button></div></div></div><div class="note danger no-icon flat"><ul><li>从源码可以看到，<code>@Controller</code>、<code>@Service</code>、<code>@Repository</code>这三个注解都是<code>@Component</code>注解的别名，也就是这几个注解功能一样，用哪个都可以。</li><li>为了增强程序可读性，建议：<ul><li>控制器上使用<code>@Controller</code>注解</li><li>服务层上使用<code>@Service</code>注解</li><li>数据访问层上使用<code>@Repository</code>注解</li></ul></li><li>这几个注解都只有一个value属性，该属性值就是Bean的id,也就是Bean的名字。</li></ul></div><h2 id="Spring注解的使用"><a href="#Spring注解的使用" class="headerlink" title="Spring注解的使用"></a>Spring注解的使用</h2><ul><li>如何使用前面的这几个注解呢？<ul><li>第一步：加入aop的依赖</li><li>第二步：在配置文件中添加context命名空间</li><li>第三步：在配置文件中指定扫描的包</li><li>第四步：在Bean类上使用注解</li></ul></li></ul><p><strong>第一步：加入aop的依赖</strong></p><p>当加入spring-context依赖之后，会关联加入aop的依赖。所以这一步不用做。</p><div class="img-wrap"><div class="img-bg"><img class="img" src="https://raw.githubusercontent.com/silvan2077/markdown_pic/main/Picgo/20251003200001.png"/></div></div><p><strong>第二步：在配置文件中添加context命名空间</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></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">beans</span> <span class="attr">xmlns</span>=<span class="string">&quot;http://www.springframework.org/schema/beans&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">xmlns:context</span>=<span class="string">&quot;http://www.springframework.org/schema/context&quot;</span></span></span><br><span class="line"><span class="tag">       <span class="attr">xsi:schemaLocation</span>=<span class="string">&quot;http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd</span></span></span><br><span class="line"><span class="string"><span class="tag">                           http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd&quot;</span>&gt;</span></span><br><span class="line"></span><br><span class="line"><span class="tag">&lt;/<span class="name">beans</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></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">beans</span> <span class="attr">xmlns</span>=<span class="string">&quot;http://www.springframework.org/schema/beans&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">xmlns:context</span>=<span class="string">&quot;http://www.springframework.org/schema/context&quot;</span></span></span><br><span class="line"><span class="tag">       <span class="attr">xsi:schemaLocation</span>=<span class="string">&quot;http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd</span></span></span><br><span class="line"><span class="string"><span class="tag">                           http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd&quot;</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">context:component-scan</span> <span class="attr">base-package</span>=<span class="string">&quot;com.powernode.spring6.bean&quot;</span>/&gt;</span></span><br><span class="line"><span class="tag">&lt;/<span class="name">beans</span>&gt;</span></span><br></pre></td></tr></table></figure><p><strong>第四步：在Bean类上使用注解</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></pre></td><td class="code"><pre><span class="line"><span class="meta">@Component(value = &quot;userBean&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">&#125;</span><br></pre></td></tr></table></figure><div class="tabs" id="annotationtest"><ul class="nav-tabs"><li class="tab active"><button type="button" data-href="#annotationtest-1">测试程序</button></li><li class="tab"><button type="button" data-href="#annotationtest-2">执行结果</button></li></ul><div class="tab-contents"><div class="tab-item-content active" id="annotationtest-1"><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><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_">AnnotationTest</span> &#123;</span><br><span class="line">    <span class="meta">@Test</span></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">testBean</span><span class="params">()</span>&#123;</span><br><span class="line">        <span class="type">ApplicationContext</span> <span class="variable">applicationContext</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">ClassPathXmlApplicationContext</span>(<span class="string">&quot;spring.xml&quot;</span>);</span><br><span class="line">        <span class="type">User</span> <span class="variable">userBean</span> <span class="operator">=</span> applicationContext.getBean(<span class="string">&quot;userBean&quot;</span>, User.class);</span><br><span class="line">        System.out.println(userBean);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><button type="button" class="tab-to-top" aria-label="scroll to top"><i class="fas fa-arrow-up"></i></button></div><div class="tab-item-content" id="annotationtest-2"><p>执行结果：</p><div class="img-wrap"><div class="img-bg"><img class="img" src="https://raw.githubusercontent.com/silvan2077/markdown_pic/main/Picgo/20251003200138.png"/></div></div><button type="button" class="tab-to-top" aria-label="scroll to top"><i class="fas fa-arrow-up"></i></button></div></div></div><p><strong>如果注解的属性名是value，那么value是可以省略的。</strong><br><div class="tabs" id="示例"><ul class="nav-tabs"><li class="tab active"><button type="button" data-href="#示例-1">示例1</button></li><li class="tab"><button type="button" data-href="#示例-2">示例2</button></li></ul><div class="tab-contents"><div class="tab-item-content active" id="示例-1"><figure class="highlight java"><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="meta">@Component(&quot;vipBean&quot;)</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">Vip</span> &#123;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><button type="button" class="tab-to-top" aria-label="scroll to top"><i class="fas fa-arrow-up"></i></button></div><div class="tab-item-content" id="示例-2"><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><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_">AnnotationTest</span> &#123;</span><br><span class="line">    <span class="meta">@Test</span></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">testBean</span><span class="params">()</span>&#123;</span><br><span class="line">        <span class="type">ApplicationContext</span> <span class="variable">applicationContext</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">ClassPathXmlApplicationContext</span>(<span class="string">&quot;spring.xml&quot;</span>);</span><br><span class="line">        <span class="type">Vip</span> <span class="variable">vipBean</span> <span class="operator">=</span> applicationContext.getBean(<span class="string">&quot;vipBean&quot;</span>, Vip.class);</span><br><span class="line">        System.out.println(vipBean);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><button type="button" class="tab-to-top" aria-label="scroll to top"><i class="fas fa-arrow-up"></i></button></div></div></div></p><div class="note danger no-icon flat"><p><strong>如果把value属性彻底去掉，spring会被Bean自动取名吗？会的。并且默认名字的规律是：Bean类名首字母小写。</strong></p></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></pre></td><td class="code"><pre><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_">BankDao</span> &#123;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>也就是说，这个BankDao的bean的名字为：bankDao<br>测试一下<br><div class="tabs" id="默认bean的名字"><ul class="nav-tabs"><li class="tab active"><button type="button" data-href="#默认bean的名字-1">测试程序</button></li><li class="tab"><button type="button" data-href="#默认bean的名字-2">执行结果</button></li></ul><div class="tab-contents"><div class="tab-item-content active" id="默认bean的名字-1"><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><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_">AnnotationTest</span> &#123;</span><br><span class="line">    <span class="meta">@Test</span></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">testBean</span><span class="params">()</span>&#123;</span><br><span class="line">        <span class="type">ApplicationContext</span> <span class="variable">applicationContext</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">ClassPathXmlApplicationContext</span>(<span class="string">&quot;spring.xml&quot;</span>);</span><br><span class="line">        <span class="type">BankDao</span> <span class="variable">bankDao</span> <span class="operator">=</span> applicationContext.getBean(<span class="string">&quot;bankDao&quot;</span>, BankDao.class);</span><br><span class="line">        System.out.println(bankDao);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><button type="button" class="tab-to-top" aria-label="scroll to top"><i class="fas fa-arrow-up"></i></button></div><div class="tab-item-content" id="默认bean的名字-2"><div class="img-wrap"><div class="img-bg"><img class="img" src="https://raw.githubusercontent.com/silvan2077/markdown_pic/main/Picgo/20251003200244.png"/></div></div><button type="button" class="tab-to-top" aria-label="scroll to top"><i class="fas fa-arrow-up"></i></button></div></div></div></p><p><strong>如果是多个包怎么办？有两种解决方案：</strong></p><ul><li><strong>第一种：在配置文件中指定多个包，用逗号隔开。</strong></li><li><strong>第二种：指定多个包的共同父包。</strong></li></ul><p>先来测试一下逗号（英文）的方式：</p><div class="tabs" id="多个包"><ul class="nav-tabs"><li class="tab active"><button type="button" data-href="#多个包-1">定义新的Bean类</button></li><li class="tab"><button type="button" data-href="#多个包-2">修改配置文件</button></li><li class="tab"><button type="button" data-href="#多个包-3">测试程序</button></li><li class="tab"><button type="button" data-href="#多个包-4">执行结果</button></li></ul><div class="tab-contents"><div class="tab-item-content active" id="多个包-1"><p>创建一个新的包：bean2，定义一个Bean类。<br><figure class="highlight java"><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="meta">@Service</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">Order</span> &#123;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></p><button type="button" class="tab-to-top" aria-label="scroll to top"><i class="fas fa-arrow-up"></i></button></div><div class="tab-item-content" id="多个包-2"><p>配置文件修改：</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></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">beans</span> <span class="attr">xmlns</span>=<span class="string">&quot;http://www.springframework.org/schema/beans&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">xmlns:context</span>=<span class="string">&quot;http://www.springframework.org/schema/context&quot;</span></span></span><br><span class="line"><span class="tag">       <span class="attr">xsi:schemaLocation</span>=<span class="string">&quot;http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd</span></span></span><br><span class="line"><span class="string"><span class="tag">                           http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd&quot;</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">context:component-scan</span> <span class="attr">base-package</span>=<span class="string">&quot;com.powernode.spring6.bean,com.powernode.spring6.bean2&quot;</span>/&gt;</span></span><br><span class="line"><span class="tag">&lt;/<span class="name">beans</span>&gt;</span></span><br></pre></td></tr></table></figure><button type="button" class="tab-to-top" aria-label="scroll to top"><i class="fas fa-arrow-up"></i></button></div><div class="tab-item-content" id="多个包-3"><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><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">package</span> com.powernode.spring6.test;</span><br><span class="line"></span><br><span class="line"><span class="keyword">import</span> com.powernode.spring6.bean.BankDao;</span><br><span class="line"><span class="keyword">import</span> com.powernode.spring6.bean2.Order;</span><br><span class="line"><span class="keyword">import</span> org.junit.Test;</span><br><span class="line"><span class="keyword">import</span> org.springframework.context.ApplicationContext;</span><br><span class="line"><span class="keyword">import</span> org.springframework.context.support.ClassPathXmlApplicationContext;</span><br><span class="line"></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">AnnotationTest</span> &#123;</span><br><span class="line">    <span class="meta">@Test</span></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">testBean</span><span class="params">()</span>&#123;</span><br><span class="line">        <span class="type">ApplicationContext</span> <span class="variable">applicationContext</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">ClassPathXmlApplicationContext</span>(<span class="string">&quot;spring.xml&quot;</span>);</span><br><span class="line">        <span class="type">BankDao</span> <span class="variable">bankDao</span> <span class="operator">=</span> applicationContext.getBean(<span class="string">&quot;bankDao&quot;</span>, BankDao.class);</span><br><span class="line">        System.out.println(bankDao);</span><br><span class="line">        <span class="type">Order</span> <span class="variable">order</span> <span class="operator">=</span> applicationContext.getBean(<span class="string">&quot;order&quot;</span>, Order.class);</span><br><span class="line">        System.out.println(order);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><button type="button" class="tab-to-top" aria-label="scroll to top"><i class="fas fa-arrow-up"></i></button></div><div class="tab-item-content" id="多个包-4"><p>执行结果：</p><div class="img-wrap"><div class="img-bg"><img class="img" src="https://raw.githubusercontent.com/silvan2077/markdown_pic/main/Picgo/20251003200334.png"/></div></div><button type="button" class="tab-to-top" aria-label="scroll to top"><i class="fas fa-arrow-up"></i></button></div></div></div><p><strong>指定共同的父包：</strong><br><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></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">beans</span> <span class="attr">xmlns</span>=<span class="string">&quot;http://www.springframework.org/schema/beans&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">xmlns:context</span>=<span class="string">&quot;http://www.springframework.org/schema/context&quot;</span></span></span><br><span class="line"><span class="tag">       <span class="attr">xsi:schemaLocation</span>=<span class="string">&quot;http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd</span></span></span><br><span class="line"><span class="string"><span class="tag">                           http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd&quot;</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">context:component-scan</span> <span class="attr">base-package</span>=<span class="string">&quot;com.powernode.spring6&quot;</span>/&gt;</span></span><br><span class="line"><span class="tag">&lt;/<span class="name">beans</span>&gt;</span></span><br></pre></td></tr></table></figure></p><p>执行测试程序：<br><div class="img-wrap"><div class="img-bg"><img class="img" src="https://raw.githubusercontent.com/silvan2077/markdown_pic/main/Picgo/20251003200411.png"/></div></div></p><h2 id="选择性实例化Bean"><a href="#选择性实例化Bean" class="headerlink" title="选择性实例化Bean"></a>选择性实例化Bean</h2><div class="note info no-icon flat"><p>假设在某个包下有许多Bean，有的Bean上标注了Component，有的标注了Controller，有的标注了Service，有的标注了Repository，现在由于某种特殊业务的需要，只允许其中所有的Controller参与Bean管理，其他的都不实例化。这应该怎么办呢？</p></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></pre></td><td class="code"><pre><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">public</span> <span class="title function_">A</span><span class="params">()</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><span class="line"></span><br><span class="line"><span class="meta">@Controller</span></span><br><span class="line"><span class="keyword">class</span> <span class="title class_">B</span> &#123;</span><br><span class="line">    <span class="keyword">public</span> <span class="title function_">B</span><span class="params">()</span> &#123;</span><br><span class="line">        System.out.println(<span class="string">&quot;B的无参数构造方法执行&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">@Service</span></span><br><span class="line"><span class="keyword">class</span> <span class="title class_">C</span> &#123;</span><br><span class="line">    <span class="keyword">public</span> <span class="title function_">C</span><span class="params">()</span> &#123;</span><br><span class="line">        System.out.println(<span class="string">&quot;C的无参数构造方法执行&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">@Repository</span></span><br><span class="line"><span class="keyword">class</span> <span class="title class_">D</span> &#123;</span><br><span class="line">    <span class="keyword">public</span> <span class="title function_">D</span><span class="params">()</span> &#123;</span><br><span class="line">        System.out.println(<span class="string">&quot;D的无参数构造方法执行&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">@Controller</span></span><br><span class="line"><span class="keyword">class</span> <span class="title class_">E</span> &#123;</span><br><span class="line">    <span class="keyword">public</span> <span class="title function_">E</span><span class="params">()</span> &#123;</span><br><span class="line">        System.out.println(<span class="string">&quot;E的无参数构造方法执行&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">@Controller</span></span><br><span class="line"><span class="keyword">class</span> <span class="title class_">F</span> &#123;</span><br><span class="line">    <span class="keyword">public</span> <span class="title function_">F</span><span class="params">()</span> &#123;</span><br><span class="line">        System.out.println(<span class="string">&quot;F的无参数构造方法执行&quot;</span>);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><ul><li>想仅仅实例化Conroller层，应修改配置文件<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="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">beans</span> <span class="attr">xmlns</span>=<span class="string">&quot;http://www.springframework.org/schema/beans&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">xmlns:context</span>=<span class="string">&quot;http://www.springframework.org/schema/context&quot;</span></span></span><br><span class="line"><span class="tag">       <span class="attr">xsi:schemaLocation</span>=<span class="string">&quot;http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd</span></span></span><br><span class="line"><span class="string"><span class="tag">                           http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd&quot;</span>&gt;</span></span><br><span class="line"></span><br><span class="line">    <span class="tag">&lt;<span class="name">context:component-scan</span> <span class="attr">base-package</span>=<span class="string">&quot;com.powernode.spring6.bean3&quot;</span> <span class="attr">use-default-filters</span>=<span class="string">&quot;false&quot;</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">context:include-filter</span> <span class="attr">type</span>=<span class="string">&quot;annotation&quot;</span> <span class="attr">expression</span>=<span class="string">&quot;org.springframework.stereotype.Controller&quot;</span>/&gt;</span></span><br><span class="line">    <span class="tag">&lt;/<span class="name">context:component-scan</span>&gt;</span></span><br><span class="line">    </span><br><span class="line"><span class="tag">&lt;/<span class="name">beans</span>&gt;</span></span><br></pre></td></tr></table></figure><div class="note info no-icon flat"><ul><li><code>use-default-filters=&quot;true&quot;</code> 表示：使用spring默认的规则，只要有Component、Controller、Service、Repository中的任意一个注解标注，则进行实例化。</li><li><code>use-default-filters=&quot;false&quot;</code> 表示：不再spring默认实例化规则，即使有Component、Controller、Service、Repository这些注解标注，也不再实例化。</li><li><context:include-filter type="annotation" expression="org.springframework.stereotype.Controller"/> 表示只有Controller进行实例化。</li></ul></div><div class="tabs" id="选择性实例化bean"><ul class="nav-tabs"><li class="tab active"><button type="button" data-href="#选择性实例化bean-1">测试程序</button></li><li class="tab"><button type="button" data-href="#选择性实例化bean-2">执行结果</button></li></ul><div class="tab-contents"><div class="tab-item-content active" id="选择性实例化bean-1"><figure class="highlight java"><table><tr><td class="gutter"><pre><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="meta">@Test</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">testChoose</span><span class="params">()</span>&#123;</span><br><span class="line">    <span class="type">ApplicationContext</span> <span class="variable">applicationContext</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">ClassPathXmlApplicationContext</span>(<span class="string">&quot;spring-choose.xml&quot;</span>);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><button type="button" class="tab-to-top" aria-label="scroll to top"><i class="fas fa-arrow-up"></i></button></div><div class="tab-item-content" id="选择性实例化bean-2"><div class="img-wrap"><div class="img-bg"><img class="img" src="https://raw.githubusercontent.com/silvan2077/markdown_pic/main/Picgo/20251003200513.png"/></div></div><button type="button" class="tab-to-top" aria-label="scroll to top"><i class="fas fa-arrow-up"></i></button></div></div></div></li><li>该方法是设置只有Controller进行实例化，也可以继续保持默认全部实例化，而将不需要的注解排除。</li></ul><div class="tabs" id="选择性实例化bean1"><ul class="nav-tabs"><li class="tab active"><button type="button" data-href="#选择性实例化bean1-1">配置文件</button></li><li class="tab"><button type="button" data-href="#选择性实例化bean1-2">测试结果</button></li></ul><div class="tab-contents"><div class="tab-item-content active" id="选择性实例化bean1-1"><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">context:component-scan</span> <span class="attr">base-package</span>=<span class="string">&quot;com.powernode.spring6.bean3&quot;</span>&gt;</span></span><br><span class="line">  <span class="tag">&lt;<span class="name">context:exclude-filter</span> <span class="attr">type</span>=<span class="string">&quot;annotation&quot;</span> <span class="attr">expression</span>=<span class="string">&quot;org.springframework.stereotype.Repository&quot;</span>/&gt;</span></span><br><span class="line">  <span class="tag">&lt;<span class="name">context:exclude-filter</span> <span class="attr">type</span>=<span class="string">&quot;annotation&quot;</span> <span class="attr">expression</span>=<span class="string">&quot;org.springframework.stereotype.Service&quot;</span>/&gt;</span></span><br><span class="line">  <span class="tag">&lt;<span class="name">context:exclude-filter</span> <span class="attr">type</span>=<span class="string">&quot;annotation&quot;</span> <span class="attr">expression</span>=<span class="string">&quot;org.springframework.stereotype.Controller&quot;</span>/&gt;</span></span><br><span class="line"><span class="tag">&lt;/<span class="name">context:component-scan</span>&gt;</span></span><br></pre></td></tr></table></figure><button type="button" class="tab-to-top" aria-label="scroll to top"><i class="fas fa-arrow-up"></i></button></div><div class="tab-item-content" id="选择性实例化bean1-2"><div class="img-wrap"><div class="img-bg"><img class="img" src="https://raw.githubusercontent.com/silvan2077/markdown_pic/main/Picgo/20251003200601.png"/></div></div><button type="button" class="tab-to-top" aria-label="scroll to top"><i class="fas fa-arrow-up"></i></button></div></div></div><h2 id="负责注入的注解"><a href="#负责注入的注解" class="headerlink" title="负责注入的注解"></a>负责注入的注解</h2><p><code>@Component</code> <code>@Controller</code> <code>@Service</code> <code>@Repository</code>这四个注解是用来声明Bean的，声明后这些Bean将被实例化。而给Bean属性赋值需要用到这些注解：</p><ul><li><code>@Value</code></li><li><code>@Autowired</code></li><li><code>@Qualifier</code></li><li><code>@Resource</code></li></ul><h3 id="Value"><a href="#Value" class="headerlink" title="@Value"></a>@Value</h3><p>当属性的类型是简单类型时，可以直接使用<code>@Value</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></pre></td><td class="code"><pre><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_">User</span> &#123;</span><br><span class="line">    <span class="meta">@Value(value = &quot;zhangsan&quot;)</span></span><br><span class="line">    <span class="keyword">private</span> String name;</span><br><span class="line">    <span class="meta">@Value(&quot;20&quot;)</span></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="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;name=&#x27;&quot;</span> + name + <span class="string">&#x27;\&#x27;&#x27;</span> +</span><br><span class="line">                <span class="string">&quot;, age=&quot;</span> + age +</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><details class="folding-tag" cyan><summary> 开启包扫描 </summary>              <div class='content'>              <ul><li>使用注解声明Bean时必须要被Spring扫描到才能被Spring管理，因此<strong>必须在配置中写上包扫描</strong></li><li>属性注入时，只要是已经交给Spring管理的Bean就可以注入属性，<strong>不需要单独开启包扫描</strong></li><li><strong>严格来说，包扫描时为了找到并注册Bean，和@Value无关</strong><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></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">beans</span> <span class="attr">xmlns</span>=<span class="string">&quot;http://www.springframework.org/schema/beans&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">xmlns:context</span>=<span class="string">&quot;http://www.springframework.org/schema/context&quot;</span></span></span><br><span class="line"><span class="tag">       <span class="attr">xsi:schemaLocation</span>=<span class="string">&quot;http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd</span></span></span><br><span class="line"><span class="string"><span class="tag">                           http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd&quot;</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">context:component-scan</span> <span class="attr">base-package</span>=<span class="string">&quot;com.powernode.spring6.bean4&quot;</span>/&gt;</span></span><br><span class="line"><span class="tag">&lt;/<span class="name">beans</span>&gt;</span></span><br></pre></td></tr></table></figure></li></ul>              </div>            </details><div class="tabs" id="@value"><ul class="nav-tabs"><li class="tab active"><button type="button" data-href="#@value-1">测试程序</button></li><li class="tab"><button type="button" data-href="#@value-2">执行结果</button></li></ul><div class="tab-contents"><div class="tab-item-content active" id="@value-1"><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><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="meta">@Test</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">testValue</span><span class="params">()</span>&#123;</span><br><span class="line">    <span class="type">ApplicationContext</span> <span class="variable">applicationContext</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">ClassPathXmlApplicationContext</span>(<span class="string">&quot;spring-injection.xml&quot;</span>);</span><br><span class="line">    <span class="type">Object</span> <span class="variable">user</span> <span class="operator">=</span> applicationContext.getBean(<span class="string">&quot;user&quot;</span>);</span><br><span class="line">    System.out.println(user);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><button type="button" class="tab-to-top" aria-label="scroll to top"><i class="fas fa-arrow-up"></i></button></div><div class="tab-item-content" id="@value-2"><div class="img-wrap"><div class="img-bg"><img class="img" src="https://raw.githubusercontent.com/silvan2077/markdown_pic/main/Picgo/20251003200650.png"/></div></div><button type="button" class="tab-to-top" aria-label="scroll to top"><i class="fas fa-arrow-up"></i></button></div></div></div><p>通过以上代码可以发现，即使没给属性提供<code>setter</code>方法，但仍然可以完成属性赋值。</p><p>如果提供setter方法，并且在setter方法上添加@Value注解，可以完成注入吗？尝试一下：</p><div class="tabs" id="@value1"><ul class="nav-tabs"><li class="tab active"><button type="button" data-href="#@value1-1">测试程序</button></li><li class="tab"><button type="button" data-href="#@value1-2">执行结果</button></li></ul><div class="tab-contents"><div class="tab-item-content active" id="@value1-1"><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><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="meta">@Component</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> String name;</span><br><span class="line"></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="meta">@Value(&quot;李四&quot;)</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">        <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="meta">@Value(&quot;30&quot;)</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="built_in">this</span>.age = age;</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;name=&#x27;&quot;</span> + name + <span class="string">&#x27;\&#x27;&#x27;</span> +</span><br><span class="line">                <span class="string">&quot;, age=&quot;</span> + age +</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><button type="button" class="tab-to-top" aria-label="scroll to top"><i class="fas fa-arrow-up"></i></button></div><div class="tab-item-content" id="@value1-2"><div class="img-wrap"><div class="img-bg"><img class="img" src="https://raw.githubusercontent.com/silvan2077/markdown_pic/main/Picgo/20251003200738.png"/></div></div><button type="button" class="tab-to-top" aria-label="scroll to top"><i class="fas fa-arrow-up"></i></button></div></div></div><ul><li>通过测试可以得知，@Value注解可以直接使用在属性上，也可以使用在setter方法上。都可以完成属性的赋值。</li><li>为了简化代码，以后我们一般不提供setter方法，直接在属性上使用@Value注解完成属性赋值。</li><li>出于好奇，我们再来测试一下，是否能够通过构造方法完成注入：</li></ul><div class="tabs" id="@value2"><ul class="nav-tabs"><li class="tab active"><button type="button" data-href="#@value2-1">测试程序</button></li><li class="tab"><button type="button" data-href="#@value2-2">执行结果</button></li></ul><div class="tab-contents"><div class="tab-item-content active" id="@value2-1"><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><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="meta">@Component</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> String name;</span><br><span class="line"></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="keyword">public</span> <span class="title function_">User</span><span class="params">(<span class="meta">@Value(&quot;隔壁老王&quot;)</span> String name, <span class="meta">@Value(&quot;33&quot;)</span> <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="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;name=&#x27;&quot;</span> + name + <span class="string">&#x27;\&#x27;&#x27;</span> +</span><br><span class="line">                <span class="string">&quot;, age=&quot;</span> + age +</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><button type="button" class="tab-to-top" aria-label="scroll to top"><i class="fas fa-arrow-up"></i></button></div><div class="tab-item-content" id="@value2-2"><p>执行结果：</p><div class="img-wrap"><div class="img-bg"><img class="img" src="https://raw.githubusercontent.com/silvan2077/markdown_pic/main/Picgo/20251003200828.png"/></div></div><button type="button" class="tab-to-top" aria-label="scroll to top"><i class="fas fa-arrow-up"></i></button></div></div></div><div class="note danger no-icon flat"><p><strong>通过测试得知：@Value注解可以出现在属性上、setter方法上、以及构造方法的形参上。可见Spring给我们提供了多样化的注入。</strong></p></div><h3 id="Autowired与-Qualifier"><a href="#Autowired与-Qualifier" class="headerlink" title="@Autowired与@Qualifier"></a>@Autowired与@Qualifier</h3><p>前面提到<code>@Value</code>可以注入<strong>简单类型</strong>，而<code>@Autowired</code>注解可以用来注入<strong>非简单类型</strong>。被翻译为：自动连线的，或者自动装配。单独使用<code>@Autowired</code>注解，<strong>默认根据类型装配</strong>。【默认是byType】</p><p>看一下它的源码：<br><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><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="meta">@Target(&#123;ElementType.CONSTRUCTOR, ElementType.METHOD, ElementType.PARAMETER, ElementType.FIELD, ElementType.ANNOTATION_TYPE&#125;)</span></span><br><span class="line"><span class="meta">@Retention(RetentionPolicy.RUNTIME)</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> Autowired &#123;</span><br><span class="line"><span class="type">boolean</span> <span class="title function_">required</span><span class="params">()</span> <span class="keyword">default</span> <span class="literal">true</span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></p><p>源码中有两处需要注意：</p><ul><li>第一处：该注解可以标注在哪里？<ul><li>构造方法上</li><li>方法上</li><li>形参上</li><li>属性上</li><li>注解上</li></ul></li><li>第二处：该注解有一个required属性，默认值是true，表示<strong>在注入的时候要求被注入的Bean必须是存在的，如果不存在则报错。</strong>如果required属性设置为false，表示注入的Bean存在或者不存在都没关系，存在的话就注入，不存在的话，也不报错。</li></ul><p><strong>我们先在属性上使用@Autowired注解：</strong><br><div class="tabs" id="@autowired"><ul class="nav-tabs"><li class="tab active"><button type="button" data-href="#@autowired-1">UserDao接口</button></li><li class="tab"><button type="button" data-href="#@autowired-2">UserDao实现类</button></li><li class="tab"><button type="button" data-href="#@autowired-3">配置文件</button></li><li class="tab"><button type="button" data-href="#@autowired-4">测试程序</button></li></ul><div class="tab-contents"><div class="tab-item-content active" id="@autowired-1"><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><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">package</span> com.powernode.spring6.dao;</span><br><span class="line"></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">interface</span> <span class="title class_">UserDao</span> &#123;</span><br><span class="line">    <span class="keyword">void</span> <span class="title function_">insert</span><span class="params">()</span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><button type="button" class="tab-to-top" aria-label="scroll to top"><i class="fas fa-arrow-up"></i></button></div><div class="tab-item-content" id="@autowired-2"><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><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="meta">@Repository</span> <span class="comment">//纳入bean管理</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">UserDaoForMySQL</span> <span class="keyword">implements</span> <span class="title class_">UserDao</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_">insert</span><span class="params">()</span> &#123;</span><br><span class="line">        System.out.println(<span class="string">&quot;正在向mysql数据库插入User数据&quot;</span>);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><!-- tab UserService --><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><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">@Service</span> <span class="comment">// 纳入bean管理</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="comment">// 没有提供构造方法和setter方法。</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">()</span>&#123;</span><br><span class="line">        userDao.insert();</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><button type="button" class="tab-to-top" aria-label="scroll to top"><i class="fas fa-arrow-up"></i></button></div><div class="tab-item-content" id="@autowired-3"><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></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">beans</span> <span class="attr">xmlns</span>=<span class="string">&quot;http://www.springframework.org/schema/beans&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">xmlns:context</span>=<span class="string">&quot;http://www.springframework.org/schema/context&quot;</span></span></span><br><span class="line"><span class="tag">       <span class="attr">xsi:schemaLocation</span>=<span class="string">&quot;http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd</span></span></span><br><span class="line"><span class="string"><span class="tag">                           http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd&quot;</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">context:component-scan</span> <span class="attr">base-package</span>=<span class="string">&quot;com.powernode.spring6.dao,com.powernode.spring6.service&quot;</span>/&gt;</span></span><br><span class="line"><span class="tag">&lt;/<span class="name">beans</span>&gt;</span></span><br></pre></td></tr></table></figure><button type="button" class="tab-to-top" aria-label="scroll to top"><i class="fas fa-arrow-up"></i></button></div><div class="tab-item-content" id="@autowired-4"><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><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="meta">@Test</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">testAutowired</span><span class="params">()</span>&#123;</span><br><span class="line">    <span class="type">ApplicationContext</span> <span class="variable">applicationContext</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">ClassPathXmlApplicationContext</span>(<span class="string">&quot;spring-injection.xml&quot;</span>);</span><br><span class="line">    <span class="type">UserService</span> <span class="variable">userService</span> <span class="operator">=</span> applicationContext.getBean(<span class="string">&quot;userService&quot;</span>, UserService.class);</span><br><span class="line">    userService.save();</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><button type="button" class="tab-to-top" aria-label="scroll to top"><i class="fas fa-arrow-up"></i></button></div></div></div><br>以上构造方法和setter方法都没有提供，经过测试，仍然可以注入成功。</p><div class="note info no-icon flat"><p>在构造方法，set方法，形参上使用的验证方法一致，此处省略</p></div><ul><li>此时我们已经清楚<code>@Autowired注解</code>可以出现在哪些位置了。</li><li>@Autowired注解默认是<code>byType</code>进行注入的，也就是说根据类型注入的，如果以上程序中，UserDao接口还有另外一个实现类，会出现问题吗？</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></pre></td><td class="code"><pre><span class="line"><span class="meta">@Repository</span> <span class="comment">//纳入bean管理</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">UserDaoForOracle</span> <span class="keyword">implements</span> <span class="title class_">UserDao</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_">insert</span><span class="params">()</span> &#123;</span><br><span class="line">        System.out.println(<span class="string">&quot;正在向Oracle数据库插入User数据&quot;</span>);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>当写完这个新的实现类之后，此时IDEA工具已经提示错误信息了：<br><div class="img-wrap"><div class="img-bg"><img class="img" src="https://raw.githubusercontent.com/silvan2077/markdown_pic/main/Picgo/20251003200904.png"/></div></div><br>错误信息中说：不能装配，UserDao这个Bean的数量大于1.</p><p>怎么解决这个问题呢？<strong>这时候就要根据名称进行装配了。</strong></p><p><code>@Autowired注解</code>和<code>@Qualifier注解</code>联合起来才可以根据名称进行装配，在@Qualifier注解中指定Bean名称。<br><div class="tabs" id="@autowired与@qualifier"><ul class="nav-tabs"><li class="tab active"><button type="button" data-href="#@autowired与@qualifier-1">UserService实现类</button></li><li class="tab"><button type="button" data-href="#@autowired与@qualifier-2">UserService</button></li><li class="tab"><button type="button" data-href="#@autowired与@qualifier-3">执行结果</button></li></ul><div class="tab-contents"><div class="tab-item-content active" id="@autowired与@qualifier-1"><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><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="meta">@Repository</span> <span class="comment">// 这里没有给bean起名，默认名字是：userDaoForOracle</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">UserDaoForOracle</span> <span class="keyword">implements</span> <span class="title class_">UserDao</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_">insert</span><span class="params">()</span> &#123;</span><br><span class="line">        System.out.println(<span class="string">&quot;正在向Oracle数据库插入User数据&quot;</span>);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><button type="button" class="tab-to-top" aria-label="scroll to top"><i class="fas fa-arrow-up"></i></button></div><div class="tab-item-content" id="@autowired与@qualifier-2"><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><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="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><br><span class="line"></span><br><span class="line">    <span class="meta">@Autowired</span></span><br><span class="line">    <span class="meta">@Qualifier(&quot;userDaoForOracle&quot;)</span> <span class="comment">// 这个是bean的名字。</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 class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">save</span><span class="params">()</span>&#123;</span><br><span class="line">        userDao.insert();</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><button type="button" class="tab-to-top" aria-label="scroll to top"><i class="fas fa-arrow-up"></i></button></div><div class="tab-item-content" id="@autowired与@qualifier-3"><div class="img-wrap"><div class="img-bg"><img class="img" src="https://cdn.nlark.com/yuque/0/2022/png/21376908/1665564055076-ffda3ad0-f957-4216-bf6c-957d62724d5f.png?x-oss-process=image%2Fwatermark%2Ctype_d3F5LW1pY3JvaGVp%2Csize_14%2Ctext_5Yqo5Yqb6IqC54K5%2Ccolor_FFFFFF%2Cshadow_50%2Ct_80%2Cg_se%2Cx_10%2Cy_10%2Fformat%2Cwebp"/></div></div><button type="button" class="tab-to-top" aria-label="scroll to top"><i class="fas fa-arrow-up"></i></button></div></div></div></p><div class="note danger no-icon flat"><p><strong>- <code>@Autowired</code>注解可以出现在：属性上、构造方法上、构造方法的参数上、setter方法上。</strong><br><strong>- 当带参数的构造方法只有一个，<code>@Autowired</code>注解可以省略。</strong><br><strong>- <code>@Autowired</code>默认根据类型注入。如果要根据名称注入的话，需要配合<code>@Qualifier</code>一起使用。</strong></p></div><h2 id="Resource"><a href="#Resource" class="headerlink" title="@Resource"></a>@Resource</h2><p>@Resource注解也可以完成非简单类型注入。它和@Autowired注解有什么区别？</p><ul><li><strong>@Resource注解是JDK扩展包中的，也就是说属于JDK的一部分</strong>。所以该注解是标准注解，更加具有通用性。(JSR-250标准中制定的注解类型。JSR是Java规范提案。)</li><li><strong>@Autowired注解是Spring框架自己的。</strong></li><li><strong>@Resource注解默认根据名称装配byName，未指定name时，使用属性名作为name。</strong> 通过name找不到的话会自动启动通过类型byType装配。</li><li><strong>@Autowired注解默认根据类型装配byType</strong>。如果想根据名称装配，需要配合@Qualifier注解一起用。</li><li>@Resource注解用在属性上、setter方法上。</li><li>@Autowired注解用在属性上、setter方法上、构造方法上、构造方法参数上。</li><li><div class="note info no-icon flat"><ul><li>@Resource注解属于JDK扩展包，所以不在JDK当中，需要额外引入以下依赖：【<strong>如果是JDK8的话不需要额外引入依赖。高于JDK11或低于JDK8需要引入以下依赖。</strong>】</li><li>需要注意的是，如果使用的是<code>Spring6</code>，要知道<code>Spring6</code>不再支持<code>JavaEE</code>，它支持的是<code>JakartaEE9</code>。（Oracle把JavaEE贡献给Apache了，Apache把JavaEE的名字改成JakartaEE了，大家之前所接触的所有的  javax.<em>  包名统一修改为  jakarta.</em>包名了。）</li></ul></div><div class="tabs" id="@resource"><ul class="nav-tabs"><li class="tab active"><button type="button" data-href="#@resource-1">Spring6及以上版本使用此依赖</button></li><li class="tab"><button type="button" data-href="#@resource-2">Spring5及以下版本使用此依赖</button></li></ul><div class="tab-contents"><div class="tab-item-content active" id="@resource-1"><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>jakarta.annotation<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.annotation-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.1.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><button type="button" class="tab-to-top" aria-label="scroll to top"><i class="fas fa-arrow-up"></i></button></div><div class="tab-item-content" id="@resource-2"><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>javax.annotation<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>javax.annotation-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>1.3.2<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><button type="button" class="tab-to-top" aria-label="scroll to top"><i class="fas fa-arrow-up"></i></button></div></div></div></li></ul><p>@Resource注解的源码如下：<br><div class="img-wrap"><div class="img-bg"><img class="img" src="https://raw.githubusercontent.com/silvan2077/markdown_pic/main/Picgo/20251003201345.png"/></div></div></p><details class="folding-tag" yellow><summary> 测试是否为根据Bean的Id进行装配 </summary>              <div class='content'>              <div class="tabs" id="@resource1"><ul class="nav-tabs"><li class="tab active"><button type="button" data-href="#@resource1-1">将该类名称改为xyz</button></li><li class="tab"><button type="button" data-href="#@resource1-2">按名称装配</button></li><li class="tab"><button type="button" data-href="#@resource1-3">测试结果</button></li></ul><div class="tab-contents"><div class="tab-item-content active" id="@resource1-1"><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><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="meta">@Repository(&quot;xyz&quot;)</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">UserDaoForOracle</span> <span class="keyword">implements</span> <span class="title class_">UserDao</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_">insert</span><span class="params">()</span> &#123;</span><br><span class="line">        System.out.println(<span class="string">&quot;正在向Oracle数据库插入User数据&quot;</span>);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><button type="button" class="tab-to-top" aria-label="scroll to top"><i class="fas fa-arrow-up"></i></button></div><div class="tab-item-content" id="@resource1-2"><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><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">@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">@Resource(name = &quot;xyz&quot;)</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="keyword">public</span> <span class="keyword">void</span> <span class="title function_">save</span><span class="params">()</span>&#123;</span><br><span class="line">        userDao.insert();</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><button type="button" class="tab-to-top" aria-label="scroll to top"><i class="fas fa-arrow-up"></i></button></div><div class="tab-item-content" id="@resource1-3"><div class="img-wrap"><div class="img-bg"><img class="img" src="https://raw.githubusercontent.com/silvan2077/markdown_pic/main/Picgo/20251003201531.png"/></div></div><button type="button" class="tab-to-top" aria-label="scroll to top"><i class="fas fa-arrow-up"></i></button></div></div></div>              </div>            </details><details class="folding-tag" cyan><summary> 测试@Resource注解没有指定name时，如何查找 </summary>              <div class='content'>              <p><strong>把UserDaoForOracle的名字xyz修改为userDao，让这个Bean的名字和UserService类中的UserDao属性名一致：</strong></p><div class="tabs" id="@resource2"><ul class="nav-tabs"><li class="tab active"><button type="button" data-href="#@resource2-1">UserDaoForOracle</button></li><li class="tab"><button type="button" data-href="#@resource2-2">UserService</button></li><li class="tab"><button type="button" data-href="#@resource2-3">测试结果</button></li></ul><div class="tab-contents"><div class="tab-item-content active" id="@resource2-1"><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><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="meta">@Repository(&quot;userDao&quot;)</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">UserDaoForOracle</span> <span class="keyword">implements</span> <span class="title class_">UserDao</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_">insert</span><span class="params">()</span> &#123;</span><br><span class="line">        System.out.println(<span class="string">&quot;正在向Oracle数据库插入User数据&quot;</span>);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><button type="button" class="tab-to-top" aria-label="scroll to top"><i class="fas fa-arrow-up"></i></button></div><div class="tab-item-content" id="@resource2-2"><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><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">@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">@Resource</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="keyword">public</span> <span class="keyword">void</span> <span class="title function_">save</span><span class="params">()</span>&#123;</span><br><span class="line">        userDao.insert();</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><button type="button" class="tab-to-top" aria-label="scroll to top"><i class="fas fa-arrow-up"></i></button></div><div class="tab-item-content" id="@resource2-3"><div class="img-wrap"><div class="img-bg"><img class="img" src="https://raw.githubusercontent.com/silvan2077/markdown_pic/main/Picgo/20251003201610.png"/></div></div><button type="button" class="tab-to-top" aria-label="scroll to top"><i class="fas fa-arrow-up"></i></button></div></div></div><div class="note danger no-icon flat"><p>通过测试得知，当@Resource注解使用时没有指定name的时候，还是根据name进行查找，这个name是属性名。</p></div>              </div>            </details><p>接下来把UserService类中的属性名修改一下重新测试：<br><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><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">@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">@Resource</span></span><br><span class="line">    <span class="keyword">private</span> UserDao userDao2;</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">()</span>&#123;</span><br><span class="line">        userDao2.insert();</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></p><p>执行结果：<br><div class="img-wrap"><div class="img-bg"><img class="img" src="https://raw.githubusercontent.com/silvan2077/markdown_pic/main/Picgo/20251003201703.png"/></div></div></p><p>根据异常信息得知：显然当通过name找不到的时候，自然会启动byType进行注入。以上的错误是因为UserDao接口下有两个实现类导致的。所以根据类型注入就会报错。</p><details class="folding-tag" red><summary> 测试@Resource注解使用在Setter方法上 </summary>              <div class='content'>              <figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><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="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><br><span class="line"></span><br><span class="line">    <span class="meta">@Resource</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="keyword">public</span> <span class="keyword">void</span> <span class="title function_">save</span><span class="params">()</span>&#123;</span><br><span class="line">        userDao.insert();</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>注意这个setter方法的方法名，setUserDao去掉set之后，将首字母变小写userDao，userDao就是name</p><p>执行结果：</p><div class="img-wrap"><div class="img-bg"><img class="img" src="https://raw.githubusercontent.com/silvan2077/markdown_pic/main/Picgo/20251003202030.png"/></div></div>              </div>            </details><div class="note danger no-icon flat"><p><strong>一句话总结@Resource注解：默认<code>byName</code>注入，没有指定name时把属性名当做name，根据name找不到时，才会<code>byType</code>注入。byType注入时，某种类型的Bean只能有一个。</strong></p></div><h2 id="全注解式开发"><a href="#全注解式开发" class="headerlink" title="全注解式开发"></a>全注解式开发</h2><p><strong>全注解开发就是不再使用spring配置文件了。写一个配置类来代替配置文件。</strong><br><figure class="highlight java"><table><tr><td class="gutter"><pre><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="meta">@Configuration</span></span><br><span class="line"><span class="meta">@ComponentScan(&#123;&quot;com.powernode.spring6.dao&quot;, &quot;com.powernode.spring6.service&quot;&#125;)</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">Spring6Configuration</span> &#123;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></p><p>不再使用配置文件，因此编写测试程序时，不再需要<code>new ClassPathXmlApplicationContext()</code>对象了。<br><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><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="meta">@Test</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">testNoXml</span><span class="params">()</span>&#123;</span><br><span class="line">    <span class="type">ApplicationContext</span> <span class="variable">applicationContext</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">AnnotationConfigApplicationContext</span>(Spring6Configuration.class);</span><br><span class="line">    <span class="type">UserService</span> <span class="variable">userService</span> <span class="operator">=</span> applicationContext.getBean(<span class="string">&quot;userService&quot;</span>, UserService.class);</span><br><span class="line">    userService.save();</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></p><h1 id="GoF之代理模式"><a href="#GoF之代理模式" class="headerlink" title="GoF之代理模式"></a>GoF之代理模式</h1><h2 id="对代理模式的理解"><a href="#对代理模式的理解" class="headerlink" title="对代理模式的理解"></a>对代理模式的理解</h2><details class="folding-tag" yellow><summary> 举例 </summary>              <div class='content'>              <ul><li><strong>生活场景1</strong>：牛村的牛二看上了隔壁村小花，牛二不好意思直接找小花，于是牛二找来了媒婆王妈妈。这里面就有一个非常典型的代理模式。牛二不能和小花直接对接，只能找一个中间人。其中王妈妈是代理类，牛二是目标类。王妈妈代替牛二和小花先见个面。（现实生活中的婚介所）【在程序中，对象A和对象B无法直接交互时。】</li><li><strong>生活场景2</strong>：你刚到北京，要租房子，可以自己找，也可以找链家帮你找。其中链家是代理类，你是目标类。你们两个都有共同的行为：找房子。不过链家除了满足你找房子，另外会收取一些费用的。(现实生活中的房产中介)【在程序中，功能需要增强时。】</li><li><strong>西游记场景</strong>：八戒和高小姐的故事。八戒要强抢民女高翠兰。悟空得知此事之后怎么做的？悟空幻化成高小姐的模样。代替高小姐与八戒会面。其中八戒是客户端程序。悟空是代理类。高小姐是目标类。那天夜里，在八戒眼里，眼前的就是高小姐，对于八戒来说，他是不知道眼前的高小姐是悟空幻化的，在他内心里这就是高小姐。所以悟空代替高小姐和八戒亲了嘴儿。这是非常典型的代理模式实现的保护机制。</li></ul>              </div>            </details><p><strong>代理模式中有一个非常重要的特点：对于客户端程序来说，使用代理对象时就像在使用目标对象一样。</strong></p><p>代理模式是GoF23种设计模式之一。属于结构型设计模式。</p><p>代理模式的作用是：为其他对象提供一种代理以控制对这个对象的访问。在某些情况下，一个客户不想或者不能直接引用一个对象，此时可以通过一个称之为<code>代理</code>的第三者来实现间接引用。代理对象可以在客户端和目标对象之间起到中介的作用，并且可以通过代理对象去掉客户不应该看到的内容和服务或者添加客户需要的额外服务。 通过引入一个新的对象来实现对真实对象的操作或者将新的对象作为真实对象的一个替身，这种实现机制即为<code>代理模式</code>，通过引入代理对象来间接访问一个对象，这就是代理模式的模式动机。</p><p>代理模式中的角色：</p><ul><li><strong>代理类</strong></li><li><strong>目标类</strong></li><li><strong>代理类和目标类的公共接口</strong>：客户端在使用代理类时就像在使用目标类，不被客户端所察觉，所以代理类和目标类要有共同的行为，也就是<code>实现共同的接口</code>。</li></ul><p>代理模式的类图：</p><div class="img-wrap"><div class="img-bg"><img class="img" src="https://raw.githubusercontent.com/silvan2077/markdown_pic/main/Picgo/20251005141658.png"/></div></div><div class="note info no-icon flat"><p>代理模式在代码实现上，包括两种形式：</p><ul><li>静态代理</li><li>动态代理</li></ul></div><h2 id="静态代理"><a href="#静态代理" class="headerlink" title="静态代理"></a>静态代理</h2><p>现在有这样一个接口和实现类：<br><div class="tabs" id="静态代理"><ul class="nav-tabs"><li class="tab active"><button type="button" data-href="#静态代理-1">接口</button></li><li class="tab"><button type="button" data-href="#静态代理-2">实现类</button></li></ul><div class="tab-contents"><div class="tab-item-content active" id="静态代理-1"><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><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">interface</span> <span class="title class_">OrderService</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"><span class="comment">     */</span></span><br><span class="line">    <span class="keyword">void</span> <span class="title function_">generate</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">    <span class="keyword">void</span> <span class="title function_">detail</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">    <span class="keyword">void</span> <span class="title function_">modify</span><span class="params">()</span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><button type="button" class="tab-to-top" aria-label="scroll to top"><i class="fas fa-arrow-up"></i></button></div><div class="tab-item-content" id="静态代理-2"><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><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_">OrderServiceImpl</span> <span class="keyword">implements</span> <span class="title class_">OrderService</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_">generate</span><span class="params">()</span> &#123;</span><br><span class="line">        <span class="keyword">try</span> &#123;</span><br><span class="line">            Thread.sleep(<span class="number">1234</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>);</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_">detail</span><span class="params">()</span> &#123;</span><br><span class="line">        <span class="keyword">try</span> &#123;</span><br><span class="line">            Thread.sleep(<span class="number">2541</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>);</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_">modify</span><span class="params">()</span> &#123;</span><br><span class="line">        <span class="keyword">try</span> &#123;</span><br><span class="line">            Thread.sleep(<span class="number">1010</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>);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><button type="button" class="tab-to-top" aria-label="scroll to top"><i class="fas fa-arrow-up"></i></button></div></div></div><br>其中Thread.sleep()方法的调用是为了模拟操作耗时。</p><p>项目已上线，并且运行正常，但是客户反馈系统有一些地方运行较慢，要求项目组对系统进行优化。于是项目负责人就下达了这个需求。首先需要搞清楚是哪些业务方法耗时较长，于是让我们统计每个业务方法所耗费的时长。如果是你，你该怎么做呢？</p><details class="folding-tag" blue><summary> 第一种方案：直接修改Java源代码 </summary>              <div class='content'>              <ul><li>直接在每个业务方法中添加统计时间的逻辑<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><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_">OrderServiceImpl</span> <span class="keyword">implements</span> <span class="title class_">OrderService</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_">generate</span><span class="params">()</span> &#123;</span><br><span class="line">        <span class="type">long</span> <span class="variable">begin</span> <span class="operator">=</span> System.currentTimeMillis();</span><br><span class="line">        <span class="keyword">try</span> &#123;</span><br><span class="line">            Thread.sleep(<span class="number">1234</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>);</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 - begin)+<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">public</span> <span class="keyword">void</span> <span class="title function_">detail</span><span class="params">()</span> &#123;</span><br><span class="line">        <span class="type">long</span> <span class="variable">begin</span> <span class="operator">=</span> System.currentTimeMillis();</span><br><span class="line">        <span class="keyword">try</span> &#123;</span><br><span class="line">            Thread.sleep(<span class="number">2541</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>);</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 - begin)+<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">public</span> <span class="keyword">void</span> <span class="title function_">modify</span><span class="params">()</span> &#123;</span><br><span class="line">        <span class="type">long</span> <span class="variable">begin</span> <span class="operator">=</span> System.currentTimeMillis();</span><br><span class="line">        <span class="keyword">try</span> &#123;</span><br><span class="line">            Thread.sleep(<span class="number">1010</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>);</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 - begin)+<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>需求可以满足，但这样显然违背了OCP开闭原则。这种方案不可取。</li></ul>              </div>            </details><details class="folding-tag" yellow><summary> 第二种方案：编写一个子类继承 </summary>              <div class='content'>              <ul><li>第二种方案：编写一个子类继承OrderServiceImpl，在子类中重写每个方法，代码如下：<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><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_">OrderServiceImplSub</span> <span class="keyword">extends</span> <span class="title class_">OrderServiceImpl</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_">generate</span><span class="params">()</span> &#123;</span><br><span class="line">        <span class="type">long</span> <span class="variable">begin</span> <span class="operator">=</span> System.currentTimeMillis();</span><br><span class="line">        <span class="built_in">super</span>.generate();</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 - begin)+<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">public</span> <span class="keyword">void</span> <span class="title function_">detail</span><span class="params">()</span> &#123;</span><br><span class="line">        <span class="type">long</span> <span class="variable">begin</span> <span class="operator">=</span> System.currentTimeMillis();</span><br><span class="line">        <span class="built_in">super</span>.detail();</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 - begin)+<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">public</span> <span class="keyword">void</span> <span class="title function_">modify</span><span class="params">()</span> &#123;</span><br><span class="line">        <span class="type">long</span> <span class="variable">begin</span> <span class="operator">=</span> System.currentTimeMillis();</span><br><span class="line">        <span class="built_in">super</span>.modify();</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 - begin)+<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></li></ul><p><div class="note info no-icon flat"><p>这种方式可以解决，但是存在两个问题：</p></p><p><ul></p><p><li>第一个问题：假设系统中有100个这样的业务类，需要提供100个子类，并且之前写好的创建Service对象的代码，都要修改为创建子类对象。</li></p><p><li>第二个问题：由于采用了继承的方式，导致代码之间的耦合度较高。</li><br>&lt;/ul&gt;<br>&lt;/div&gt;<br>这种方案也不可取。</p>              </div>            </details><ul><li>第三种方案：使用代理模式（采用静态代理）</li></ul><p>可以为OrderService接口提供一个代理类。<br><div class="tabs" id="静态代理1"><ul class="nav-tabs"><li class="tab active"><button type="button" data-href="#静态代理1-1">静态代理类</button></li><li class="tab"><button type="button" data-href="#静态代理1-2">客户端程序</button></li><li class="tab"><button type="button" data-href="#静态代理1-3">运行结果</button></li></ul><div class="tab-contents"><div class="tab-item-content active" id="静态代理1-1"><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><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">public</span> <span class="keyword">class</span> <span class="title class_">OrderServiceProxy</span> <span class="keyword">implements</span> <span class="title class_">OrderService</span>&#123; <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">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="keyword">public</span> <span class="title function_">OrderServiceProxy</span><span class="params">(OrderService orderService)</span> &#123;</span><br><span class="line">        <span class="built_in">this</span>.orderService = orderService;</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_">generate</span><span class="params">()</span> &#123;</span><br><span class="line">        <span class="type">long</span> <span class="variable">begin</span> <span class="operator">=</span> System.currentTimeMillis();</span><br><span class="line">        <span class="comment">// 执行目标对象的目标方法</span></span><br><span class="line">        orderService.generate();</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 - begin)+<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">public</span> <span class="keyword">void</span> <span class="title function_">detail</span><span class="params">()</span> &#123;</span><br><span class="line">        <span class="type">long</span> <span class="variable">begin</span> <span class="operator">=</span> System.currentTimeMillis();</span><br><span class="line">        <span class="comment">// 执行目标对象的目标方法</span></span><br><span class="line">        orderService.detail();</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 - begin)+<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">public</span> <span class="keyword">void</span> <span class="title function_">modify</span><span class="params">()</span> &#123;</span><br><span class="line">        <span class="type">long</span> <span class="variable">begin</span> <span class="operator">=</span> System.currentTimeMillis();</span><br><span class="line">        <span class="comment">// 执行目标对象的目标方法</span></span><br><span class="line">        orderService.modify();</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 - begin)+<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><button type="button" class="tab-to-top" aria-label="scroll to top"><i class="fas fa-arrow-up"></i></button></div><div class="tab-item-content" id="静态代理1-2"><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><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_">Client</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">OrderService</span> <span class="variable">target</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">OrderServiceImpl</span>();</span><br><span class="line">        <span class="comment">// 创建代理对象</span></span><br><span class="line">        <span class="type">OrderService</span> <span class="variable">proxy</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">OrderServiceProxy</span>(target);</span><br><span class="line">        <span class="comment">// 调用代理对象的代理方法</span></span><br><span class="line">        proxy.generate();</span><br><span class="line">        proxy.modify();</span><br><span class="line">        proxy.detail();</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><button type="button" class="tab-to-top" aria-label="scroll to top"><i class="fas fa-arrow-up"></i></button></div><div class="tab-item-content" id="静态代理1-3"><div class="img-wrap"><div class="img-bg"><img class="img" src="https://raw.githubusercontent.com/silvan2077/markdown_pic/main/Picgo/20251005143228.png"/></div></div><button type="button" class="tab-to-top" aria-label="scroll to top"><i class="fas fa-arrow-up"></i></button></div></div></div></p><div class="note info no-icon flat"><p>这种方式的优点：符合OCP开闭原则，同时采用的是关联关系，所以程序的耦合度较低。所以这种方案是被推荐的。</p></div><p>以上就是代理模式中的静态代理，其中<code>OrderService接口</code>是代理类和目标类的<code>共同接口</code>。OrderServiceImpl是目标类。OrderServiceProxy是代理类。</p><div class="note danger no-icon flat"><p>静态代理适合较小项目使用，如果项目较大，系统中业务接口很多，一个接口对应一个代理类，使用静态代理便需要创建很多代理类，造成<code>类爆炸</code>。而接下来的动态代理就可以解决该问题。</p></div><h2 id="动态代理"><a href="#动态代理" class="headerlink" title="动态代理"></a>动态代理</h2><p>在程序运行阶段，在内存中动态生成代理类的字节码，被称为<code>动态代理</code>。<strong>动态代理能帮助我们减少代理类的数量。解决代码复用的问题</strong>。</p><p>在内存当中动态生成类的技术常见的包括：</p><ul><li><code>JDK动态代理技术</code>：<strong>只能代理接口。</strong></li><li><code>CGLIB动态代理技术</code>：CGLIB(Code Generation Library)是一个开源项目。是一个强大的，高性能，高质量的Code生成类库，它可以在运行期扩展Java类与实现Java接口。<strong>既可以代理接口，又可以代理类</strong>，<strong>底层是通过继承的方式实现的</strong>。性能比JDK动态代理要好。<strong>（底层有一个小而快的字节码处理框架ASM。）</strong></li><li>Javassist动态代理技术：Javassist是一个开源的分析、编辑和创建Java字节码的类库。是由东京工业大学的数学和计算机科学系的 Shigeru Chiba （千叶 滋）所创建的。它已加入了开放源代码JBoss 应用服务器项目，通过使用Javassist对字节码操作为JBoss实现动态”AOP”框架。</li></ul><h3 id="JDK动态代理"><a href="#JDK动态代理" class="headerlink" title="JDK动态代理"></a>JDK动态代理</h3><p>还是使用静态代理中的例子：一个接口和一个实现类。</p><div class="tabs" id="jdk动态代理1"><ul class="nav-tabs"><li class="tab active"><button type="button" data-href="#jdk动态代理1-1">接口</button></li><li class="tab"><button type="button" data-href="#jdk动态代理1-2">实现类</button></li></ul><div class="tab-contents"><div class="tab-item-content active" id="jdk动态代理1-1"><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><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">interface</span> <span class="title class_">OrderService</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"><span class="comment">     */</span></span><br><span class="line">    <span class="keyword">void</span> <span class="title function_">generate</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">    <span class="keyword">void</span> <span class="title function_">detail</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">    <span class="keyword">void</span> <span class="title function_">modify</span><span class="params">()</span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><button type="button" class="tab-to-top" aria-label="scroll to top"><i class="fas fa-arrow-up"></i></button></div><div class="tab-item-content" id="jdk动态代理1-2"><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><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_">OrderServiceImpl</span> <span class="keyword">implements</span> <span class="title class_">OrderService</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_">generate</span><span class="params">()</span> &#123;</span><br><span class="line">        <span class="keyword">try</span> &#123;</span><br><span class="line">            Thread.sleep(<span class="number">1234</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>);</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_">detail</span><span class="params">()</span> &#123;</span><br><span class="line">        <span class="keyword">try</span> &#123;</span><br><span class="line">            Thread.sleep(<span class="number">2541</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>);</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_">modify</span><span class="params">()</span> &#123;</span><br><span class="line">        <span class="keyword">try</span> &#123;</span><br><span class="line">            Thread.sleep(<span class="number">1010</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>);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><button type="button" class="tab-to-top" aria-label="scroll to top"><i class="fas fa-arrow-up"></i></button></div></div></div><p>在静态代理时，除了以上一个接口和一个实现类之外，要需要写一个代理类UserServiceProxy，而在动态代理中UserServiceProxy代理类可以动态生成。所以这个类不需要写。我们直接写客户端程序即可：</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="keyword">public</span> <span class="keyword">class</span> <span class="title class_">Client</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">OrderService</span> <span class="variable">target</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">OrderServiceImpl</span>();</span><br><span class="line">        <span class="comment">// 第二步：创建代理对象</span></span><br><span class="line">        <span class="type">OrderService</span> <span class="variable">orderServiceProxy</span> <span class="operator">=</span> Proxy.newProxyInstance(target.getClass().getClassLoader(), target.getClass().getInterfaces(), 调用处理器对象);</span><br><span class="line">        <span class="comment">// 第三步：调用代理对象的代理方法</span></span><br><span class="line">        orderServiceProxy.detail();</span><br><span class="line">        orderServiceProxy.modify();</span><br><span class="line">        orderServiceProxy.generate();</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p><code>OrderService orderServiceProxy = Proxy.newProxyInstance(target.getClass().getClassLoader(), target.getClass().getInterfaces(), 调用处理器对象)</code>这行代码做了两件事：</p><ul><li>第一件事：在内存中生成了代理类的字节码</li><li>第二件事：创建代理对象</li></ul><p>Proxy类全名：java.lang.reflect.Proxy。这是JDK提供的一个类（所以称为JDK动态代理）。主要是通过这个类在内存中生成代理类的字节码。</p><p>其中newProxyInstance()方法有三个参数：</p><ul><li><strong>第一个参数：类加载器</strong>。在内存中生成了字节码，要想执行这个字节码，也是需要先把这个字节码加载到内存当中的。所以要指定使用哪个类加载器加载。</li><li><strong>第二个参数：接口类型</strong>。代理类和目标类实现相同的接口，所以要通过这个参数告诉JDK动态代理生成的类要实现哪些接口。</li><li><strong>第三个参数：调用处理器</strong>。这是一个JDK动态代理规定的接口，接口全名：java.lang.reflect.InvocationHandler。显然这是一个回调接口，也就是说调用这个接口中方法的程序已经写好了，就差这个接口的实现类了。</li></ul><p>所以接下来我们要写一下java.lang.reflect.InvocationHandler接口的实现类，并且实现接口中的方法，代码如下：</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></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">TimerInvocationHandler</span> <span class="keyword">implements</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, Object[] args)</span> <span class="keyword">throws</span> Throwable &#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>InvocationHandler接口中有一个方法invoke，这个invoke方法上有三个参数：</p><ul><li><strong>第一个参数：Object proxy。代理对象</strong>。设计这个参数只是为了后期的方便，如果想在invoke方法中使用代理对象的话，尽管通过这个参数来使用。</li><li><strong>第二个参数：Method method。目标方法</strong>。</li><li><strong>第三个参数：Object[] args。目标方法调用时要传的参数</strong>。</li></ul><p>我们想要调用目标对象的目标方法，首先需要有目标对象的存在，所以我们通过给TimerInvocationHandler类添加一个构造方法，将目标对象传给TimerInvocationHandler类。</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></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">TimerInvocationHandler</span> <span class="keyword">implements</span> <span class="title class_">InvocationHandler</span> &#123;</span><br><span class="line">    <span class="comment">// 目标对象</span></span><br><span class="line">    <span class="keyword">private</span> Object target;</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_">TimerInvocationHandler</span><span class="params">(Object target)</span> &#123;</span><br><span class="line">        <span class="built_in">this</span>.target = target;</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_">invoke</span><span class="params">(Object proxy, Method method, Object[] args)</span> <span class="keyword">throws</span> Throwable &#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>有了目标对象我们就可以在invoke()方法中调用目标方法了。代码如下：</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_">TimerInvocationHandler</span> <span class="keyword">implements</span> <span class="title class_">InvocationHandler</span> &#123;</span><br><span class="line">    <span class="comment">// 目标对象</span></span><br><span class="line">    <span class="keyword">private</span> Object target;</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_">TimerInvocationHandler</span><span class="params">(Object target)</span> &#123;</span><br><span class="line">        <span class="built_in">this</span>.target = target;</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_">invoke</span><span class="params">(Object proxy, Method method, Object[] args)</span> <span class="keyword">throws</span> Throwable &#123;</span><br><span class="line">        <span class="comment">// 目标执行之前增强。</span></span><br><span class="line">        <span class="type">long</span> <span class="variable">begin</span> <span class="operator">=</span> System.currentTimeMillis();</span><br><span class="line">        <span class="comment">// 调用目标对象的目标方法</span></span><br><span class="line">        <span class="type">Object</span> <span class="variable">retValue</span> <span class="operator">=</span> method.invoke(target, args);</span><br><span class="line">        <span class="comment">// 目标执行之后增强。</span></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 - begin)+<span class="string">&quot;毫秒&quot;</span>);</span><br><span class="line">        <span class="comment">// 一定要记得返回哦。</span></span><br><span class="line">        <span class="keyword">return</span> retValue;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>到此为止，调用处理器就完成了。接下来继续完善Client程序：</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></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">Client</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">OrderService</span> <span class="variable">target</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">OrderServiceImpl</span>();</span><br><span class="line">        <span class="comment">// 创建代理对象</span></span><br><span class="line">        <span class="type">OrderService</span> <span class="variable">orderServiceProxy</span> <span class="operator">=</span> (OrderService) Proxy.newProxyInstance(target.getClass().getClassLoader(),</span><br><span class="line">                                                                                target.getClass().getInterfaces(),</span><br><span class="line">                                                                                <span class="keyword">new</span> <span class="title class_">TimerInvocationHandler</span>(target));</span><br><span class="line">        <span class="comment">// 调用代理对象的代理方法</span></span><br><span class="line">        orderServiceProxy.detail();</span><br><span class="line">        orderServiceProxy.modify();</span><br><span class="line">        orderServiceProxy.generate();</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><div class="note info no-icon flat"><ul><li>这样我们需要的几个类都写完了，但是发现这几个类都没有调用invoke方法，这是为什么？<ul><li>当我们调用代理对象的代理方法时，invoke方法就会被系统调用。</li></ul></li></ul></div><ul><li>折腾半天，这不还是要写接口的实现类，也没省什么劲呀，那还用什么动态代理呢？<ul><li>使用动态代理后无论多少Service接口，多少业务类，实现一个代理功能只需要写一次TimerInvocationHandler接口，使代码得到了复用，且防止类爆炸。</li></ul></li></ul><p>下面这段看上去很繁琐，可以提供一个工具类，封装一下下面的方法<br><div class="img-wrap"><div class="img-bg"><img class="img" src="https://raw.githubusercontent.com/silvan2077/markdown_pic/main/Picgo/20251005151907.png"/></div></div></p><p>我们可以提供一个工具类：ProxyUtil，封装一个方法：</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="keyword">public</span> <span class="keyword">class</span> <span class="title class_">ProxyUtil</span> &#123;</span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">static</span> Object <span class="title function_">newProxyInstance</span><span class="params">(Object target)</span> &#123;</span><br><span class="line">        <span class="keyword">return</span> Proxy.newProxyInstance(target.getClass().getClassLoader(), </span><br><span class="line">                target.getClass().getInterfaces(), </span><br><span class="line">                <span class="keyword">new</span> <span class="title class_">TimerInvocationHandler</span>(target));</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><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></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">Client</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">OrderService</span> <span class="variable">target</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">OrderServiceImpl</span>();</span><br><span class="line">        <span class="comment">// 创建代理对象</span></span><br><span class="line">        <span class="type">OrderService</span> <span class="variable">orderServiceProxy</span> <span class="operator">=</span> (OrderService) ProxyUtil.newProxyInstance(target);</span><br><span class="line">        <span class="comment">// 调用代理对象的代理方法</span></span><br><span class="line">        orderServiceProxy.detail();</span><br><span class="line">        orderServiceProxy.modify();</span><br><span class="line">        orderServiceProxy.generate();</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h3 id="CGLIB动态代理"><a href="#CGLIB动态代理" class="headerlink" title="CGLIB动态代理"></a>CGLIB动态代理</h3><p><strong>CGLIB既可以代理接口，又可以代理类。</strong>底层采用<code>继承</code>的方式实现。所以被代理的目标类不能使用final修饰。</p><p>使用CGLIB，需要引入它的依赖：</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>cglib<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>cglib<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.3.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>准备一个没有实现接口的类，如下：</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_">UserService</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_">login</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"></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">logout</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></pre></td></tr></table></figure><p>使用CGLIB在内存中为UserService类生成代理类，并创建对象：</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="keyword">public</span> <span class="keyword">class</span> <span class="title class_">Client</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">Enhancer</span> <span class="variable">enhancer</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">Enhancer</span>();</span><br><span class="line">        <span class="comment">// 告诉cglib要继承哪个类</span></span><br><span class="line">        enhancer.setSuperclass(UserService.class);</span><br><span class="line">        <span class="comment">// 设置回调接口</span></span><br><span class="line">        enhancer.setCallback(方法拦截器对象);</span><br><span class="line">        <span class="comment">// 生成源码，编译class，加载到JVM，并创建代理对象</span></span><br><span class="line">        <span class="type">UserService</span> <span class="variable">userServiceProxy</span> <span class="operator">=</span> (UserService)enhancer.create();</span><br><span class="line"></span><br><span class="line">        userServiceProxy.login();</span><br><span class="line">        userServiceProxy.logout();</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>和<code>JDK动态代理</code>原理差不多，在<code>CGLIB</code>中需要提供的不是<code>InvocationHandler</code>，而是：<code>net.sf.cglib.proxy.MethodInterceptor</code></p><p>编写<code>MethodInterceptor</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></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">TimerMethodInterceptor</span> <span class="keyword">implements</span> <span class="title class_">MethodInterceptor</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_">intercept</span><span class="params">(Object target, Method method, Object[] objects, MethodProxy methodProxy)</span> <span class="keyword">throws</span> Throwable &#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>MethodInterceptor接口中有一个方法intercept()，该方法有4个参数：</p><ul><li>第一个参数：目标对象</li><li>第二个参数：目标方法</li><li>第三个参数：目标方法调用时的实参</li><li>第四个参数：代理方法</li></ul><p>在MethodInterceptor的intercept()方法中调用目标以及添加增强：</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></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">TimerMethodInterceptor</span> <span class="keyword">implements</span> <span class="title class_">MethodInterceptor</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_">intercept</span><span class="params">(Object target, Method method, Object[] objects, MethodProxy methodProxy)</span> <span class="keyword">throws</span> Throwable &#123;</span><br><span class="line">        <span class="comment">// 前增强</span></span><br><span class="line">        <span class="type">long</span> <span class="variable">begin</span> <span class="operator">=</span> System.currentTimeMillis();</span><br><span class="line">        <span class="comment">// 调用目标</span></span><br><span class="line">        <span class="type">Object</span> <span class="variable">retValue</span> <span class="operator">=</span> methodProxy.invokeSuper(target, objects);</span><br><span class="line">        <span class="comment">// 后增强</span></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 - begin) + <span class="string">&quot;毫秒&quot;</span>);</span><br><span class="line">        <span class="comment">// 一定要返回</span></span><br><span class="line">        <span class="keyword">return</span> retValue;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><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></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">Client</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">Enhancer</span> <span class="variable">enhancer</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">Enhancer</span>();</span><br><span class="line">        <span class="comment">// 告诉cglib要继承哪个类</span></span><br><span class="line">        enhancer.setSuperclass(UserService.class);</span><br><span class="line">        <span class="comment">// 设置回调接口</span></span><br><span class="line">        enhancer.setCallback(<span class="keyword">new</span> <span class="title class_">TimerMethodInterceptor</span>());</span><br><span class="line">        <span class="comment">// 生成源码，编译class，加载到JVM，并创建代理对象</span></span><br><span class="line">        <span class="type">UserService</span> <span class="variable">userServiceProxy</span> <span class="operator">=</span> (UserService)enhancer.create();</span><br><span class="line"></span><br><span class="line">        userServiceProxy.login();</span><br><span class="line">        userServiceProxy.logout();</span><br><span class="line"></span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>对于高版本的JDK，如果使用CGLIB，需要在启动项中添加两个启动参数：</p><div class="img-wrap"><div class="img-bg"><img class="img" src="https://raw.githubusercontent.com/silvan2077/markdown_pic/main/Picgo/20251005152343.png"/></div></div><ul><li>—add-opens java.base/java.lang=ALL-UNNAMED</li><li>—add-opens java.base/sun.net.util=ALL-UNNAMED</li></ul><h1 id="面向切面编程AOP"><a href="#面向切面编程AOP" class="headerlink" title="面向切面编程AOP"></a>面向切面编程AOP</h1><ul><li><code>AOP（Aspect Oriented Programming）</code>：面向切面编程，面向方</li><li>AOP是对OOP的补充延伸。</li><li>AOP底层使用的就是动态代理来实现的。<div class="note info no-icon flat"><p>IoC使软件组件松耦合。AOP让你能够捕捉系统中经常使用的功能，把它转化成组件。</p></div></li></ul><p>Spring的AOP使用的动态代理是：<code>JDK动态代理</code> + <code>CGLIB动态代理</code>。Spring在这两种动态代理中灵活切换，如果是代理接口，会默认使用JDK动态代理，如果要代理某个类，这个类没有实现接口，就会切换使用CGLIB。当然，你也可以强制通过一些配置让Spring只使用CGLIB。</p><h2 id="AOP介绍"><a href="#AOP介绍" class="headerlink" title="AOP介绍"></a>AOP介绍</h2><p>一般一个系统当中都会有一些系统服务，例如：日志、事务管理、安全等。这些系统服务被称为：<strong><code>交叉业务</code></strong></p><p>这些<strong>交叉业务</strong>几乎是通用的，不管是做银行账户转账，还是删除用户数据。日志、事务管理、安全，这些都是需要做的。</p><p>如果每一个业务处理过程中，都需要掺杂这些交叉业务代码进去的话，存在两方面问题：</p><ul><li>第一：交叉业务代码在多个业务流程中反复出现，代码没有得到复用，且修改业务代码需要多处修改</li><li>第二：程序员无法专注核心业务代码的编写，在编写核心业务代码的同时还需要处理这些交叉业务。</li></ul><p>使用AOP可以很轻松的解决以上问题。</p><p>快速理解AOP的思想：<br><div class="img-wrap"><div class="img-bg"><img class="img" src="https://raw.githubusercontent.com/silvan2077/markdown_pic/main/Picgo/20251003211132.png"/></div></div><br><strong>用一句话总结AOP：将与核心业务无关的代码独立的抽取出来，形成一个独立的组件，然后以横向交叉的方式应用到业务流程当中的过程被称为AOP。</strong></p><div class="note danger no-icon flat"><p><strong>AOP的优点：</strong></p><ul><li><strong>第一：代码复用性增强。</strong></li><li><strong>第二：代码易维护。</strong></li><li><strong>第三：使开发者更关注业务逻辑。</strong></li></ul></div><h2 id="AOP的七大术语"><a href="#AOP的七大术语" class="headerlink" title="AOP的七大术语"></a>AOP的七大术语</h2><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><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_">UserService</span>&#123;</span><br><span class="line">     <span class="comment">//连接点 Joinpoint</span></span><br><span class="line">     <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">do1</span><span class="params">()</span>&#123;   <span class="comment">//切点 Pointcut</span></span><br><span class="line">        System.out.println(<span class="string">&quot;do 1&quot;</span>);</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">do2</span><span class="params">()</span>&#123;</span><br><span class="line">        System.out.println(<span class="string">&quot;do 2&quot;</span>);</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">do3</span><span class="params">()</span>&#123;</span><br><span class="line">        System.out.println(<span class="string">&quot;do 3&quot;</span>);</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">do4</span><span class="params">()</span>&#123;</span><br><span class="line">        System.out.println(<span class="string">&quot;do 4&quot;</span>);</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">do5</span><span class="params">()</span>&#123;</span><br><span class="line">        System.out.println(<span class="string">&quot;do 5&quot;</span>);</span><br><span class="line">    &#125;</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_">service</span><span class="params">()</span>&#123;</span><br><span class="line">        do1();</span><br><span class="line">        do2();</span><br><span class="line">        do3();</span><br><span class="line">        do5();</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><ul><li><strong>连接点 Joinpoint</strong><ul><li>在程序的整个执行流程中，<strong>可以织入</strong>切面的位置。方法的执行前后，异常抛出之后等位置。</li></ul></li><li><strong>切点 Pointcut</strong><ul><li>在程序执行流程中，<strong>真正织入</strong>切面的方法。（一个切点对应多个连接点）</li></ul></li><li><p><strong>通知 Advice</strong></p><ul><li>通知又叫增强，就是具体你要织入的代码。</li><li>通知包括：<ul><li>前置通知</li><li>后置通知</li><li>环绕通知</li><li>异常通知</li><li>最终通知<div class="img-wrap"><div class="img-bg"><img class="img" src="https://raw.githubusercontent.com/silvan2077/markdown_pic/main/Picgo/20251005152720.png"/></div></div></li></ul></li></ul></li><li><p><strong>切面 Aspect</strong></p><ul><li><strong>切点 + 通知就是切面。</strong></li></ul></li><li><p>织入 Weaving</p><ul><li>把通知应用到目标对象上的过程。</li></ul></li><li><p>代理对象 Proxy</p><ul><li>一个目标对象被织入通知后产生的新对象。</li></ul></li><li><p>目标对象 Target</p><ul><li>被织入通知的对象。</li></ul></li></ul><p>通过下图，大家可以很好的理解AOP的相关术语：</p><div class="img-wrap"><div class="img-bg"><img class="img" src="https://raw.githubusercontent.com/silvan2077/markdown_pic/main/Picgo/20251005152825.png"/></div></div><h2 id="切点表达式"><a href="#切点表达式" class="headerlink" title="切点表达式"></a>切点表达式</h2><p>切点表达式用来定义<code>通知（Advice）</code>往哪些方法上切入。</p><p>切入点表达式语法格式：</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">execution([访问控制权限修饰符] 返回值类型 [全限定类名]方法名(形式参数列表) [异常])</span><br></pre></td></tr></table></figure><p>访问控制权限修饰符：</p><ul><li>可选项。</li><li>没写，就是4个权限都包括。</li><li>写public就表示只包括公开的方法。</li></ul><p>返回值类型：</p><ul><li>必填项。</li><li>* 表示返回值类型任意。</li></ul><p>全限定类名：</p><ul><li>可选项。</li><li>两个点“..”代表当前包以及子包下的所有类。</li><li>省略时表示所有的类。</li></ul><p>方法名：</p><ul><li>必填项。</li><li>*表示所有方法。</li><li>set*表示所有的set方法。</li></ul><p>形式参数列表：</p><ul><li>必填项</li><li>() 表示没有参数的方法</li><li>(..) 参数类型和个数随意的方法</li><li>(*) 只有一个参数的方法</li><li>(*, String) 第一个参数类型随意，第二个参数是String的。</li></ul><p>异常：</p><ul><li>可选项。</li><li>省略时表示任意异常类型。</li></ul><p>理解以下的切点表达式：<br><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><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">// service包下所有的类中以delete开始的所有方法</span></span><br><span class="line">execution(<span class="keyword">public</span> * com.powernode.mall.service.*.delete*(..))</span><br><span class="line"><span class="comment">// mall包下所有的类的所有的方法</span></span><br><span class="line">execution(* com.powernode.mall..*(..))</span><br><span class="line"><span class="comment">// 所有类的所有方法</span></span><br><span class="line">execution(* *(..))</span><br></pre></td></tr></table></figure></p><h2 id="使用Spring的AOP"><a href="#使用Spring的AOP" class="headerlink" title="使用Spring的AOP"></a>使用Spring的AOP</h2><p>Spring对AOP的实现包括以下3种方式：</p><ul><li><strong>第一种方式：Spring框架结合AspectJ框架实现的AOP，基于注解方式。</strong></li><li><strong>第二种方式：Spring框架结合AspectJ框架实现的AOP，基于XML方式。</strong></li><li>第三种方式：Spring框架自己实现的AOP，基于XML配置方式。</li></ul><p>实际开发中，都是<code>Spring+AspectJ</code>来实现AOP。所以重点学习第一种和第二种方式。</p><div class="note info no-icon flat"><ul><li>什么是AspectJ？<ul><li>AspectJ是一个支持AOP的框架。AspectJ框架是独立于Spring框架之外一个框架。 </li></ul></li></ul></div><h3 id="准备工作"><a href="#准备工作" class="headerlink" title="准备工作"></a>准备工作</h3><p>使用Spring+AspectJ的AOP需要引入的依赖如下：</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></pre></td><td class="code"><pre><span class="line"><span class="comment">&lt;!--spring context依赖--&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<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-context<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-M2<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="comment">&lt;!--spring aop依赖--&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<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-aop<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-M2<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="comment">&lt;!--spring aspects依赖--&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<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-aspects<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-M2<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>Spring配置文件中添加context命名空间和aop命名空间</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="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">beans</span> <span class="attr">xmlns</span>=<span class="string">&quot;http://www.springframework.org/schema/beans&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">xmlns:context</span>=<span class="string">&quot;http://www.springframework.org/schema/context&quot;</span></span></span><br><span class="line"><span class="tag">       <span class="attr">xmlns:aop</span>=<span class="string">&quot;http://www.springframework.org/schema/aop&quot;</span></span></span><br><span class="line"><span class="tag">       <span class="attr">xsi:schemaLocation</span>=<span class="string">&quot;http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd</span></span></span><br><span class="line"><span class="string"><span class="tag">                           http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd</span></span></span><br><span class="line"><span class="string"><span class="tag">                           http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd&quot;</span>&gt;</span></span><br><span class="line"></span><br><span class="line"><span class="tag">&lt;/<span class="name">beans</span>&gt;</span></span><br></pre></td></tr></table></figure><h3 id="基于AspectJ的AOP注解式开发"><a href="#基于AspectJ的AOP注解式开发" class="headerlink" title="基于AspectJ的AOP注解式开发"></a>基于AspectJ的AOP注解式开发</h3><h4 id="实现步骤"><a href="#实现步骤" class="headerlink" title="实现步骤"></a>实现步骤</h4><p>第一步：定义目标类以及目标方法<br><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><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">public</span> <span class="keyword">class</span> <span class="title class_">OrderService</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_">generate</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></pre></td></tr></table></figure><br>第二步：定义切面类<br><figure class="highlight java"><table><tr><td class="gutter"><pre><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">// 切面类</span></span><br><span class="line"><span class="meta">@Aspect</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">MyAspect</span> &#123;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></p><p>第三步：目标类和切面类都纳入spring bean管理<br>    在目标类OrderService上添加 <strong>@Component</strong>注解。<br>    在切面类MyAspect类上添加 <strong>@Component</strong>注解。</p><p>第四步：在spring配置文件中添加组建扫描</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></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">beans</span> <span class="attr">xmlns</span>=<span class="string">&quot;http://www.springframework.org/schema/beans&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">xmlns:context</span>=<span class="string">&quot;http://www.springframework.org/schema/context&quot;</span></span></span><br><span class="line"><span class="tag">       <span class="attr">xmlns:aop</span>=<span class="string">&quot;http://www.springframework.org/schema/aop&quot;</span></span></span><br><span class="line"><span class="tag">       <span class="attr">xsi:schemaLocation</span>=<span class="string">&quot;http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd</span></span></span><br><span class="line"><span class="string"><span class="tag">                           http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd</span></span></span><br><span class="line"><span class="string"><span class="tag">                           http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd&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">context:component-scan</span> <span class="attr">base-package</span>=<span class="string">&quot;com.powernode.spring6.service&quot;</span>/&gt;</span></span><br><span class="line"><span class="tag">&lt;/<span class="name">beans</span>&gt;</span></span><br></pre></td></tr></table></figure><p>第五步：在切面类中添加通知<br><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><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="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_">MyAspect</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_">advice</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></pre></td></tr></table></figure></p><p>第六步：在通知上添加切点表达式<br><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><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="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_">MyAspect</span> &#123;</span><br><span class="line">    </span><br><span class="line">    <span class="comment">// 切点表达式</span></span><br><span class="line">    <span class="meta">@Before(&quot;execution(* com.powernode.spring6.service.OrderService.*(..))&quot;)</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_">advice</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></pre></td></tr></table></figure></p><p><strong>注解@Before表示前置通知。</strong></p><p>第七步：在spring配置文件中启用自动代理<br><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="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">beans</span> <span class="attr">xmlns</span>=<span class="string">&quot;http://www.springframework.org/schema/beans&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">xmlns:context</span>=<span class="string">&quot;http://www.springframework.org/schema/context&quot;</span></span></span><br><span class="line"><span class="tag">       <span class="attr">xmlns:aop</span>=<span class="string">&quot;http://www.springframework.org/schema/aop&quot;</span></span></span><br><span class="line"><span class="tag">       <span class="attr">xsi:schemaLocation</span>=<span class="string">&quot;http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd</span></span></span><br><span class="line"><span class="string"><span class="tag">                           http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd</span></span></span><br><span class="line"><span class="string"><span class="tag">                           http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd&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">context:component-scan</span> <span class="attr">base-package</span>=<span class="string">&quot;com.powernode.spring6.service&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">aop:aspectj-autoproxy</span> <span class="attr">proxy-target-class</span>=<span class="string">&quot;true&quot;</span>/&gt;</span></span><br><span class="line"><span class="tag">&lt;/<span class="name">beans</span>&gt;</span></span><br></pre></td></tr></table></figure></p><p><aop:aspectj-autoproxy  proxy-target-class="true"/> 开启自动代理之后，凡事带有@Aspect注解的bean都会生成代理对象。<br><div class="note info no-icon flat"><p>proxy-target-class=”true” 表示采用cglib动态代理。<br>proxy-target-class=”false” 表示采用jdk动态代理。默认值是false。即使写成false，当没有接口的时候，也会自动选择cglib生成代理类。</p></div></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></pre></td><td class="code"><pre><span class="line"><span class="keyword">package</span> com.powernode.spring6.test;</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">AOPTest</span> &#123;</span><br><span class="line">    <span class="meta">@Test</span></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">testAOP</span><span class="params">()</span>&#123;</span><br><span class="line">        <span class="type">ApplicationContext</span> <span class="variable">applicationContext</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">ClassPathXmlApplicationContext</span>(<span class="string">&quot;spring-aspectj-aop-annotation.xml&quot;</span>);</span><br><span class="line">        <span class="type">OrderService</span> <span class="variable">orderService</span> <span class="operator">=</span> applicationContext.getBean(<span class="string">&quot;orderService&quot;</span>, OrderService.class);</span><br><span class="line">        orderService.generate();</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>运行结果：</p><p><img src="https://cdn.nlark.com/yuque/0/2022/png/21376908/1665843923087-e1116f09-2470-46cb-b21a-1526f62cab50.png?x-oss-process=image%2Fwatermark%2Ctype_d3F5LW1pY3JvaGVp%2Csize_15%2Ctext_5Yqo5Yqb6IqC54K5%2Ccolor_FFFFFF%2Cshadow_50%2Ct_80%2Cg_se%2Cx_10%2Cy_10" alt="img"></p><h4 id="通知类型"><a href="#通知类型" class="headerlink" title="通知类型"></a>通知类型</h4><p>通知类型包括：</p><ul><li>前置通知：@Before 目标方法执行之前的通知</li><li>后置通知：@AfterReturning 目标方法执行之后的通知</li><li>环绕通知：@Around 目标方法之前添加通知，同时目标方法执行之后添加通知。</li><li>异常通知：@AfterThrowing 发生异常之后执行的通知</li><li>最终通知：@After 放在finally语句块中的通知</li></ul><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">package</span> com.powernode.spring6.service;</span><br><span class="line"></span><br><span class="line"><span class="keyword">import</span> org.aspectj.lang.ProceedingJoinPoint;</span><br><span class="line"><span class="keyword">import</span> org.aspectj.lang.annotation.*;</span><br><span class="line"><span class="keyword">import</span> org.springframework.stereotype.Component;</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="meta">@Aspect</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">MyAspect</span> &#123;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Around(&quot;execution(* com.powernode.spring6.service.OrderService.*(..))&quot;)</span></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">aroundAdvice</span><span class="params">(ProceedingJoinPoint proceedingJoinPoint)</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="comment">// 执行目标方法。</span></span><br><span class="line">        proceedingJoinPoint.proceed();</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="meta">@Before(&quot;execution(* com.powernode.spring6.service.OrderService.*(..))&quot;)</span></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">beforeAdvice</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"></span><br><span class="line">    <span class="meta">@AfterReturning(&quot;execution(* com.powernode.spring6.service.OrderService.*(..))&quot;)</span></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">afterReturningAdvice</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"></span><br><span class="line">    <span class="meta">@AfterThrowing(&quot;execution(* com.powernode.spring6.service.OrderService.*(..))&quot;)</span></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">afterThrowingAdvice</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"></span><br><span class="line">    <span class="meta">@After(&quot;execution(* com.powernode.spring6.service.OrderService.*(..))&quot;)</span></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">afterAdvice</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"></span><br><span class="line">&#125;</span><br><span class="line"><span class="keyword">package</span> com.powernode.spring6.service;</span><br><span class="line"></span><br><span class="line"><span class="keyword">import</span> org.springframework.stereotype.Component;</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_">OrderService</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_">generate</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 class="keyword">package</span> com.powernode.spring6.test;</span><br><span class="line"></span><br><span class="line"><span class="keyword">import</span> com.powernode.spring6.service.OrderService;</span><br><span class="line"><span class="keyword">import</span> org.junit.Test;</span><br><span class="line"><span class="keyword">import</span> org.springframework.context.ApplicationContext;</span><br><span class="line"><span class="keyword">import</span> org.springframework.context.support.ClassPathXmlApplicationContext;</span><br><span class="line"></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">AOPTest</span> &#123;</span><br><span class="line">    <span class="meta">@Test</span></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">testAOP</span><span class="params">()</span>&#123;</span><br><span class="line">        <span class="type">ApplicationContext</span> <span class="variable">applicationContext</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">ClassPathXmlApplicationContext</span>(<span class="string">&quot;spring-aspectj-aop-annotation.xml&quot;</span>);</span><br><span class="line">        <span class="type">OrderService</span> <span class="variable">orderService</span> <span class="operator">=</span> applicationContext.getBean(<span class="string">&quot;orderService&quot;</span>, OrderService.class);</span><br><span class="line">        orderService.generate();</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>执行结果：</p><p><img src="https://cdn.nlark.com/yuque/0/2022/png/21376908/1665892617792-22cc74a2-6876-4cd1-bb17-87d3b5211cae.png?x-oss-process=image%2Fwatermark%2Ctype_d3F5LW1pY3JvaGVp%2Csize_19%2Ctext_5Yqo5Yqb6IqC54K5%2Ccolor_FFFFFF%2Cshadow_50%2Ct_80%2Cg_se%2Cx_10%2Cy_10" alt="img"></p><p>通过上面的执行结果就可以判断他们的执行顺序了，这里不再赘述。</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></pre></td><td class="code"><pre><span class="line"><span class="keyword">package</span> com.powernode.spring6.service;</span><br><span class="line"></span><br><span class="line"><span class="keyword">import</span> org.springframework.stereotype.Component;</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_">OrderService</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_">generate</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">        <span class="keyword">if</span> (<span class="number">1</span> == <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_">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">&#125;</span><br></pre></td></tr></table></figure><p>再次执行测试程序，结果如下：</p><p><img src="https://cdn.nlark.com/yuque/0/2022/png/21376908/1665892847715-75045cd0-63b1-47f9-a77e-05911dc72339.png?x-oss-process=image%2Fwatermark%2Ctype_d3F5LW1pY3JvaGVp%2Csize_24%2Ctext_5Yqo5Yqb6IqC54K5%2Ccolor_FFFFFF%2Cshadow_50%2Ct_80%2Cg_se%2Cx_10%2Cy_10" alt="img"></p><p>通过测试得知，当发生异常之后，最终通知也会执行，因为最终通知@After会出现在finally语句块中。</p><p>出现异常之后，<strong>后置通知</strong>和<strong>环绕通知的结束部分</strong>不会执行。</p><h4 id="切面的先后顺序"><a href="#切面的先后顺序" class="headerlink" title="切面的先后顺序"></a>切面的先后顺序</h4><p>我们知道，业务流程当中不一定只有一个切面，可能有的切面控制事务，有的记录日志，有的进行安全控制，如果多个切面的话，顺序如何控制：<strong>可以使用@Order注解来标识切面类，为@Order注解的value指定一个整数型的数字，数字越小，优先级越高</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><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></pre></td><td class="code"><pre><span class="line"><span class="keyword">package</span> com.powernode.spring6.service;</span><br><span class="line"></span><br><span class="line"><span class="keyword">import</span> org.aspectj.lang.ProceedingJoinPoint;</span><br><span class="line"><span class="keyword">import</span> org.aspectj.lang.annotation.*;</span><br><span class="line"><span class="keyword">import</span> org.springframework.core.annotation.Order;</span><br><span class="line"><span class="keyword">import</span> org.springframework.stereotype.Component;</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="meta">@Order(1)</span> <span class="comment">//设置优先级</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">YourAspect</span> &#123;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Around(&quot;execution(* com.powernode.spring6.service.OrderService.*(..))&quot;)</span></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">aroundAdvice</span><span class="params">(ProceedingJoinPoint proceedingJoinPoint)</span> <span class="keyword">throws</span> Throwable &#123;</span><br><span class="line">        System.out.println(<span class="string">&quot;YourAspect环绕通知开始&quot;</span>);</span><br><span class="line">        <span class="comment">// 执行目标方法。</span></span><br><span class="line">        proceedingJoinPoint.proceed();</span><br><span class="line">        System.out.println(<span class="string">&quot;YourAspect环绕通知结束&quot;</span>);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Before(&quot;execution(* com.powernode.spring6.service.OrderService.*(..))&quot;)</span></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">beforeAdvice</span><span class="params">()</span>&#123;</span><br><span class="line">        System.out.println(<span class="string">&quot;YourAspect前置通知&quot;</span>);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@AfterReturning(&quot;execution(* com.powernode.spring6.service.OrderService.*(..))&quot;)</span></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">afterReturningAdvice</span><span class="params">()</span>&#123;</span><br><span class="line">        System.out.println(<span class="string">&quot;YourAspect后置通知&quot;</span>);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@AfterThrowing(&quot;execution(* com.powernode.spring6.service.OrderService.*(..))&quot;)</span></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">afterThrowingAdvice</span><span class="params">()</span>&#123;</span><br><span class="line">        System.out.println(<span class="string">&quot;YourAspect异常通知&quot;</span>);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@After(&quot;execution(* com.powernode.spring6.service.OrderService.*(..))&quot;)</span></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">afterAdvice</span><span class="params">()</span>&#123;</span><br><span class="line">        System.out.println(<span class="string">&quot;YourAspect最终通知&quot;</span>);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br><span class="line"><span class="keyword">package</span> com.powernode.spring6.service;</span><br><span class="line"></span><br><span class="line"><span class="keyword">import</span> org.aspectj.lang.ProceedingJoinPoint;</span><br><span class="line"><span class="keyword">import</span> org.aspectj.lang.annotation.*;</span><br><span class="line"><span class="keyword">import</span> org.springframework.core.annotation.Order;</span><br><span class="line"><span class="keyword">import</span> org.springframework.stereotype.Component;</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="meta">@Aspect</span></span><br><span class="line"><span class="meta">@Order(2)</span> <span class="comment">//设置优先级</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">MyAspect</span> &#123;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Around(&quot;execution(* com.powernode.spring6.service.OrderService.*(..))&quot;)</span></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">aroundAdvice</span><span class="params">(ProceedingJoinPoint proceedingJoinPoint)</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="comment">// 执行目标方法。</span></span><br><span class="line">        proceedingJoinPoint.proceed();</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="meta">@Before(&quot;execution(* com.powernode.spring6.service.OrderService.*(..))&quot;)</span></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">beforeAdvice</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"></span><br><span class="line">    <span class="meta">@AfterReturning(&quot;execution(* com.powernode.spring6.service.OrderService.*(..))&quot;)</span></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">afterReturningAdvice</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"></span><br><span class="line">    <span class="meta">@AfterThrowing(&quot;execution(* com.powernode.spring6.service.OrderService.*(..))&quot;)</span></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">afterThrowingAdvice</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"></span><br><span class="line">    <span class="meta">@After(&quot;execution(* com.powernode.spring6.service.OrderService.*(..))&quot;)</span></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">afterAdvice</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"></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>执行测试程序：</p><p><img src="https://cdn.nlark.com/yuque/0/2022/png/21376908/1665893738167-b3c55a19-6129-4615-813f-9b8dc0f17f40.png?x-oss-process=image%2Fwatermark%2Ctype_d3F5LW1pY3JvaGVp%2Csize_28%2Ctext_5Yqo5Yqb6IqC54K5%2Ccolor_FFFFFF%2Cshadow_50%2Ct_80%2Cg_se%2Cx_10%2Cy_10" alt="img"></p><p>通过修改@Order注解的整数值来切换顺序，执行测试程序：</p><p><img src="https://cdn.nlark.com/yuque/0/2022/png/21376908/1665893833282-2cbc59cc-15a5-44c4-bb20-cbdac65a750d.png?x-oss-process=image%2Fwatermark%2Ctype_d3F5LW1pY3JvaGVp%2Csize_28%2Ctext_5Yqo5Yqb6IqC54K5%2Ccolor_FFFFFF%2Cshadow_50%2Ct_80%2Cg_se%2Cx_10%2Cy_10" alt="img"></p><h4 id="优化使用切点表达式"><a href="#优化使用切点表达式" class="headerlink" title="优化使用切点表达式"></a>优化使用切点表达式</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><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">package</span> com.powernode.spring6.service;</span><br><span class="line"></span><br><span class="line"><span class="keyword">import</span> org.aspectj.lang.ProceedingJoinPoint;</span><br><span class="line"><span class="keyword">import</span> org.aspectj.lang.annotation.*;</span><br><span class="line"><span class="keyword">import</span> org.springframework.core.annotation.Order;</span><br><span class="line"><span class="keyword">import</span> org.springframework.stereotype.Component;</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="meta">@Aspect</span></span><br><span class="line"><span class="meta">@Order(2)</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">MyAspect</span> &#123;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Around(&quot;execution(* com.powernode.spring6.service.OrderService.*(..))&quot;)</span></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">aroundAdvice</span><span class="params">(ProceedingJoinPoint proceedingJoinPoint)</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="comment">// 执行目标方法。</span></span><br><span class="line">        proceedingJoinPoint.proceed();</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="meta">@Before(&quot;execution(* com.powernode.spring6.service.OrderService.*(..))&quot;)</span></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">beforeAdvice</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"></span><br><span class="line">    <span class="meta">@AfterReturning(&quot;execution(* com.powernode.spring6.service.OrderService.*(..))&quot;)</span></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">afterReturningAdvice</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"></span><br><span class="line">    <span class="meta">@AfterThrowing(&quot;execution(* com.powernode.spring6.service.OrderService.*(..))&quot;)</span></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">afterThrowingAdvice</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"></span><br><span class="line">    <span class="meta">@After(&quot;execution(* com.powernode.spring6.service.OrderService.*(..))&quot;)</span></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">afterAdvice</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"></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>缺点是：</p><ul><li>第一：切点表达式重复写了多次，没有得到复用。</li><li>第二：如果要修改切点表达式，需要修改多处，难维护。</li></ul><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></pre></td><td class="code"><pre><span class="line"><span class="keyword">package</span> com.powernode.spring6.service;</span><br><span class="line"></span><br><span class="line"><span class="keyword">import</span> org.aspectj.lang.ProceedingJoinPoint;</span><br><span class="line"><span class="keyword">import</span> org.aspectj.lang.annotation.*;</span><br><span class="line"><span class="keyword">import</span> org.springframework.core.annotation.Order;</span><br><span class="line"><span class="keyword">import</span> org.springframework.stereotype.Component;</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="meta">@Aspect</span></span><br><span class="line"><span class="meta">@Order(2)</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">MyAspect</span> &#123;</span><br><span class="line">    </span><br><span class="line">    <span class="meta">@Pointcut(&quot;execution(* com.powernode.spring6.service.OrderService.*(..))&quot;)</span></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">pointcut</span><span class="params">()</span>&#123;&#125;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Around(&quot;pointcut()&quot;)</span></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">aroundAdvice</span><span class="params">(ProceedingJoinPoint proceedingJoinPoint)</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="comment">// 执行目标方法。</span></span><br><span class="line">        proceedingJoinPoint.proceed();</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="meta">@Before(&quot;pointcut()&quot;)</span></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">beforeAdvice</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"></span><br><span class="line">    <span class="meta">@AfterReturning(&quot;pointcut()&quot;)</span></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">afterReturningAdvice</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"></span><br><span class="line">    <span class="meta">@AfterThrowing(&quot;pointcut()&quot;)</span></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">afterThrowingAdvice</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"></span><br><span class="line">    <span class="meta">@After(&quot;pointcut()&quot;)</span></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">afterAdvice</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"></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>使用@Pointcut注解来定义独立的切点表达式。</p><p>注意这个@Pointcut注解标注的方法随意，只是起到一个能够让@Pointcut注解编写的位置。</p><p>执行测试程序：</p><p><img src="https://cdn.nlark.com/yuque/0/2022/png/21376908/1665893833282-2cbc59cc-15a5-44c4-bb20-cbdac65a750d.png?x-oss-process=image%2Fwatermark%2Ctype_d3F5LW1pY3JvaGVp%2Csize_28%2Ctext_5Yqo5Yqb6IqC54K5%2Ccolor_FFFFFF%2Cshadow_50%2Ct_80%2Cg_se%2Cx_10%2Cy_10" alt="img"></p><h4 id="全注解式开发AOP"><a href="#全注解式开发AOP" class="headerlink" title="全注解式开发AOP"></a>全注解式开发AOP</h4><p>就是编写一个类，在这个类上面使用大量注解来代替spring的配置文件，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></pre></td><td class="code"><pre><span class="line"><span class="keyword">package</span> com.powernode.spring6.service;</span><br><span class="line"></span><br><span class="line"><span class="keyword">import</span> org.springframework.context.annotation.ComponentScan;</span><br><span class="line"><span class="keyword">import</span> org.springframework.context.annotation.Configuration;</span><br><span class="line"><span class="keyword">import</span> org.springframework.context.annotation.EnableAspectJAutoProxy;</span><br><span class="line"></span><br><span class="line"><span class="meta">@Configuration</span></span><br><span class="line"><span class="meta">@ComponentScan(&quot;com.powernode.spring6.service&quot;)</span></span><br><span class="line"><span class="meta">@EnableAspectJAutoProxy(proxyTargetClass = true)</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">Spring6Configuration</span> &#123;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><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></pre></td><td class="code"><pre><span class="line"><span class="meta">@Test</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">testAOPWithAllAnnotation</span><span class="params">()</span>&#123;</span><br><span class="line">    <span class="type">ApplicationContext</span> <span class="variable">applicationContext</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">AnnotationConfigApplicationContext</span>(Spring6Configuration.class);</span><br><span class="line">    <span class="type">OrderService</span> <span class="variable">orderService</span> <span class="operator">=</span> applicationContext.getBean(<span class="string">&quot;orderService&quot;</span>, OrderService.class);</span><br><span class="line">    orderService.generate();</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>执行结果如下：</p><p><img src="https://cdn.nlark.com/yuque/0/2022/png/21376908/1665893833282-2cbc59cc-15a5-44c4-bb20-cbdac65a750d.png?x-oss-process=image%2Fwatermark%2Ctype_d3F5LW1pY3JvaGVp%2Csize_28%2Ctext_5Yqo5Yqb6IqC54K5%2Ccolor_FFFFFF%2Cshadow_50%2Ct_80%2Cg_se%2Cx_10%2Cy_10" alt="img"></p><h3 id="15-4-3-基于XML配置方式的AOP（了解）"><a href="#15-4-3-基于XML配置方式的AOP（了解）" class="headerlink" title="15.4.3 基于XML配置方式的AOP（了解）"></a>15.4.3 基于XML配置方式的AOP（了解）</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></pre></td><td class="code"><pre><span class="line"><span class="keyword">package</span> com.powernode.spring6.service;</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_">VipService</span> &#123;</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>&#123;</span><br><span class="line">        System.out.println(<span class="string">&quot;保存vip信息。&quot;</span>);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><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></pre></td><td class="code"><pre><span class="line"><span class="keyword">package</span> com.powernode.spring6.service;</span><br><span class="line"></span><br><span class="line"><span class="keyword">import</span> org.aspectj.lang.ProceedingJoinPoint;</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_">TimerAspect</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_">time</span><span class="params">(ProceedingJoinPoint proceedingJoinPoint)</span> <span class="keyword">throws</span> Throwable &#123;</span><br><span class="line">        <span class="type">long</span> <span class="variable">begin</span> <span class="operator">=</span> System.currentTimeMillis();</span><br><span class="line">        <span class="comment">//执行目标</span></span><br><span class="line">        proceedingJoinPoint.proceed();</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 - begin)+<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>第三步：编写spring配置文件</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></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">beans</span> <span class="attr">xmlns</span>=<span class="string">&quot;http://www.springframework.org/schema/beans&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">xmlns:context</span>=<span class="string">&quot;http://www.springframework.org/schema/context&quot;</span></span></span><br><span class="line"><span class="tag">       <span class="attr">xmlns:aop</span>=<span class="string">&quot;http://www.springframework.org/schema/aop&quot;</span></span></span><br><span class="line"><span class="tag">       <span class="attr">xsi:schemaLocation</span>=<span class="string">&quot;http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd</span></span></span><br><span class="line"><span class="string"><span class="tag">                           http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd</span></span></span><br><span class="line"><span class="string"><span class="tag">                           http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd&quot;</span>&gt;</span></span><br><span class="line"></span><br><span class="line">    <span class="comment">&lt;!--纳入spring bean管理--&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">bean</span> <span class="attr">id</span>=<span class="string">&quot;vipService&quot;</span> <span class="attr">class</span>=<span class="string">&quot;com.powernode.spring6.service.VipService&quot;</span>/&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">bean</span> <span class="attr">id</span>=<span class="string">&quot;timerAspect&quot;</span> <span class="attr">class</span>=<span class="string">&quot;com.powernode.spring6.service.TimerAspect&quot;</span>/&gt;</span></span><br><span class="line"></span><br><span class="line">    <span class="comment">&lt;!--aop配置--&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">aop:config</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">aop:pointcut</span> <span class="attr">id</span>=<span class="string">&quot;p&quot;</span> <span class="attr">expression</span>=<span class="string">&quot;execution(* com.powernode.spring6.service.VipService.*(..))&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">aop:aspect</span> <span class="attr">ref</span>=<span class="string">&quot;timerAspect&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">aop:around</span> <span class="attr">method</span>=<span class="string">&quot;time&quot;</span> <span class="attr">pointcut-ref</span>=<span class="string">&quot;p&quot;</span>/&gt;</span></span><br><span class="line">        <span class="tag">&lt;/<span class="name">aop:aspect</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;/<span class="name">aop:config</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;/<span class="name">beans</span>&gt;</span></span><br></pre></td></tr></table></figure><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></pre></td><td class="code"><pre><span class="line"><span class="keyword">package</span> com.powernode.spring6.test;</span><br><span class="line"></span><br><span class="line"><span class="keyword">import</span> com.powernode.spring6.service.VipService;</span><br><span class="line"><span class="keyword">import</span> org.junit.Test;</span><br><span class="line"><span class="keyword">import</span> org.springframework.context.ApplicationContext;</span><br><span class="line"><span class="keyword">import</span> org.springframework.context.support.ClassPathXmlApplicationContext;</span><br><span class="line"></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">AOPTest3</span> &#123;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Test</span></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">testAOPXml</span><span class="params">()</span>&#123;</span><br><span class="line">        <span class="type">ApplicationContext</span> <span class="variable">applicationContext</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">ClassPathXmlApplicationContext</span>(<span class="string">&quot;spring-aop-xml.xml&quot;</span>);</span><br><span class="line">        <span class="type">VipService</span> <span class="variable">vipService</span> <span class="operator">=</span> applicationContext.getBean(<span class="string">&quot;vipService&quot;</span>, VipService.class);</span><br><span class="line">        vipService.add();</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>执行结果：</p><p><img src="https://cdn.nlark.com/yuque/0/2022/png/21376908/1665902800121-49540c48-d6c2-4909-874d-e1a485e67ea5.png?x-oss-process=image%2Fwatermark%2Ctype_d3F5LW1pY3JvaGVp%2Csize_23%2Ctext_5Yqo5Yqb6IqC54K5%2Ccolor_FFFFFF%2Cshadow_50%2Ct_80%2Cg_se%2Cx_10%2Cy_10" alt="img"></p><h2 id="15-5-AOP的实际案例：事务处理"><a href="#15-5-AOP的实际案例：事务处理" class="headerlink" title="15.5 AOP的实际案例：事务处理"></a>15.5 AOP的实际案例：事务处理</h2><p>项目中的事务控制是在所难免的。在一个业务流程当中，可能需要多条DML语句共同完成，为了保证数据的安全，这多条DML语句要么同时成功，要么同时失败。这就需要添加事务控制的代码。例如以下伪代码：</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></pre></td><td class="code"><pre><span class="line">class 业务类<span class="number">1</span>&#123;</span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> 业务方法<span class="number">1</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">            startTransaction();</span><br><span class="line">            </span><br><span class="line">            <span class="comment">// 执行核心业务逻辑</span></span><br><span class="line">            step1();</span><br><span class="line">            step2();</span><br><span class="line">            step3();</span><br><span class="line">            ....</span><br><span class="line">            </span><br><span class="line">            <span class="comment">// 提交事务</span></span><br><span class="line">            commitTransaction();</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">            rollbackTransaction();</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> 业务方法<span class="number">2</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">            startTransaction();</span><br><span class="line">            </span><br><span class="line">            <span class="comment">// 执行核心业务逻辑</span></span><br><span class="line">            step1();</span><br><span class="line">            step2();</span><br><span class="line">            step3();</span><br><span class="line">            ....</span><br><span class="line">            </span><br><span class="line">            <span class="comment">// 提交事务</span></span><br><span class="line">            commitTransaction();</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">            rollbackTransaction();</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> 业务方法<span class="number">3</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">            startTransaction();</span><br><span class="line">            </span><br><span class="line">            <span class="comment">// 执行核心业务逻辑</span></span><br><span class="line">            step1();</span><br><span class="line">            step2();</span><br><span class="line">            step3();</span><br><span class="line">            ....</span><br><span class="line">            </span><br><span class="line">            <span class="comment">// 提交事务</span></span><br><span class="line">            commitTransaction();</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">            rollbackTransaction();</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">class 业务类<span class="number">2</span>&#123;</span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> 业务方法<span class="number">1</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">            startTransaction();</span><br><span class="line">            </span><br><span class="line">            <span class="comment">// 执行核心业务逻辑</span></span><br><span class="line">            step1();</span><br><span class="line">            step2();</span><br><span class="line">            step3();</span><br><span class="line">            ....</span><br><span class="line">            </span><br><span class="line">            <span class="comment">// 提交事务</span></span><br><span class="line">            commitTransaction();</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">            rollbackTransaction();</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> 业务方法<span class="number">2</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">            startTransaction();</span><br><span class="line">            </span><br><span class="line">            <span class="comment">// 执行核心业务逻辑</span></span><br><span class="line">            step1();</span><br><span class="line">            step2();</span><br><span class="line">            step3();</span><br><span class="line">            ....</span><br><span class="line">            </span><br><span class="line">            <span class="comment">// 提交事务</span></span><br><span class="line">            commitTransaction();</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">            rollbackTransaction();</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> 业务方法<span class="number">3</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">            startTransaction();</span><br><span class="line">            </span><br><span class="line">            <span class="comment">// 执行核心业务逻辑</span></span><br><span class="line">            step1();</span><br><span class="line">            step2();</span><br><span class="line">            step3();</span><br><span class="line">            ....</span><br><span class="line">            </span><br><span class="line">            <span class="comment">// 提交事务</span></span><br><span class="line">            commitTransaction();</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">            rollbackTransaction();</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="comment">//......</span></span><br></pre></td></tr></table></figure><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></pre></td><td class="code"><pre><span class="line"><span class="keyword">try</span>&#123;</span><br><span class="line">    <span class="comment">// 开启事务</span></span><br><span class="line">    startTransaction();</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">    commitTransaction();</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">    rollbackTransaction();</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>这个控制事务的代码就是和业务逻辑没有关系的“<strong>交叉业务</strong>”。以上伪代码当中可以看到这些交叉业务的代码没有得到复用，并且如果这些交叉业务代码需要修改，那必然需要修改多处，难维护，怎么解决？可以采用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></pre></td><td class="code"><pre><span class="line"><span class="keyword">package</span> com.powernode.spring6.biz;</span><br><span class="line"></span><br><span class="line"><span class="keyword">import</span> org.springframework.stereotype.Component;</span><br><span class="line"></span><br><span class="line"><span class="meta">@Component</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_">AccountService</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_">transfer</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">    <span class="comment">// 取款业务方法</span></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">withdraw</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 class="keyword">package</span> com.powernode.spring6.biz;</span><br><span class="line"></span><br><span class="line"><span class="keyword">import</span> org.springframework.stereotype.Component;</span><br><span class="line"></span><br><span class="line"><span class="meta">@Component</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_">OrderService</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_">generate</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">    <span class="comment">// 取消订单</span></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">cancel</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></pre></td></tr></table></figure><p>注意，以上两个业务类已经纳入spring bean的管理，因为都添加了@Component注解。</p><p>接下来我们给以上两个业务类的4个方法添加事务控制代码，使用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></pre></td><td class="code"><pre><span class="line"><span class="keyword">package</span> com.powernode.spring6.biz;</span><br><span class="line"></span><br><span class="line"><span class="keyword">import</span> org.aspectj.lang.ProceedingJoinPoint;</span><br><span class="line"><span class="keyword">import</span> org.aspectj.lang.annotation.Around;</span><br><span class="line"><span class="keyword">import</span> org.aspectj.lang.annotation.Aspect;</span><br><span class="line"><span class="keyword">import</span> org.springframework.stereotype.Component;</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="comment">// 事务切面类</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">TransactionAspect</span> &#123;</span><br><span class="line">    </span><br><span class="line">    <span class="meta">@Around(&quot;execution(* com.powernode.spring6.biz..*(..))&quot;)</span></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">aroundAdvice</span><span class="params">(ProceedingJoinPoint proceedingJoinPoint)</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>);</span><br><span class="line">            <span class="comment">// 执行目标</span></span><br><span class="line">            proceedingJoinPoint.proceed();</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> (Throwable e) &#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">&#125;</span><br></pre></td></tr></table></figure><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></pre></td><td class="code"><pre><span class="line"><span class="keyword">package</span> com.powernode.spring6.test;</span><br><span class="line"></span><br><span class="line"><span class="keyword">import</span> com.powernode.spring6.biz.AccountService;</span><br><span class="line"><span class="keyword">import</span> com.powernode.spring6.biz.OrderService;</span><br><span class="line"><span class="keyword">import</span> com.powernode.spring6.service.Spring6Configuration;</span><br><span class="line"><span class="keyword">import</span> org.junit.Test;</span><br><span class="line"><span class="keyword">import</span> org.springframework.context.ApplicationContext;</span><br><span class="line"><span class="keyword">import</span> org.springframework.context.annotation.AnnotationConfigApplicationContext;</span><br><span class="line"></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">AOPTest2</span> &#123;</span><br><span class="line">    <span class="meta">@Test</span></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">testTransaction</span><span class="params">()</span>&#123;</span><br><span class="line">        <span class="type">ApplicationContext</span> <span class="variable">applicationContext</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">AnnotationConfigApplicationContext</span>(Spring6Configuration.class);</span><br><span class="line">        <span class="type">OrderService</span> <span class="variable">orderService</span> <span class="operator">=</span> applicationContext.getBean(<span class="string">&quot;orderService&quot;</span>, OrderService.class);</span><br><span class="line">        <span class="type">AccountService</span> <span class="variable">accountService</span> <span class="operator">=</span> applicationContext.getBean(<span class="string">&quot;accountService&quot;</span>, AccountService.class);</span><br><span class="line">        <span class="comment">// 生成订单</span></span><br><span class="line">        orderService.generate();</span><br><span class="line">        <span class="comment">// 取消订单</span></span><br><span class="line">        orderService.cancel();</span><br><span class="line">        <span class="comment">// 转账</span></span><br><span class="line">        accountService.transfer();</span><br><span class="line">        <span class="comment">// 取款</span></span><br><span class="line">        accountService.withdraw();</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>执行结果：</p><p><img src="https://cdn.nlark.com/yuque/0/2022/png/21376908/1665899075177-6660d491-06d4-4296-b69a-b54716179c9d.png?x-oss-process=image%2Fwatermark%2Ctype_d3F5LW1pY3JvaGVp%2Csize_24%2Ctext_5Yqo5Yqb6IqC54K5%2Ccolor_FFFFFF%2Cshadow_50%2Ct_80%2Cg_se%2Cx_10%2Cy_10" alt="img"></p><p>通过测试可以看到，所有的业务方法都添加了事务控制的代码。</p><h2 id="15-6-AOP的实际案例：安全日志"><a href="#15-6-AOP的实际案例：安全日志" class="headerlink" title="15.6 AOP的实际案例：安全日志"></a>15.6 AOP的实际案例：安全日志</h2><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></pre></td><td class="code"><pre><span class="line"><span class="keyword">package</span> com.powernode.spring6.biz;</span><br><span class="line"></span><br><span class="line"><span class="keyword">import</span> org.springframework.stereotype.Component;</span><br><span class="line"></span><br><span class="line"><span class="meta">@Component</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 class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">getUser</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">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">saveUser</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">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">deleteUser</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">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">modifyUser</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></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="keyword">package</span> com.powernode.spring6.biz;</span><br><span class="line"></span><br><span class="line"><span class="keyword">import</span> org.springframework.stereotype.Component;</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_">ProductService</span> &#123;</span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">getProduct</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">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">saveProduct</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">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">deleteProduct</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">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">modifyProduct</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></pre></td></tr></table></figure><p>注意：已经添加了@Component注解。</p><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></pre></td><td class="code"><pre><span class="line"><span class="keyword">package</span> com.powernode.spring6.biz;</span><br><span class="line"></span><br><span class="line"><span class="keyword">import</span> org.aspectj.lang.JoinPoint;</span><br><span class="line"><span class="keyword">import</span> org.aspectj.lang.annotation.Aspect;</span><br><span class="line"><span class="keyword">import</span> org.aspectj.lang.annotation.Before;</span><br><span class="line"><span class="keyword">import</span> org.aspectj.lang.annotation.Pointcut;</span><br><span class="line"><span class="keyword">import</span> org.springframework.stereotype.Component;</span><br><span class="line"></span><br><span class="line"><span class="meta">@Component</span></span><br><span class="line"><span class="meta">@Aspect</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">SecurityAspect</span> &#123;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Pointcut(&quot;execution(* com.powernode.spring6.biz..save*(..))&quot;)</span></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">savePointcut</span><span class="params">()</span>&#123;&#125;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Pointcut(&quot;execution(* com.powernode.spring6.biz..delete*(..))&quot;)</span></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">deletePointcut</span><span class="params">()</span>&#123;&#125;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Pointcut(&quot;execution(* com.powernode.spring6.biz..modify*(..))&quot;)</span></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">modifyPointcut</span><span class="params">()</span>&#123;&#125;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Before(&quot;savePointcut() || deletePointcut() || modifyPointcut()&quot;)</span></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">beforeAdivce</span><span class="params">(JoinPoint joinpoint)</span>&#123;</span><br><span class="line">        System.out.println(<span class="string">&quot;XXX操作员正在操作&quot;</span>+joinpoint.getSignature().getName()+<span class="string">&quot;方法&quot;</span>);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br><span class="line"><span class="meta">@Test</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">testSecurity</span><span class="params">()</span>&#123;</span><br><span class="line">    <span class="type">ApplicationContext</span> <span class="variable">applicationContext</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">AnnotationConfigApplicationContext</span>(Spring6Configuration.class);</span><br><span class="line">    <span class="type">UserService</span> <span class="variable">userService</span> <span class="operator">=</span> applicationContext.getBean(<span class="string">&quot;userService&quot;</span>, UserService.class);</span><br><span class="line">    <span class="type">ProductService</span> <span class="variable">productService</span> <span class="operator">=</span> applicationContext.getBean(<span class="string">&quot;productService&quot;</span>, ProductService.class);</span><br><span class="line">    userService.getUser();</span><br><span class="line">    userService.saveUser();</span><br><span class="line">    userService.deleteUser();</span><br><span class="line">    userService.modifyUser();</span><br><span class="line">    productService.getProduct();</span><br><span class="line">    productService.saveProduct();</span><br><span class="line">    productService.deleteProduct();</span><br><span class="line">    productService.modifyProduct();</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>执行结果：</p>]]></content>
    
    
      
      
    <summary type="html">&lt;h1 id=&quot;Spring-Ioc注解式开发&quot;&gt;&lt;a href=&quot;#Spring-Ioc注解式开发&quot; class=&quot;headerlink&quot; title=&quot;Spring Ioc注解式开发&quot;&gt;&lt;/a&gt;Spring Ioc注解式开发&lt;/h1&gt;&lt;h2 id=&quot;回顾注解&quot;&gt;&lt;a href</summary>
      
    
    
    
    
    <category term="Spring" scheme="https://silvan.chat/tags/Spring/"/>
    
    <category term="Java" scheme="https://silvan.chat/tags/Java/"/>
    
  </entry>
  
  <entry>
    <title>SpringBoot核心机制</title>
    <link href="https://silvan.chat/2025/10/02/Springboot-SpringBoot%E6%A0%B8%E5%BF%83%E6%9C%BA%E5%88%B6/"/>
    <id>https://silvan.chat/2025/10/02/Springboot-SpringBoot%E6%A0%B8%E5%BF%83%E6%9C%BA%E5%88%B6/</id>
    <published>2025-10-02T03:50:41.221Z</published>
    <updated>2025-10-03T05:53:13.379Z</updated>
    
    <content type="html"><![CDATA[<h1 id="为什么以继承的方式引入SpringBoot"><a href="#为什么以继承的方式引入SpringBoot" class="headerlink" title="为什么以继承的方式引入SpringBoot"></a>为什么以继承的方式引入SpringBoot</h1><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>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.3.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><h2 id="作为父项目和作为依赖的区别："><a href="#作为父项目和作为依赖的区别：" class="headerlink" title="作为父项目和作为依赖的区别："></a>作为父项目和作为依赖的区别：</h2><p><strong>继承父工程的优势:</strong></p><ul><li><code>依赖管理</code>：可以在父工程中定义依赖的版本，子模块可以直接引用而不必指定版本号。</li><li><code>插件管理</code>：可以在父工程中配置常用的插件及其版本，子模块可以直接使用这些配置。</li><li><code>属性设置</code>：可以在父工程中定义一些通用的属性，如项目编码、Java 版本等。</li><li><code>统一配置</code>：可以统一多个子模块的构建配置，确保一致性。</li></ul><p><strong>直接引入依赖的局限性</strong>（如果你不使用继承父工程的方式，而是通过直接引入依赖的方式来管理项目，那么你将失去上述的一些优势）</p><ul><li><code>依赖版本管理</code>：每个子模块都需要单独指定依赖的版本，这会导致大量的重复配置，并且难以维护。</li><li><code>插件配置</code>：每个子模块都需要单独配置插件及其版本，无法共享父工程中的插件配置。</li><li><code>属性设置</code>：每个子模块都需要单独设置通用的属性，如项目编码、Java 版本等。</li><li><code>构建配置</code>：每个子模块的构建配置需要单独维护，难以保证一致性。<div class="note info no-icon flat"><p><strong>总结：选择哪种方式取决于你的具体需求。</strong></p><ul><li><strong>如果你希望多个项目之间共享构建配置，那么使用父项目是一个好的选择；</strong></li><li><strong>如果你只是想在项目之间共享代码，那么应该使用依赖关系。</strong></li></ul></div></li></ul><p><strong>Spring Boot预先对开发中需要用到的依赖进行了版本的统一管理。我们需要和SpringBoot框架共享这个构建配置。因此官方推荐使用继承的方式引入SpringBoot框架。</strong></p><h2 id="依赖统一管理的好处"><a href="#依赖统一管理的好处" class="headerlink" title="依赖统一管理的好处"></a>依赖统一管理的好处</h2><p>Spring Boot 框架的一个重要特性就是<code>简化项目依赖管理</code>。它通过提供一个叫做<code>依赖管理</code>的功能来帮助开发者更容易地管理和使用第三方库和其他Spring组件。具体来说，SpringBoot 提供了一个包含多个Spring和其他常用库的依赖版本配置文件（通常是在 <code>spring-boot-dependencies</code> 文件中），这使得开<strong>发者不需要在自己的项目中显式指定这些依赖的版本号</strong>。</p><p>这样做有以下几个好处：</p><ul><li><p><strong>简化依赖声明</strong>：<br> 开发者只需要在 <code>pom.xml</code> 文件中声明需要的依赖而不需要指定其版本号，因为 Spring Boot 已经为这些依赖指定了版本。例如，如果你需要使用mysql驱动，你只需要添加相应的依赖声明而不需要关心版本。</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>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">dependency</span>&gt;</span></span><br></pre></td></tr></table></figure></li><li><p><strong>避免版本冲突</strong>：<br> 当多个库之间存在依赖关系的时候，如果手动管理版本可能会导致版本之间的冲突（即“依赖地狱”）。Spring Boot 提供的统一版本管理可以减少这种冲突的可能性。</p></li><li><strong>易于升级</strong>：<br> 当 Spring Boot 发布新版本时，通常会更新其依赖库到最新稳定版。因此，当你升级 Spring Boot 版本时，它所管理的所有依赖也会随之更新到兼容的版本。</li><li><strong>减少配置错误</strong>：<br> 由于 Spring Boot 自动处理了依赖的版本，减少了手动输入版本号可能引入的拼写或格式错误。</li><li><strong>提高开发效率</strong>：<br> 开发者可以专注于业务逻辑的编写，而不是花费时间在解决依赖问题上。</li></ul><div class="note danger no-icon flat"><ul><li>总的来说，Spring Boot 的依赖管理功能使得开发者可以更加专注于业务逻辑的实现，同时减少了因依赖版本不一致而引发的问题，提高了项目的可维护性和开发效率。</li><li>如果在项目中需要更改某个依赖的版本号，不想使用SpringBoot框架指定的版本号，只需要在引入依赖时强行执行版本号即可，maven支持就近原则</li></ul></div><h1 id="Starter-启动器"><a href="#Starter-启动器" class="headerlink" title="Starter-启动器"></a>Starter-启动器</h1><p>在SpringBoot中，<code>启动器（Starter）</code>本质上是一个简化依赖管理的概念。<br>Spring Boot 的启动器本质上就是一组预定义的依赖集合，它们被组织成一个个Maven的依赖，以方便开发者快速集成特定的功能模块。<br>如果你想做web开发，只需要引入web启动器。web启动器会自动引入web开发所需要的子依赖。</p><h2 id="启动器实现原理"><a href="#启动器实现原理" class="headerlink" title="启动器实现原理"></a>启动器实现原理</h2><ol><li><strong>依赖聚合</strong>：<br>每个启动器通常对应一个特定的功能集或者一个完整的应用模块，如 <code>spring-boot-starter-web</code> 就包含了构建 Web 应用所需的所有基本依赖项，如 Spring MVC, Tomcat 嵌入式容器等。</li><li><strong>依赖传递</strong>：<br>当你在项目中引入一个启动器时，它不仅会把自身作为依赖加入到你的项目中，还会把它的所有直接依赖项（transitive dependencies）也加入进来。这意味着你不需要单独声明这些依赖项，它们会自动成为项目的一部分。</li><li><strong>版本管理</strong>：<br>启动器内部已经指定了所有依赖项的具体版本，这些版本信息存储在一个公共的 BOM（Bill of Materials，物料清单）文件中，通常是 <code>spring-boot-dependencies</code>。当引入启动器时，实际上也间接引用了这个 BOM，从而确保了所有依赖项版本的一致性。</li><li><strong>自动配置</strong>：<br>许多启动器还提供了<code>自动配置</code>（Auto-configuration），这是一种机制，允许 SpringBoot 根据类路径上的可用组件自动设置你的应用程序。例如，如果类路径上有 Spring MVC 和嵌入式 Tomcat，则 Spring Boot 会自动配置它们，并准备好一个 web 应用程序。</li></ol><h2 id="有哪些启动器"><a href="#有哪些启动器" class="headerlink" title="有哪些启动器"></a>有哪些启动器</h2><p>启动器通常包括：</p><ul><li>SpringBoot官方提供的启动器</li><li>非官方提供的启动器</li></ul><div class="tabs" id="启动器"><ul class="nav-tabs"><li class="tab active"><button type="button" data-href="#启动器-1">官方提供的启动器</button></li><li class="tab"><button type="button" data-href="#启动器-2">非官方提供的启动器</button></li></ul><div class="tab-contents"><div class="tab-item-content active" id="启动器-1"><p>启动器命名特点：spring-boot-starter-*</p><div class="img-wrap"><div class="img-bg"><img class="img" src="https://cdn.nlark.com/yuque/0/2024/png/21376908/1729328350698-5231f924-4ae0-447b-a1af-f2c25e4f8440.png?x-oss-process=image%2Fformat%2Cwebp"/></div></div><button type="button" class="tab-to-top" aria-label="scroll to top"><i class="fas fa-arrow-up"></i></button></div><div class="tab-item-content" id="启动器-2"><p>启动器命名特点：*-spring-boot-starter</p><div class="img-wrap"><div class="img-bg"><img class="img" src="https://cdn.nlark.com/yuque/0/2024/png/21376908/1729328504925-f0aa6730-ee7f-4a1d-85f9-3d2c9075318f.png?x-oss-process=image%2Fformat%2Cwebp"/></div></div><button type="button" class="tab-to-top" aria-label="scroll to top"><i class="fas fa-arrow-up"></i></button></div></div></div><h1 id="SpringBoot核心注解"><a href="#SpringBoot核心注解" class="headerlink" title="SpringBoot核心注解"></a>SpringBoot核心注解</h1><p>创建一个只有Web启动器的新模块，并添加一个Controller类。</p><h2 id="SpringBootApplication"><a href="#SpringBootApplication" class="headerlink" title="@SpringBootApplication"></a>@SpringBootApplication</h2><ul><li>SpringBoot的主入口就被<code>@SpringBootApplication</code>注解标注，可见该注解的重要性。<br>注解源码：<div class="img-wrap"><div class="img-bg"><img class="img" src="https://cdn.nlark.com/yuque/0/2024/png/21376908/1729563192417-c03008ef-81f9-4741-ad09-42d49a4b2cc9.png?x-oss-process=image%2Fformat%2Cwebp"/></div></div></li><li>该注解属于<code>组合注解</code>。拥有<code>@SpringBootConfiguration</code>、<code>@EnableAutoConfiguration</code>、<code>@ComponentScan</code>的功能。<h2 id="SpringBootConfiguration"><a href="#SpringBootConfiguration" class="headerlink" title="@SpringBootConfiguration"></a>@SpringBootConfiguration</h2>注解源码：<div class="img-wrap"><div class="img-bg"><img class="img" src="https://cdn.nlark.com/yuque/0/2024/png/21376908/1729563436496-752f56df-52aa-404b-bf83-c122a06f1312.png?x-oss-process=image%2Fformat%2Cwebp"/></div></div></li><li>能够发现该注解又被<code>@Configuration</code>注解标注，这说明<code>主入口</code>的程序本质是一个配置类，所以<code>主入口</code>的方法可以被<code>@Bean</code>注解标注，被<code>@Bean</code>注解的标注的方法会被Spring容器自动调用，并且将该方法的返回对象纳入IoC容器的管理。</li></ul><div class="tabs" id="测试configuration"><ul class="nav-tabs"><li class="tab active"><button type="button" data-href="#测试configuration-1">测试configuration</button></li><li class="tab"><button type="button" data-href="#测试configuration-2">测试结果</button></li></ul><div class="tab-contents"><div class="tab-item-content active" id="测试configuration-1"><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><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">@SpringBootApplication</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">Sb305CoreApplication</span> &#123;</span><br><span class="line">    <span class="meta">@Bean</span></span><br><span class="line">    <span class="keyword">public</span> Date <span class="title function_">getNowDate</span><span class="params">()</span>&#123; <span class="comment">// 方法名作为bean的id</span></span><br><span class="line">        <span class="keyword">return</span> <span class="keyword">new</span> <span class="title class_">Date</span>();</span><br><span class="line">    &#125;</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">ConfigurableApplicationContext</span> <span class="variable">applicationContext</span> <span class="operator">=</span> SpringApplication.run(Sb305CoreApplication.class, args);</span><br><span class="line">        <span class="type">Date</span> <span class="variable">dateBean1</span> <span class="operator">=</span> applicationContext.getBean(Date.class);</span><br><span class="line">        System.out.println(dateBean1);</span><br><span class="line">        <span class="type">Date</span> <span class="variable">dateBean2</span> <span class="operator">=</span> applicationContext.getBean(<span class="string">&quot;getNowDate&quot;</span>, Date.class);</span><br><span class="line">        System.out.println(dateBean2);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><button type="button" class="tab-to-top" aria-label="scroll to top"><i class="fas fa-arrow-up"></i></button></div><div class="tab-item-content" id="测试configuration-2"><div class="img-wrap"><div class="img-bg"><img class="img" src="https://cdn.nlark.com/yuque/0/2024/png/21376908/1729564458157-1d953623-9405-4577-8fa4-ed955038be77.png?x-oss-process=image%2Fformat%2Cwebp"/></div></div><button type="button" class="tab-to-top" aria-label="scroll to top"><i class="fas fa-arrow-up"></i></button></div></div></div><div class="note warning no-icon flat"><p>这个<code>配置类</code>也可以称为<code>源</code>，起源的意思，<code>SpringBoot</code>从这个配置类开始加载项目中所有的<code>bean</code>。</p></div><h2 id="EnableAutoConfiguration"><a href="#EnableAutoConfiguration" class="headerlink" title="@EnableAutoConfiguration"></a>@EnableAutoConfiguration</h2><ul><li>该注解表示<code>启用自动配置</code>。</li><li><strong>Spring Boot 会根据引入的依赖自动配置好一系列的 Bean，无需手动编写复杂的配置代码。</strong></li></ul><p>例如：在SpringBoot中进行了以下配置：<br><figure class="highlight java"><table><tr><td class="gutter"><pre><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">spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver</span><br><span class="line">spring.datasource.url=jdbc:mysql:<span class="comment">//localhost:3306/springboot</span></span><br><span class="line">spring.datasource.username=root</span><br><span class="line">spring.datasource.password=<span class="number">123456</span></span><br></pre></td></tr></table></figure><br>并且在依赖中引入了<code>mybatis依赖</code>/<code>mybatis启动器</code>，那么SpringBoot框架将为你自动化配置以下bean：</p><ul><li><strong>SqlSessionFactory</strong>: MyBatis的核心工厂SqlSessionFactory会被自动配置。这个工厂负责创建SqlSession实例，后者用来执行映射文件中的SQL语句。</li><li><strong>TransactionManager</strong>: DataSourceTransactionManager会被自动配置来管理与数据源相关的事务。</li></ul><h2 id="ComponentScan注解"><a href="#ComponentScan注解" class="headerlink" title="@ComponentScan注解"></a>@ComponentScan注解</h2><p>这个注解的作用是：启动组件扫描功能，代替spring框架xml文件中这个配置：<br><figure class="highlight xml"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="tag">&lt;<span class="name">context:component-scan</span> <span class="attr">base-package</span>=<span class="string">&quot;com.powernode.sb305core&quot;</span>/&gt;</span></span><br></pre></td></tr></table></figure><br><div class="note info no-icon flat"><p>因此被<code>@SpringBootApplication</code>注解标注之后，会自动扫描<code>主入口程序所在包及子包</code>，因此如果一个bean要纳入IoC容器的管理则必须放到主入口程序所在包及子包下。放到主入口程序所在包之外的话，扫描不到。测试一下：</p></div><br><div class="tabs" id="测试componentscan"><ul class="nav-tabs"><li class="tab active"><button type="button" data-href="#测试componentscan-1">扫描到</button></li><li class="tab"><button type="button" data-href="#测试componentscan-2">扫描不到</button></li></ul><div class="tab-contents"><div class="tab-item-content active" id="测试componentscan-1"><div class="img-wrap"><div class="img-bg"><img class="img" src="https://cdn.nlark.com/yuque/0/2024/png/21376908/1729565961938-3cbe2276-79a3-4a57-ab29-d2cd3153d5f8.png?x-oss-process=image%2Fformat%2Cwebp"/></div></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></pre></td><td class="code"><pre><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_">HelloController</span> &#123;</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>&#123;</span><br><span class="line">        <span class="keyword">return</span> <span class="string">&quot;hello world!&quot;</span>;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>启动服务器测试：</p><div class="img-wrap"><div class="img-bg"><img class="img" src="https://cdn.nlark.com/yuque/0/2024/png/21376908/1729566015788-bfbce42f-90b2-48cf-b583-4490db6da625.png?x-oss-process=image%2Fformat%2Cwebp"/></div></div><button type="button" class="tab-to-top" aria-label="scroll to top"><i class="fas fa-arrow-up"></i></button></div><div class="tab-item-content" id="测试componentscan-2"><ul><li>此时UserController不在主入口程序所在包及子包下<div class="img-wrap"><div class="img-bg"><img class="img" src="https://cdn.nlark.com/yuque/0/2024/png/21376908/1729566119617-fd950fb7-d8a1-42f7-8ff0-e1d7fab60409.png?x-oss-process=image%2Fformat%2Cwebp"/></div></div></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></pre></td><td class="code"><pre><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_">UserController</span> &#123;</span><br><span class="line">    <span class="meta">@GetMapping(&quot;/list&quot;)</span></span><br><span class="line">    <span class="keyword">public</span> String <span class="title function_">list</span><span class="params">()</span>&#123;</span><br><span class="line">        <span class="keyword">return</span> <span class="string">&quot;user list!&quot;</span>;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>启动服务器测试：</p><p><div class="img-wrap"><div class="img-bg"><img class="img" src="https://cdn.nlark.com/yuque/0/2024/png/21376908/1729566187896-1d4053bd-d5da-4fd4-a243-ebd80388ac17.png?x-oss-process=image%2Fformat%2Cwebp"/></div>&lt;/div&gt;<br>通过测试得知UserController没有被纳入IoC容器的管理。</p><button type="button" class="tab-to-top" aria-label="scroll to top"><i class="fas fa-arrow-up"></i></button></div></div></div></p><div class="note danger no-icon flat"><p><strong>最终结论：要让Bean纳入IoC容器的管理，必须将类放到主入口程序同级目录下，或者子目录下。</strong></p></div><h1 id="SpringBoot的单元测试"><a href="#SpringBoot的单元测试" class="headerlink" title="SpringBoot的单元测试"></a>SpringBoot的单元测试</h1><h2 id="不使用单元测试调用Service"><a href="#不使用单元测试调用Service" class="headerlink" title="不使用单元测试调用Service"></a>不使用单元测试调用Service</h2><h3 id="第一步：创建模块"><a href="#第一步：创建模块" class="headerlink" title="第一步：创建模块"></a>第一步：创建模块</h3><ul><li>使用脚手架创建一个模块，该模块不添加任何启动器<div class="img-wrap"><div class="img-bg"><img class="img" src="https://cdn.nlark.com/yuque/0/2024/png/21376908/1729581295420-d78e7122-66d3-43da-9ba0-a5dea864cf7a.png?x-oss-process=image%2Fformat%2Cwebp"/></div></div><h3 id="第二步：编写Service"><a href="#第二步：编写Service" class="headerlink" title="第二步：编写Service"></a>第二步：编写Service</h3><div class="img-wrap"><div class="img-bg"><img class="img" src="https://cdn.nlark.com/yuque/0/2024/png/21376908/1729581533729-d56983cc-f11b-4f3a-ac12-cbc28989d0b9.png?x-oss-process=image%2Fformat%2Cwebp"/></div></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></pre></td><td class="code"><pre><span class="line"><span class="meta">@Service(&quot;userService&quot;)</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 class="meta">@Override</span></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">save</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></pre></td></tr></table></figure><h3 id="第三步：调用Service"><a href="#第三步：调用Service" class="headerlink" title="第三步：调用Service"></a>第三步：调用Service</h3></li><li>直接在入口程序调用service<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><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="keyword">public</span> <span class="keyword">class</span> <span class="title class_">Sb306TestApplication</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">ConfigurableApplicationContext</span> <span class="variable">applicationContext</span> <span class="operator">=</span> SpringApplication.run(Sb306TestApplication.class, args);</span><br><span class="line">        <span class="type">UserService</span> <span class="variable">userService</span> <span class="operator">=</span> applicationContext.getBean(<span class="string">&quot;userService&quot;</span>, UserService.class);</span><br><span class="line">        userService.save();</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>执行结果：<div class="img-wrap"><div class="img-bg"><img class="img" src="https://cdn.nlark.com/yuque/0/2024/png/21376908/1729581624703-aa5dfd5f-1a61-48a9-bce3-9d3d615913e5.png?x-oss-process=image%2Fformat%2Cwebp"/></div></div></li></ul><div class="note warning no-icon flat"><p>该方法是手动获取Spring上下文对象<code>ConfigurableApplicationContext</code>，然后通过getBean方法获取bean，从Spring容器中获取Service对象，然后调用方法。</p></div><h2 id="使用单元测试调用Service"><a href="#使用单元测试调用Service" class="headerlink" title="使用单元测试调用Service"></a>使用单元测试调用Service</h2><h3 id="引入test-starter"><a href="#引入test-starter" class="headerlink" title="引入test-starter"></a>引入test-starter</h3><ul><li><p>SpringBoot在创建项目时就为我们生成了单元测试类，但如果要使用单元测试，还需要引入单元测试的启动器，如果使用脚手架创建SpringBoot项目，这个test启动器会自动引入</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>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></pre></td></tr></table></figure><div class="img-wrap"><div class="img-bg"><img class="img" src="https://cdn.nlark.com/yuque/0/2024/png/21376908/1729581986594-85a62eb9-67d3-4637-a9de-d2578bc1b55f.png?x-oss-process=image%2Fformat%2Cwebp"/></div></div><h3 id="SpringBootTest注解"><a href="#SpringBootTest注解" class="headerlink" title="@SpringBootTest注解"></a>@SpringBootTest注解</h3><p><code>@SpringBootTest</code> 会创建一个完整的 Spring 应用程序上下文（Application Context），这个上下文包含了应用程序的所有组件和服务。以下是 <code>@SpringBootTest</code> 做的一些主要工作：</p></li><li><p><strong>创建 ApplicationContext</strong>：</p><ul><li><code>@SpringBootTest</code> 使用 <code>SpringApplication</code> 的 <code>run()</code> 方法来启动一个 Spring Boot 应用程序上下文。这意味着它会加载应用程序的主配置类和其他相关的配置类。</li></ul></li><li><p><strong>加载配置文件</strong>：</p><ul><li>它会查找并加载默认的配置文件，如 <code>application.properties</code></li></ul></li><li><p><strong>自动配置</strong>：</p><ul><li>如果应用程序依赖于 Spring Boot 的自动配置特性，<code>@SpringBootTest</code> 会确保这些自动配置生效。这意味着它会根据可用的类和bean来自动配置一些组件，如数据库连接、消息队列等。</li></ul></li><li><p><strong>注入依赖</strong>：</p><ul><li>使用 <code>@SpringBootTest</code> 创建的应用程序上下文允许你在测试类中使用 <code>@Autowired</code> 注入需要的 bean，就像在一个真实的 Spring Boot 应用程序中一样。</li></ul></li></ul><p>总的来说，<code>@SpringBootTest</code> 为你的测试提供了尽可能接近实际运行时环境的条件，这对于验证应用程序的行为非常有用。</p><h3 id="注入Service并调用"><a href="#注入Service并调用" class="headerlink" title="注入Service并调用"></a>注入Service并调用</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="meta">@SpringBootTest</span></span><br><span class="line"><span class="keyword">class</span> <span class="title class_">Sb306TestApplicationTests</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">@Test</span></span><br><span class="line">    <span class="keyword">void</span> <span class="title function_">contextLoads</span><span class="params">()</span> &#123;</span><br><span class="line">        userService.save();</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>测试结果如下：<br><div class="img-wrap"><div class="img-bg"><img class="img" src="https://cdn.nlark.com/yuque/0/2024/png/21376908/1729582782987-88067365-fba6-4240-b704-b2f710a96647.png?x-oss-process=image%2Fformat%2Cwebp"/></div></div></p><h1 id="外部化配置"><a href="#外部化配置" class="headerlink" title="外部化配置"></a>外部化配置</h1><h2 id="什么是外部化配置？"><a href="#什么是外部化配置？" class="headerlink" title="什么是外部化配置？"></a>什么是外部化配置？</h2><p><code>外部化配置</code>：将配置信息存储在应用程序代码之外的地方。这样配置信息可以独立于代码进行管理。这样方便了配置的修改，并且修改后不需要重新编译代码，也不需要重新部署项目。<br><a href="https://docs.spring.io/spring-boot/reference/features/profiles.html">官方文档</a></p><h3 id="5-1-1-外部化配置的方式"><a href="#5-1-1-外部化配置的方式" class="headerlink" title="5.1.1. 外部化配置的方式"></a>5.1.1. 外部化配置的方式</h3><p>SpringBoot支持多种外部化配置方式，包括但不限于：</p><ul><li>properties文件</li><li>YAML文件</li><li>系统环境变量</li><li>命令行参数</li><li>……</li></ul><h3 id="5-1-2-外部化配置的优势"><a href="#5-1-2-外部化配置的优势" class="headerlink" title="5.1.2. 外部化配置的优势"></a>5.1.2. 外部化配置的优势</h3><ol><li><strong>灵活性</strong>：配置文件可以独立于应用程序部署，这使得可以根据运行环境的不同来调整配置，而无需修改代码。</li><li><strong>易于维护</strong>：配置变更不需要重新构建和部署应用程序，降低了维护成本。</li><li><strong>安全性</strong>：敏感信息如数据库密码、API密钥等可以存储在外部，并且可以限制谁有权限访问这些配置信息。</li><li><strong>共享性</strong>：多实例或多服务可以共享相同的配置信息，减少重复配置的工作量。</li><li><strong>版本控制</strong>：配置文件可以存放在版本控制系统中，便于跟踪历史版本和回滚配置。</li></ol><p>总之，外部化配置使得配置更加灵活、安全、易于管理和共享，是现代云原生应用中非常推荐的做法</p><h3 id="5-1-3-外部化配置对比传统配置"><a href="#5-1-3-外部化配置对比传统配置" class="headerlink" title="5.1.3. 外部化配置对比传统配置"></a>5.1.3. 外部化配置对比传统配置</h3><ul><li>在传统的SSM三大框架中，如果修改XML的配置后，需要对应用<code>重新打包，重新部署</code>。</li><li>使用SpringBoot框架的<code>外部化配置</code>后，修改配置后，不需要对应用重新打包，也不需要重新部署，最多重启一下服务即可。</li></ul><h2 id="5-2-properties文件"><a href="#5-2-properties文件" class="headerlink" title="5.2. properties文件"></a>5.2. properties文件</h2><ul><li><code>application.properties</code>配置文件是SpringBoot框架默认的配置文件。</li><li><code>application.properties</code>可以不配置，因为SpringBoot为我们提供了一套默认配置。</li><li>如果你要改变这些默认的行为，可以在<code>application.properties</code>文件中进行配置。</li><li><code>application.properties</code>可以放在类路径当中，也可以放在项目之外。因此称为<code>外部化配置</code>。</li></ul><p>Spring Boot 框架在启动时会尝试从以下位置加载 <code>application.properties</code> 配置文件：</p><ol><li><strong>file:./config/</strong>：首先在Spring Boot 当前工作目录下的 <code>config</code> 文件夹中查找。<ul><li><strong>注意：如果没有找到</strong><code>**application.properties**</code><strong>会继续找</strong><code>**application.yml**</code><strong>，如果这两个都没有找到，才会进入以下位置查找，以此类推。</strong></li></ul></li><li><strong>file:./</strong>：如果在当前工作目录下<code>config</code>目录中找不到时，再从当前工作目录中查找。</li><li><strong>classpath:/config/</strong>：如果从工作目录中找不到，会从类路径中找，先从类路径的 <code>/config/</code> 目录下寻找配置文件。</li><li><strong>classpath:/</strong>：如果在 <code>/config/</code> 下没有找到，它会在类路径的根目录下查找。</li></ol><p>Spring Boot 会按照这个顺序来加载配置文件，如果在多个位置有相同的属性定义，那么最先检查的位置中的属性值将优先使用。</p><p>如果你想要指定其他的配置文件位置或者改变默认的行为，可以通过 —spring.config.location= 后跟路径的方式来指定配置文件的具体位置。例如 ：<br><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">java -jar sb3-01-first-web-1.0-SNAPSHOT.jar --spring.config.location=file:///E:\a\b\application.properties</span><br></pre></td></tr></table></figure><br>此时SpringBoot会首先从<code>E:\a\b</code>这个路径加载配置文件。这种方式可以用来覆盖默认的配置文件。<br><div class="note warning no-icon flat"><p><strong>注意：以上的—spring.config.location=file:///E:\a\b\application.properties就属于命令行参数，它将来会被传递到main方法的(String[] args)参数上。</strong></p></div></p><div class="note danger no-icon flat"><p><strong>优先级总结：</strong><br><strong><code>application.properties/yml (classpath)</code> &lt; <code>application.properties/yml (外部路径)</code> &lt; <code>profile 配置文件</code> &lt; <code>环境变量 &amp; JVM 参数</code> &lt; <code>命令行参数</code></strong></p></div>]]></content>
    
    
    <summary type="html">SpringBoot核心机制</summary>
    
    
    
    
    <category term="Java" scheme="https://silvan.chat/tags/Java/"/>
    
    <category term="SpringBoot" scheme="https://silvan.chat/tags/SpringBoot/"/>
    
  </entry>
  
  <entry>
    <title>SpringBoot入门</title>
    <link href="https://silvan.chat/2025/10/01/Springboot-SpringBoot/"/>
    <id>https://silvan.chat/2025/10/01/Springboot-SpringBoot/</id>
    <published>2025-10-01T10:53:18.710Z</published>
    <updated>2025-10-02T09:06:31.830Z</updated>
    
    <content type="html"><![CDATA[<h1 id="认识SpringBoot"><a href="#认识SpringBoot" class="headerlink" title="认识SpringBoot"></a>认识SpringBoot</h1><ul><li>官方这样描述SpringBoot：<a href="https://docs.spring.io/spring-boot/index.html">SpringBoot</a><div class="img-wrap"><div class="img-bg"><img class="img" src="https://cdn.nlark.com/yuque/0/2024/png/21376908/1728893860338-bce86e2c-719d-49fe-9932-419fe1e50fb1.png?x-oss-process=image%2Fformat%2Cwebp"/></div></div></li><li><code>SpringBoot</code>提倡<code>约定优于配置</code>，将<code>简化开发</code>发挥到极致。通过SpringBoot来实现跳过大量繁琐的配置，快速构建Spring应用。</li><li><p>SpringBoot框架的设计目标：<strong>程序员只需要关注业务逻辑，环境配置交给SpringBoot。</strong></p></li><li><p>SpringBoot的<code>特性</code>：</p><ul><li>快速创建独立的Spring应用程序（Spring支持的SpringBoot都支持）</li><li>嵌入式的Tomcat、jetty、Undertow等容器</li><li>需要的功能直接引入对应的starter启动器即可。（启动器自动管理该功能需要的相关依赖，包括版本冲突等）</li><li>尽最大努力自动配置Spring应用和第三方库。（例如，进行事务管理时，不需要任何配置，只需要在Service类上添加@Transactional注解即可）</li><li>没有代码生成器，没有XML配置文件（SpringBoot的应用程序启动后不会动态创建新的Java类，所有逻辑在编译期就创建好了）</li><li>提供了生产监控的支持，也集成了外部监控系统。</li></ul></li><li><p>SpringBoot的<code>开箱即用</code>和<code>约定优于配置</code>：</p></li><li><strong>开箱即用：</strong>Spring Boot框架设计得非常便捷，开发者能够在几乎不需要任何复杂的配置的情况下，快速搭建并运行一个功能完备的Spring应用。</li><li><strong>约定优于配置：</strong><code>约定优于配置</code>（Convention Over Configuration, CoC）是一种软件设计哲学，核心思想是<strong>通过提供一组合理的默认行为来减少配置的数量，从而简化开发流程。</strong>例如：SpringBoot默认约定了使用某个事务管理器，在事务方面不需要做任何配置，只需要在对应代码的位置上使用@Transactional注解即可。</li></ul><h1 id="学习前提"><a href="#学习前提" class="headerlink" title="学习前提"></a>学习前提</h1><p>学习SpringBoot应至少学过：</p><ul><li>Spring</li><li>SpringMVC</li><li>Mybatis</li></ul><h1 id="第一个SpringBoot项目"><a href="#第一个SpringBoot项目" class="headerlink" title="第一个SpringBoot项目"></a>第一个SpringBoot项目</h1><div class="note danger no-icon flat"><p>需求：在浏览器输入<a href="http://localhost:8080/hello，返回&quot;Hello">http://localhost:8080/hello，返回&quot;Hello</a> World”</p></div><p><strong>使用SpirngBoot3开发Web项目，Spring官方提供的步骤如下：</strong></p><h2 id="第一步：创建空工程"><a href="#第一步：创建空工程" class="headerlink" title="第一步：创建空工程"></a>第一步：创建空工程</h2><ul><li>创建一个空的工程，并设置JDK版本为21（SpringBoot3要求最低版本为17）</li></ul><div class="tabs" id="第一步"><ul class="nav-tabs"><li class="tab active"><button type="button" data-href="#第一步-1">创建空工程</button></li></ul><div class="tab-contents"><div class="tab-item-content active" id="第一步-1"><div class="img-wrap"><div class="img-bg"><img class="img" src="https://cdn.nlark.com/yuque/0/2024/png/21376908/1726108555353-e1941b34-dc63-4767-954c-4f6e5e3a9aa8.png?x-oss-process=image%2Fformat%2Cwebp"/></div></div><button type="button" class="tab-to-top" aria-label="scroll to top"><i class="fas fa-arrow-up"></i></button></div></div></div><h2 id="第二步：设置Maven"><a href="#第二步：设置Maven" class="headerlink" title="第二步：设置Maven"></a>第二步：设置Maven</h2><ul><li>如果以前配置过Maven，可以跳过此步骤<div class="img-wrap"><div class="img-bg"><img class="img" src="https://cdn.nlark.com/yuque/0/2024/png/21376908/1726109769284-7a5cae72-e931-4fd3-bf1c-c99a37cb4023.png?x-oss-process=image%2Fformat%2Cwebp"/></div></div></li></ul><h2 id="第三步：创建Maven项目"><a href="#第三步：创建Maven项目" class="headerlink" title="第三步：创建Maven项目"></a>第三步：创建Maven项目</h2><div class="img-wrap"><div class="img-bg"><img class="img" src="https://cdn.nlark.com/yuque/0/2024/png/21376908/1726110490826-f7c563eb-4aff-488e-83dc-8daf82a700e9.png?x-oss-process=image%2Fformat%2Cwebp"/></div></div><h2 id="第四步：根据SpringBoot3文档操作"><a href="#第四步：根据SpringBoot3文档操作" class="headerlink" title="第四步：根据SpringBoot3文档操作"></a>第四步：根据SpringBoot3文档操作</h2><p><a href="https://docs.spring.io/spring-boot/tutorial/first-application/index.html">官方文档</a></p><div class="img-wrap"><div class="img-bg"><img class="img" src="https://cdn.nlark.com/yuque/0/2024/png/21376908/1726111411632-0a5cfc79-f222-4395-ae19-35b8d3868f3d.png?x-oss-process=image%2Fformat%2Cwebp"/></div></div><h2 id="第五步：继承SpringBoot"><a href="#第五步：继承SpringBoot" class="headerlink" title="第五步：继承SpringBoot"></a>第五步：继承SpringBoot</h2><p>要使用Spring Boot 3，需要继承这个开源项目。从官方指导文档中复制以下内容：<br><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;!--继承Spring Boot 3.3.3开源项目--&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.3.3<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></pre></td></tr></table></figure></p><div class="img-wrap"><div class="img-bg"><img class="img" src="https://cdn.nlark.com/yuque/0/2024/png/21376908/1726111736067-221c85d6-e3ce-4d41-9b20-2893705b11de.png?x-oss-process=image%2Fformat%2Cwebp"/></div></div><div class="note warning no-icon flat"><p>我们开发的每一个项目都可以看作是Springboot项目下的子项目</p></div><h2 id="第六步：添加Web启动器"><a href="#第六步：添加Web启动器" class="headerlink" title="第六步：添加Web启动器"></a>第六步：添加Web启动器</h2><p>添加SpringBootweb的启动器，让SpringBoot项目具备开发Web应用的依赖：<br><div class="tabs" id="web1"><ul class="nav-tabs"><li class="tab active"><button type="button" data-href="#web1-1">官方文档</button></li><li class="tab"><button type="button" data-href="#web1-2">添加依赖</button></li></ul><div class="tab-contents"><div class="tab-item-content active" id="web1-1"><div class="img-wrap"><div class="img-bg"><img class="img" src="https://cdn.nlark.com/yuque/0/2024/png/21376908/1726112199621-1d90cda6-1bb5-4c66-ac9f-b0f2bedb87b3.png?x-oss-process=image%2Fformat%2Cwebp"/></div></div><button type="button" class="tab-to-top" aria-label="scroll to top"><i class="fas fa-arrow-up"></i></button></div><div class="tab-item-content" id="web1-2"><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="tag">&lt;<span class="name">dependencies</span>&gt;</span></span><br><span class="line">    <span class="comment">&lt;!--引入Spring Boot 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.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">dependencies</span>&gt;</span></span><br></pre></td></tr></table></figure><button type="button" class="tab-to-top" aria-label="scroll to top"><i class="fas fa-arrow-up"></i></button></div></div></div></p><ul><li>可以看到Web的关联依赖也会被引入进来，包括SpringMVC和Tomcat<div class="img-wrap"><div class="img-bg"><img class="img" src="https://cdn.nlark.com/yuque/0/2024/png/21376908/1726112541453-f0298719-9217-4792-9af5-bb39d6582560.png?x-oss-process=image%2Fformat%2Cwebp"/></div></div></li></ul><h2 id="第七步：创建主程序"><a href="#第七步：创建主程序" class="headerlink" title="第七步：创建主程序"></a>第七步：创建主程序</h2><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><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="meta">@SpringBootApplication</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">MyApplication</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(MyApplication.class, args);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h2 id="第八步：创建Controller"><a href="#第八步：创建Controller" class="headerlink" title="第八步：创建Controller"></a>第八步：创建Controller</h2><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><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">@Controller</span></span><br><span class="line"><span class="meta">@ResponseBody</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">MyController</span> &#123;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@RequestMapping(&quot;/hello&quot;)</span></span><br><span class="line">    <span class="keyword">public</span> String <span class="title function_">index</span><span class="params">()</span>&#123;</span><br><span class="line">        <span class="keyword">return</span> <span class="string">&quot;Hello World!&quot;</span>;</span><br><span class="line">    &#125;</span><br><span class="line">    </span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h2 id="第九步：启动项目"><a href="#第九步：启动项目" class="headerlink" title="第九步：启动项目"></a>第九步：启动项目</h2><ul><li>运行Main方法就是启动Web项目了</li></ul><div class="img-wrap"><div class="img-bg"><img class="img" src="https://cdn.nlark.com/yuque/0/2024/png/21376908/1726197869192-497ee49e-fffb-4201-9c20-6caa97823963.png?x-oss-process=image%2Fformat%2Cwebp"/></div></div><h2 id="第十步：访问项目"><a href="#第十步：访问项目" class="headerlink" title="第十步：访问项目"></a>第十步：访问项目</h2><ul><li>直接打开浏览器，访问<a href="http://localhost:8080/hello">http://localhost:8080/hello</a>，返回”Hello World”</li></ul><h1 id="便捷的部署方式"><a href="#便捷的部署方式" class="headerlink" title="便捷的部署方式"></a>便捷的部署方式</h1><h2 id="打jar包运行"><a href="#打jar包运行" class="headerlink" title="打jar包运行"></a>打jar包运行</h2><p><strong>SpringBoot为我们提供了打包插件，可以将SpringBoot项目打包为可执行 jar 包。</strong><code>Web服务器</code>（Tomcat）也会连同一块打入jar包中。只要电脑上安装了Java的运行环境（JDK），就可以启动Spring Boot项目。<br><div class="img-wrap"><div class="img-bg"><img class="img" src="https://cdn.nlark.com/yuque/0/2024/png/21376908/1726199029529-6ffc59e1-3d92-4d4e-ab44-31f24cba9585.png?x-oss-process=image%2Fformat%2Cwebp"/></div></div></p><ul><li>根据官方文档步骤，引入打包插件<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></pre></td><td class="code"><pre><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></pre></td></tr></table></figure></li><li>执行打包命令，生成可执行Jar包</li></ul>]]></content>
    
    
    <summary type="html">SpringBoot快速入门</summary>
    
    
    
    
    <category term="Java" scheme="https://silvan.chat/tags/Java/"/>
    
    <category term="SpringBoot" scheme="https://silvan.chat/tags/SpringBoot/"/>
    
  </entry>
  
  <entry>
    <title>Spring(2)</title>
    <link href="https://silvan.chat/2025/09/28/Spring-Spring-2/"/>
    <id>https://silvan.chat/2025/09/28/Spring-Spring-2/</id>
    <published>2025-09-28T09:49:18.948Z</published>
    <updated>2025-10-03T07:17:49.539Z</updated>
    
    <content type="html"><![CDATA[<h1 id="GoF之工厂模式"><a href="#GoF之工厂模式" class="headerlink" title="GoF之工厂模式"></a>GoF之工厂模式</h1><h2 id="了解GoF"><a href="#了解GoF" class="headerlink" title="了解GoF"></a>了解GoF</h2><ul><li><code>设计模式</code>是一种可以反复利用的解决<code>方案</code></li><li>GoF（Gang of Four），中文名——四人组。<ul><li>《Design Patterns: Elements of Reusable Object-Oriented Software》（即《设计模式》一书），1995年由 Erich Gamma、Richard Helm、Ralph Johnson 和 John Vlissides 合著。这几位作者常被称为”四人组（Gang of Four）”。</li></ul></li><li>常提到的23种设计模式，在这本书都能找到。除了GoF提到的23种设计模式，起始还有其他的设计模式，比如：JavaEE的设计模式（DAO模式、MVC模式等）。</li><li>GoF中的23种设计模式可以分为三大类：</li></ul><ul><li><strong>创建型</strong>（5个）：解决对象创建问题。<ul><li>单例模式</li><li>工厂方法模式</li><li>抽象工厂模式</li><li>建造者模式</li><li>原型模式  </li></ul></li><li><strong>结构型</strong>（7个）：一些类或对象组合在一起的经典结构。<ul><li>代理模式</li><li>装饰模式</li><li>适配器模式</li><li>组合模式</li><li>享元模式</li><li>外观模式</li><li>桥接模式</li></ul></li><li><strong>行为型</strong>（11个）：解决类或对象之间的交互问题。<ul><li>策略模式</li><li>模板方法模式</li><li>责任链模式</li><li>观察者模式</li><li>迭代子模式</li><li>命令模式</li><li>备忘录模式</li><li>状态模式</li><li>访问者模式</li><li>中介者模式</li><li>解释器模式</li></ul></li></ul><div class="note danger no-icon flat"><ul><li>工厂模式是解决对象创建问题的，所以工厂模式属于<code>创建型设计模式</code>。</li><li>为什么在这里学习工厂模式呢？<ul><li>这是因为Spring框架底层使用了大量的工厂模式。</li></ul></li></ul></div><h2 id="工厂模式的三种形态"><a href="#工厂模式的三种形态" class="headerlink" title="工厂模式的三种形态"></a>工厂模式的三种形态</h2><ul><li>第一种：<code>简单工厂模式（Simple Factory）</code>：不属于23种设计模式之一。简单工厂模式又叫做：静态工厂方法模式。简单工厂模式是工厂方法模式的一种特殊实现。</li><li>第二种：<code>工厂方法模式（Factory Method）</code>：是23种设计模式之一。</li><li>第三种：<code>抽象工厂模式（Abstract Factory）</code>：是23种设计模式之一。</li></ul><h2 id="简单工厂模式"><a href="#简单工厂模式" class="headerlink" title="简单工厂模式"></a>简单工厂模式</h2><ul><li>简单工厂模式角色包括三个：<ul><li>抽象产品角色</li><li>具体产品角色</li><li>工厂类角色<div class="tabs" id="简单工厂模式"><ul class="nav-tabs"><li class="tab active"><button type="button" data-href="#简单工厂模式-1">抽象产品角色</button></li><li class="tab"><button type="button" data-href="#简单工厂模式-2">具体产品角色</button></li><li class="tab"><button type="button" data-href="#简单工厂模式-3">工厂类角色</button></li></ul><div class="tab-contents"><div class="tab-item-content active" id="简单工厂模式-1"><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><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="keyword">public</span> <span class="keyword">abstract</span> <span class="keyword">class</span> <span class="title class_">Weapon</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"><span class="comment">     */</span></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">abstract</span> <span class="keyword">void</span> <span class="title function_">attack</span><span class="params">()</span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><button type="button" class="tab-to-top" aria-label="scroll to top"><i class="fas fa-arrow-up"></i></button></div><div class="tab-item-content" id="简单工厂模式-2"><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><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_">Tank</span> <span class="keyword">extends</span> <span class="title class_">Weapon</span>&#123;</span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">attack</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></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></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">Fighter</span> <span class="keyword">extends</span> <span class="title class_">Weapon</span>&#123;</span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">attack</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></pre></td></tr></table></figure><button type="button" class="tab-to-top" aria-label="scroll to top"><i class="fas fa-arrow-up"></i></button></div><div class="tab-item-content" id="简单工厂模式-3"><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><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_">WeaponFactory</span> &#123;</span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">static</span> Weapon <span class="title function_">get</span><span class="params">(String weaponType)</span>&#123;</span><br><span class="line">        <span class="keyword">if</span> (weaponType == <span class="literal">null</span> || weaponType.trim().length() == <span class="number">0</span>)</span><br><span class="line">            <span class="keyword">return</span> <span class="literal">null</span>;</span><br><span class="line">        <span class="type">Weapon</span> <span class="variable">weapon</span> <span class="operator">=</span> <span class="literal">null</span>;</span><br><span class="line">        <span class="keyword">if</span> (<span class="string">&quot;TANK&quot;</span>.equals(weaponType)) &#123;</span><br><span class="line">            weapon = <span class="keyword">new</span> <span class="title class_">Tank</span>();</span><br><span class="line">        &#125; <span class="keyword">else</span> <span class="keyword">if</span> (<span class="string">&quot;FIGHTER&quot;</span>.equals(weaponType)) &#123;</span><br><span class="line">            weapon = <span class="keyword">new</span> <span class="title class_">Fighter</span>();</span><br><span class="line">        &#125; <span class="keyword">else</span> <span class="keyword">if</span> (<span class="string">&quot;DAGGER&quot;</span>.equals(weaponType)) &#123;</span><br><span class="line">            weapon = <span class="keyword">new</span> <span class="title class_">Dagger</span>();</span><br><span class="line">        &#125; <span class="keyword">else</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 class="keyword">return</span> weapon;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><button type="button" class="tab-to-top" aria-label="scroll to top"><i class="fas fa-arrow-up"></i></button></div></div></div><div class="note warning no-icon flat"><p>简单工厂模式的优点：</p><ul><li>客户端程序不需要关心对象的创建细节，需要哪个对象时，只需要向工厂索要即可，初步实现了责任的分离。客户端只负责“消费”，工厂负责“生产”。<strong>生产和消费分离。</strong></li></ul><p>简单工厂模式的缺点：</p><ul><li>工厂类集中了所有产品的创造逻辑，形成一个无所不知的全能类，有人把它叫做上帝类。显然工厂类非常关键，不能出问题，一旦出问题，整个系统瘫痪。</li><li><strong>不符合OCP开闭原则</strong>。在进行系统扩展时，需要修改工厂类。</li></ul></div><h2 id="工厂方法模式"><a href="#工厂方法模式" class="headerlink" title="工厂方法模式"></a>工厂方法模式</h2></li></ul></li><li><p>工厂方法模式整体与简单工厂类似。既保留了简单工厂模式的优点，同时又解决了简单工厂模式的缺点。</p></li><li><p>工厂方法模式角色：  </p><ul><li>抽象产品角色</li><li>具体产品角色</li><li><strong>抽象工厂角色</strong></li><li><strong>具体工厂角色</strong></li></ul></li></ul><div class="tabs" id=""><ul class="nav-tabs"><li class="tab active"><button type="button" data-href="#-1">抽象产品角色</button></li><li class="tab"><button type="button" data-href="#-2">具体产品角色</button></li><li class="tab"><button type="button" data-href="#-3">抽象工厂角色</button></li><li class="tab"><button type="button" data-href="#-4">具体工厂角色</button></li><li class="tab"><button type="button" data-href="#-5">测试程序</button></li></ul><div class="tab-contents"><div class="tab-item-content active" id="-1"><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><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="keyword">public</span> <span class="keyword">abstract</span> <span class="keyword">class</span> <span class="title class_">Weapon</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"><span class="comment">     */</span></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">abstract</span> <span class="keyword">void</span> <span class="title function_">attack</span><span class="params">()</span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><button type="button" class="tab-to-top" aria-label="scroll to top"><i class="fas fa-arrow-up"></i></button></div><div class="tab-item-content" id="-2"><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><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="keyword">public</span> <span class="keyword">class</span> <span class="title class_">Gun</span> <span class="keyword">extends</span> <span class="title class_">Weapon</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_">attack</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></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></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">Fighter</span> <span class="keyword">extends</span> <span class="title class_">Weapon</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_">attack</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></pre></td></tr></table></figure><button type="button" class="tab-to-top" aria-label="scroll to top"><i class="fas fa-arrow-up"></i></button></div><div class="tab-item-content" id="-3"><figure class="highlight java"><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="keyword">public</span> <span class="keyword">interface</span> <span class="title class_">WeaponFactory</span> &#123;</span><br><span class="line">    Weapon <span class="title function_">get</span><span class="params">()</span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><button type="button" class="tab-to-top" aria-label="scroll to top"><i class="fas fa-arrow-up"></i></button></div><div class="tab-item-content" id="-4"><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><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="keyword">public</span> <span class="keyword">class</span> <span class="title class_">GunFactory</span> <span class="keyword">implements</span> <span class="title class_">WeaponFactory</span>&#123;</span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="keyword">public</span> Weapon <span class="title function_">get</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_">Gun</span>();</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></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">FighterFactory</span> <span class="keyword">implements</span> <span class="title class_">WeaponFactory</span>&#123;</span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="keyword">public</span> Weapon <span class="title function_">get</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_">Fighter</span>();</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><button type="button" class="tab-to-top" aria-label="scroll to top"><i class="fas fa-arrow-up"></i></button></div><div class="tab-item-content" id="-5"><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><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="keyword">public</span> <span class="keyword">class</span> <span class="title class_">Client</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">WeaponFactory</span> <span class="variable">factory</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">GunFactory</span>();</span><br><span class="line">        <span class="type">Weapon</span> <span class="variable">weapon</span> <span class="operator">=</span> factory.get();</span><br><span class="line">        weapon.attack();</span><br><span class="line"></span><br><span class="line">        <span class="type">WeaponFactory</span> <span class="variable">factory1</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">FighterFactory</span>();</span><br><span class="line">        <span class="type">Weapon</span> <span class="variable">weapon1</span> <span class="operator">=</span> factory1.get();</span><br><span class="line">        weapon1.attack();</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><button type="button" class="tab-to-top" aria-label="scroll to top"><i class="fas fa-arrow-up"></i></button></div></div></div><ul><li>此时想要扩展产品，只需要新增一个产品类和该产品对应的工厂即可，例如新增匕首类</li></ul><div class="tabs" id="dagger"><ul class="nav-tabs"><li class="tab active"><button type="button" data-href="#dagger-1">匕首类</button></li><li class="tab"><button type="button" data-href="#dagger-2">匕首工厂类</button></li></ul><div class="tab-contents"><div class="tab-item-content active" id="dagger-1"><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><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="keyword">public</span> <span class="keyword">class</span> <span class="title class_">Dagger</span> <span class="keyword">extends</span> <span class="title class_">Weapon</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_">attack</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></pre></td></tr></table></figure><button type="button" class="tab-to-top" aria-label="scroll to top"><i class="fas fa-arrow-up"></i></button></div><div class="tab-item-content" id="dagger-2"><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><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="keyword">public</span> <span class="keyword">class</span> <span class="title class_">DaggerFactory</span> <span class="keyword">implements</span> <span class="title class_">WeaponFactory</span>&#123;</span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="keyword">public</span> Weapon <span class="title function_">get</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_">Dagger</span>();</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><button type="button" class="tab-to-top" aria-label="scroll to top"><i class="fas fa-arrow-up"></i></button></div></div></div><div class="note warning no-icon flat"><p>工厂方法模式的优点：</p><ul><li>一个调用者想创建一个对象，只要知道其名称就可以了。 </li><li>扩展性高，如果想增加一个产品，只要扩展一个工厂类就可以。</li><li>屏蔽产品的具体实现，调用者只关心产品的接口。<br>工厂方法模式的缺点：</li><li>每次增加一个产品时，都需要增加一个具体类和对象实现工厂，使得系统中类的个数成倍增加，在一定程度上增加了系统的复杂度，同时也增加了系统具体类的依赖。这并不是什么好事。</li></ul></div><h1 id="Bean的实例化方式"><a href="#Bean的实例化方式" class="headerlink" title="Bean的实例化方式"></a>Bean的实例化方式</h1><p>Spring为Bean提供了多种实例化方式，通常包括4种方式。（也就是说在Spring中为Bean对象的创建准备了多种方案，目的：使Bean的实例更加灵活）</p><ul><li>第一种：通过<code>构造方法</code>实例化</li><li>第二种：通过<code>简单工厂模式</code>实例化</li><li>第三种：通过<code>BeanFactory</code>实例化</li><li>第四种：通过<code>FactoryBean</code>接口实例化</li></ul><h2 id="BeanFactory和FactoryBean的区别"><a href="#BeanFactory和FactoryBean的区别" class="headerlink" title="BeanFactory和FactoryBean的区别"></a>BeanFactory和FactoryBean的区别</h2><ul><li><code>BeanFactory</code>：是Spring IoC容器的顶级对象，常常被翻译为Bean工厂，用于创建和管理Bean对象。<strong>BeanFactory是工厂</strong></li><li><code>FactoryBean</code>：是Spring的一个工厂Bean，是一个能够辅助Spring实例化其他Bean对象的一个Bean。</li></ul><div class="note danger no-icon flat"><p>在Spring中，Bean可以分为两类：</p><ul><li>第一类：普通Bean</li><li>第二类：工厂Bean（记住：工厂Bean也是一种Bean，只不过这种Bean比较特殊，它可以辅助Spring实例化其它Bean对象。）</li></ul></div><h1 id="Bean的生命周期"><a href="#Bean的生命周期" class="headerlink" title="Bean的生命周期"></a>Bean的生命周期</h1><h2 id="什么是Bean的生命周期？"><a href="#什么是Bean的生命周期？" class="headerlink" title="什么是Bean的生命周期？"></a>什么是Bean的生命周期？</h2><p>Spring就是一个管理Bean对象的工厂。它负责对象的创建，对象的销毁等。<br>所谓的生命周期就是：<strong>对象从创建开始到最终销毁的整个过程。</strong></p><ul><li>什么时候创建Bean对象？</li><li>创建Bean对象的前后会调用什么方法？</li><li>Bean对象什么时候销毁？</li><li>Bean对象的销毁前后调用什么方法？</li></ul><h2 id="为什么要了解Bean的生命周期？"><a href="#为什么要了解Bean的生命周期？" class="headerlink" title="为什么要了解Bean的生命周期？"></a>为什么要了解Bean的生命周期？</h2><p>生命周期的本质：<strong>在哪个时间节点上调用了哪个类的哪个方法。</strong><br>我们需要充分的了解在这个生命线上，都有哪些特殊的时间节点。<br>只有我们知道了特殊的时间节点都在哪，才可以准确的确定代码写到哪。<br>我们可能需要在某个特殊的时间点上执行一段特定的代码，这段代码就可以放到这个节点上。当生命线走到这里的时候，就会被自动调用。</p><h2 id="Bean的生命周期之5步"><a href="#Bean的生命周期之5步" class="headerlink" title="Bean的生命周期之5步"></a>Bean的生命周期之5步</h2><ol><li>实例化Bean</li><li>Bean属性赋值</li><li>初始化Bean</li><li>使用Bean</li><li>销毁Bean</li></ol><div class="note warning no-icon flat"><ul><li>只有正常关闭spring容器，bean的销毁方法才会被调用。</li><li>ClassPathXmlApplicationContext类才有close()方法。`</li><li>配置文件中的init-method指定初始化方法。destroy-method指定销毁方法。</li></ul></div><h2 id="Bean的生命周期之7步"><a href="#Bean的生命周期之7步" class="headerlink" title="Bean的生命周期之7步"></a>Bean的生命周期之7步</h2><ol><li>实例化Bean</li><li>Bean属性赋值</li><li><code>Bean后处理器的before方法</code></li><li>初始化Bean</li><li><code>Bean后处理器的after方法</code></li><li>使用Bean</li><li>销毁Bean</li></ol><ul><li>编写一个类实现BeanPostProcessor类，并重写before和after方法<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><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_">LogBeanPostProcessor</span> <span class="keyword">implements</span> <span class="title class_">BeanPostProcessor</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_">postProcessBeforeInitialization</span><span class="params">(Object bean, String beanName)</span> <span class="keyword">throws</span> BeansException &#123;</span><br><span class="line">        System.out.println(<span class="string">&quot;Bean后处理器的before方法执行，即将开始初始化&quot;</span>);</span><br><span class="line">        <span class="keyword">return</span> bean;</span><br><span class="line">    &#125;</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_">postProcessAfterInitialization</span><span class="params">(Object bean, String beanName)</span> <span class="keyword">throws</span> BeansException &#123;</span><br><span class="line">        System.out.println(<span class="string">&quot;Bean后处理器的after方法执行，已完成初始化&quot;</span>);</span><br><span class="line">        <span class="keyword">return</span> bean;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></li></ul><div class="note warning no-icon flat"><p>在spring.xml文件中配置的Bean后处理器将作用于当前配置文件中所有的Bean。</p></div><p>在spring.xml文件中配置Bean后处理器<br><figure class="highlight xml"><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">&lt;!--配置Bean后处理器。这个后处理器将作用于当前配置文件中所有的bean。--&gt;</span></span><br><span class="line"><span class="tag">&lt;<span class="name">bean</span> <span class="attr">class</span>=<span class="string">&quot;com.powernode.spring6.bean.LogBeanPostProcessor&quot;</span>/&gt;</span></span><br></pre></td></tr></table></figure></p><h2 id="Bean的生命周期之10步（了解）"><a href="#Bean的生命周期之10步（了解）" class="headerlink" title="Bean的生命周期之10步（了解）"></a>Bean的生命周期之10步（了解）</h2><ol><li>实例化Bean</li><li>Bean属性赋值</li><li><code>检查Bean是否实现了Aware相关接口，并设置相关依赖</code></li><li><strong>Bean后处理器的before方法</strong></li><li><code>检查Bean是否实现了InitializingBean接口，并调用接口方法</code></li><li>初始化Bean</li><li><strong>Bean后处理器的after方法</strong></li><li>使用Bean</li><li><code>检查Bean是否实现了DisposableBean接口，并调用接口方法</code></li><li>销毁Bean</li></ol><p>Aware相关的接口包括：<code>BeanNameAware</code>、<code>BeanClassLoaderAware</code>、<code>BeanFactoryAware</code></p><ul><li>当Bean实现了BeanNameAware，Spring会将Bean的名字传递给Bean。</li><li>当Bean实现了BeanClassLoaderAware，Spring会将加载该Bean的类加载器传递给Bean。</li><li>当Bean实现了BeanFactoryAware，Spring会将Bean工厂对象传递给Bean。</li></ul><div class="note warning no-icon flat"><ul><li>InitializingBean的方法早于init-method的执行。</li><li>DisposableBean的方法早于destroy-method的执行。</li></ul></div><div class="note danger no-icon flat"><p>对于Bean不同的作用域，Spring有不同的管理方式</p><ul><li>单例：Spring精确的知到Bean在何时创建，何时初始化，何时被销毁。</li><li>多例：Spring只负责创建，当Bean被创建后，Bean的实例就交给客户端代码管理，Spring不再追踪其生命周期。</li></ul></div><h2 id="将自己new的对象交给Spring管理"><a href="#将自己new的对象交给Spring管理" class="headerlink" title="将自己new的对象交给Spring管理"></a>将自己new的对象交给Spring管理</h2><ul><li>Spring为我们创建的对象，它的生命周期由Spring管理。我们如何将自己new的对象交给Spring管理呢？</li></ul><h3 id="通过-Bean注解"><a href="#通过-Bean注解" class="headerlink" title="通过@Bean注解"></a>通过@Bean注解</h3><ul><li>创建一个配置类，在配置类中定义一个方法，该方法返回值为要注册的Bean对象。</li><li>在方法上添加@Bean注解。<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><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="meta">@Configuration</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">MyConfig</span> &#123;</span><br><span class="line">    <span class="meta">@Bean</span></span><br><span class="line">    <span class="keyword">public</span> MyService <span class="title function_">myService</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_">MyService</span>(); <span class="comment">// 手动 new，再交给 Spring</span></span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h3 id="通过DefaultListableBeanFactory"><a href="#通过DefaultListableBeanFactory" class="headerlink" title="通过DefaultListableBeanFactory"></a>通过DefaultListableBeanFactory</h3>可以通过<code>BeanDefinitionRegistryPostProcessor</code>或 <code>BeanFactoryPostProcessor</code>注册</li></ul><div class="tabs" id="my-tabs"><ul class="nav-tabs"><li class="tab active"><button type="button" data-href="#my-tabs-1">User</button></li></ul><div class="tab-contents"><div class="tab-item-content active" id="my-tabs-1"><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="keyword">public</span> <span class="keyword">class</span> <span class="title class_">User</span>&#123;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><button type="button" class="tab-to-top" aria-label="scroll to top"><i class="fas fa-arrow-up"></i></button></div></div></div><h3 id="使用ApplicationContext"><a href="#使用ApplicationContext" class="headerlink" title="使用ApplicationContext"></a>使用ApplicationContext</h3><p>在运行时获取 <code>ConfigurableApplicationContext</code>，调用其 API 注册：<br><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">ConfigurableApplicationContext</span> <span class="variable">context</span> <span class="operator">=</span> (ConfigurableApplicationContext) applicationContext;</span><br><span class="line">context.getBeanFactory().registerSingleton(<span class="string">&quot;myService&quot;</span>, <span class="keyword">new</span> <span class="title class_">MyService</span>());</span><br></pre></td></tr></table></figure></p><h3 id="总结"><a href="#总结" class="headerlink" title="总结"></a>总结</h3><div class="table-container"><table><thead><tr><th>方式</th><th>优点</th><th>缺点</th><th>典型场景</th></tr></thead><tbody><tr><td><strong><code>@Bean</code> 注册</strong></td><td>简单直观，最推荐；支持依赖注入；易于维护</td><td>需要提前在配置类中写死</td><td>常规手动 new 对象交给 Spring 管理</td></tr><tr><td><strong><code>BeanFactoryPostProcessor</code> / <code>registerSingleton</code></strong></td><td>可在容器启动时动态注册 Bean；灵活性高</td><td>代码复杂，理解成本高；不常用</td><td>启动阶段根据条件动态注册 Bean</td></tr><tr><td><strong>运行时 <code>ApplicationContext.registerSingleton</code></strong></td><td>可以在运行时动态注册；方便临时扩展</td><td>容器已启动时使用，可能影响一致性</td><td>运行过程中需要临时创建并托管对象</td></tr><tr><td><strong><code>@Component</code> 扫描</strong></td><td>完全交给 Spring 扫描管理；简洁</td><td>不能满足“必须自己手动 new”的需求</td><td>适合完全由 Spring 托管的类</td></tr></tbody></table></div><div class="note danger no-icon flat"><ul><li><p>如果只是单纯要把 new 出来的对象交给 Spring → @Bean 是最佳实践。</p></li><li><p>如果对象的创建需要 运行时条件 或 外部输入 → 使用 registerSingleton。</p></li><li><p>如果在做 框架级开发，要批量、动态注册 → 用 ImportBeanDefinitionRegistrar。</p></li></ul></div><h1 id="Bean的循环依赖"><a href="#Bean的循环依赖" class="headerlink" title="Bean的循环依赖"></a>Bean的循环依赖</h1><ul><li>A对象中有B属性。B对象中有A属性。这就是循环依赖。我依赖你，你也依赖我。<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><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">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 class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">setB</span><span class="params">(B b)</span> &#123; <span class="built_in">this</span>.b = b; &#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_">B</span> &#123;</span><br><span class="line">    <span class="keyword">private</span> A a;</span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">setA</span><span class="params">(A a)</span> &#123; <span class="built_in">this</span>.a = a; &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></li><li><strong>此时<code>A</code>依赖<code>B</code>，<code>B</code>又依赖<code>A</code>，普通的构造过程就会出现<code>死循环</code>。</strong></li></ul><h2 id="单例下的set注入产生的循环依赖"><a href="#单例下的set注入产生的循环依赖" class="headerlink" title="单例下的set注入产生的循环依赖"></a>单例下的set注入产生的循环依赖</h2><div class="note warning no-icon flat"><ul><li>singleton+Set注入的情况下，循环依赖是没有问题的。Spring可以解决这个问题<ul><li>Spring在创建单例Bean的时候，有一个<code>三级缓存机制</code>，提前曝光正在创建的对象引用，让另一个依赖方能够拿到这个对象的”半成品”（还未完全填充依赖的对象）。</li></ul></li><li>创建过程：<ul><li>Spring开始创建单例<code>A</code><ul><li>在实例化<code>A</code>后(还未注入属性)，Spring就把<code>ObjectFactory(A)</code>放到<code>singletonFactories</code>中。</li></ul></li><li>Spring发现<code>A</code>需要依赖<code>B</code>，开始创建<code>B</code></li><li>创建<code>B</code>时发现<code>B</code>需要依赖<code>A</code><ul><li>此时Spring会去<code>singletonFactories</code>中获取A的<code>ObjectFactory</code>，之后调用它生成一个<code>earlySingletonReference</code>（早期引用），这个引用会先放入<code>earlySingletonObjects</code>，然后B再依赖这个引用。</li><li>这样B就拿到了一个A的引用(还没有完成依赖注入)。</li></ul></li><li>B完成创建后也会放入<code>singletonObjects</code>。</li><li>回到A的注入阶段，Spring将完整的B注入到A中，此时A彻底完成初始化，重新放入<code>singletonObjects</code>中。</li><li>此时A和B全部完成了初始化</li></ul></li></ul></div><div class="tabs" id=""><ul class="nav-tabs"><li class="tab active"><button type="button" data-href="#-1">Husband</button></li><li class="tab"><button type="button" data-href="#-2">Wife</button></li><li class="tab"><button type="button" data-href="#-3">配置文件</button></li></ul><div class="tab-contents"><div class="tab-item-content active" id="-1"><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><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_">Husband</span> &#123;</span><br><span class="line">    <span class="keyword">private</span> String name;</span><br><span class="line">    <span class="keyword">private</span> Wife wife;</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></span><br><span class="line">        <span class="built_in">this</span>.name = name;</span><br><span class="line">    <span class="keyword">public</span> String <span class="title function_">getName</span><span class="params">()</span></span><br><span class="line">        <span class="keyword">return</span> name;</span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">setWife</span><span class="params">(Wife wife)</span></span><br><span class="line">        <span class="built_in">this</span>.wife = wife;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// toString()方法重写时需要注意：不能直接输出wife，输出wife.getName()。要不然会出现递归导致的栈内存溢出错误。</span></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;Husband&#123;&quot;</span> +</span><br><span class="line">                <span class="string">&quot;name=&#x27;&quot;</span> + name + <span class="string">&#x27;\&#x27;&#x27;</span> +</span><br><span class="line">                <span class="string">&quot;, wife=&quot;</span> + wife.getName() +</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><button type="button" class="tab-to-top" aria-label="scroll to top"><i class="fas fa-arrow-up"></i></button></div><div class="tab-item-content" id="-2"><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><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_">Wife</span> &#123;</span><br><span class="line">    <span class="keyword">private</span> String name;</span><br><span class="line">    <span class="keyword">private</span> Husband husband;</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></span><br><span class="line">        <span class="built_in">this</span>.name = name;</span><br><span class="line">    <span class="keyword">public</span> String <span class="title function_">getName</span><span class="params">()</span></span><br><span class="line">        <span class="keyword">return</span> name;</span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">setHusband</span><span class="params">(Husband husband)</span> </span><br><span class="line">        <span class="built_in">this</span>.husband = husband;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// toString()方法重写时需要注意：不能直接输出husband，输出husband.getName()。要不然会出现递归导致的栈内存溢出错误。</span></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;Wife&#123;&quot;</span> +</span><br><span class="line">                <span class="string">&quot;name=&#x27;&quot;</span> + name + <span class="string">&#x27;\&#x27;&#x27;</span> +</span><br><span class="line">                <span class="string">&quot;, husband=&quot;</span> + husband.getName() +</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><button type="button" class="tab-to-top" aria-label="scroll to top"><i class="fas fa-arrow-up"></i></button></div><div class="tab-item-content" id="-3"><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></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">beans</span> <span class="attr">xmlns</span>=<span class="string">&quot;http://www.springframework.org/schema/beans&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://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd&quot;</span>&gt;</span></span><br><span class="line"></span><br><span class="line">    <span class="tag">&lt;<span class="name">bean</span> <span class="attr">id</span>=<span class="string">&quot;husbandBean&quot;</span> <span class="attr">class</span>=<span class="string">&quot;com.powernode.spring6.bean.Husband&quot;</span> <span class="attr">scope</span>=<span class="string">&quot;singleton&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;name&quot;</span> <span class="attr">value</span>=<span class="string">&quot;张三&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;wife&quot;</span> <span class="attr">ref</span>=<span class="string">&quot;wifeBean&quot;</span>/&gt;</span></span><br><span class="line">    <span class="tag">&lt;/<span class="name">bean</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">bean</span> <span class="attr">id</span>=<span class="string">&quot;wifeBean&quot;</span> <span class="attr">class</span>=<span class="string">&quot;com.powernode.spring6.bean.Wife&quot;</span> <span class="attr">scope</span>=<span class="string">&quot;singleton&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;name&quot;</span> <span class="attr">value</span>=<span class="string">&quot;小花&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;husband&quot;</span> <span class="attr">ref</span>=<span class="string">&quot;husbandBean&quot;</span>/&gt;</span></span><br><span class="line">    <span class="tag">&lt;/<span class="name">bean</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;/<span class="name">beans</span>&gt;</span></span><br></pre></td></tr></table></figure><button type="button" class="tab-to-top" aria-label="scroll to top"><i class="fas fa-arrow-up"></i></button></div></div></div><p>执行结果：<br><div class="img-wrap"><div class="img-bg"><img class="img" src="https://cdn.nlark.com/yuque/0/2022/png/21376908/1665453201014-160bb88e-08d4-4d37-a1d9-44d4911a32df.png?x-oss-process=image%2Fwatermark%2Ctype_d3F5LW1pY3JvaGVp%2Csize_15%2Ctext_5Yqo5Yqb6IqC54K5%2Ccolor_FFFFFF%2Cshadow_50%2Ct_80%2Cg_se%2Cx_10%2Cy_10%2Fformat%2Cwebp"/></div></div></p><h2 id="多例下的set注入产生的循环依赖"><a href="#多例下的set注入产生的循环依赖" class="headerlink" title="多例下的set注入产生的循环依赖"></a>多例下的set注入产生的循环依赖</h2><p>同样的循环依赖问题，换为多例模式：<br><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></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">beans</span> <span class="attr">xmlns</span>=<span class="string">&quot;http://www.springframework.org/schema/beans&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://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd&quot;</span>&gt;</span></span><br><span class="line"></span><br><span class="line">    <span class="tag">&lt;<span class="name">bean</span> <span class="attr">id</span>=<span class="string">&quot;husbandBean&quot;</span> <span class="attr">class</span>=<span class="string">&quot;com.powernode.spring6.bean.Husband&quot;</span> <span class="attr">scope</span>=<span class="string">&quot;prototype&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;name&quot;</span> <span class="attr">value</span>=<span class="string">&quot;张三&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;wife&quot;</span> <span class="attr">ref</span>=<span class="string">&quot;wifeBean&quot;</span>/&gt;</span></span><br><span class="line">    <span class="tag">&lt;/<span class="name">bean</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">bean</span> <span class="attr">id</span>=<span class="string">&quot;wifeBean&quot;</span> <span class="attr">class</span>=<span class="string">&quot;com.powernode.spring6.bean.Wife&quot;</span> <span class="attr">scope</span>=<span class="string">&quot;prototype&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;name&quot;</span> <span class="attr">value</span>=<span class="string">&quot;小花&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;husband&quot;</span> <span class="attr">ref</span>=<span class="string">&quot;husbandBean&quot;</span>/&gt;</span></span><br><span class="line">    <span class="tag">&lt;/<span class="name">bean</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;/<span class="name">beans</span>&gt;</span></span><br></pre></td></tr></table></figure></p><ul><li>此时就会出现异常：<code>BeanCurrentlyInCreationException</code><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><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">Caused by: org.springframework.beans.factory.BeanCurrentlyInCreationException: Error creating bean with name <span class="string">&#x27;husbandBean&#x27;</span>: Requested bean is currently in creation: Is there an unresolvable circular reference?</span><br><span class="line">at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:<span class="number">265</span>)</span><br><span class="line">at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:<span class="number">199</span>)</span><br><span class="line">at org.springframework.beans.factory.support.BeanDefinitionValueResolver.resolveReference(BeanDefinitionValueResolver.java:<span class="number">325</span>)</span><br><span class="line">... <span class="number">44</span> more</span><br></pre></td></tr></table></figure></li><li>翻译为：创建名为“husbandBean”的bean时出错：请求的bean当前正在创建中：是否存在无法解析的循环引用？</li><li>通过测试可以知道，多例模式下的循环依赖，Spring无法解决，会抛出异常。<div class="note danger no-icon flat"><ul><li>在全部为Set模式下的循环依赖：<ul><li>一个<code>单例Bean</code>，一个<code>多例Bean</code>不会产生循环依赖。</li><li>两个都为<code>单例Bean</code>也不会产生循环依赖。</li><li>只有两个都为<code>多例Bean</code>才会产生循环依赖。<div class="img-wrap"><div class="img-bg"><img class="img" src="https://cdn.nlark.com/yuque/0/2022/png/21376908/1665454469042-69668f45-5d71-494f-8537-18142d354abd.png?x-oss-process=image%2Fwatermark%2Ctype_d3F5LW1pY3JvaGVp%2Csize_32%2Ctext_5Yqo5Yqb6IqC54K5%2Ccolor_FFFFFF%2Cshadow_50%2Ct_80%2Cg_se%2Cx_10%2Cy_10%2Fformat%2Cwebp"/></div></div></li></ul></li></ul></div><h2 id="多例下的构造注入产生的循环依赖"><a href="#多例下的构造注入产生的循环依赖" class="headerlink" title="多例下的构造注入产生的循环依赖"></a>多例下的构造注入产生的循环依赖</h2><div class="tabs" id="多例"><ul class="nav-tabs"><li class="tab active"><button type="button" data-href="#多例-1">Husband</button></li><li class="tab"><button type="button" data-href="#多例-2">Wife</button></li><li class="tab"><button type="button" data-href="#多例-3">配置文件</button></li><li class="tab"><button type="button" data-href="#多例-4">测试程序</button></li></ul><div class="tab-contents"><div class="tab-item-content active" id="多例-1"><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><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><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">Husband</span> &#123;</span><br><span class="line">    <span class="keyword">private</span> String name;</span><br><span class="line">    <span class="keyword">private</span> Wife wife;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">public</span> <span class="title function_">Husband</span><span class="params">(String name, Wife wife)</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>.wife = wife;</span><br><span 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> 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="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;Husband&#123;&quot;</span> +</span><br><span class="line">                <span class="string">&quot;name=&#x27;&quot;</span> + name + <span class="string">&#x27;\&#x27;&#x27;</span> +</span><br><span class="line">                <span class="string">&quot;, wife=&quot;</span> + wife +</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><button type="button" class="tab-to-top" aria-label="scroll to top"><i class="fas fa-arrow-up"></i></button></div><div class="tab-item-content" id="多例-2"><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><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_">Wife</span> &#123;</span><br><span class="line">    <span class="keyword">private</span> String name;</span><br><span class="line">    <span class="keyword">private</span> Husband husband;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">public</span> <span class="title function_">Wife</span><span class="params">(String name, Husband husband)</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>.husband = husband;</span><br><span 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> 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="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;Wife&#123;&quot;</span> +</span><br><span class="line">                <span class="string">&quot;name=&#x27;&quot;</span> + name + <span class="string">&#x27;\&#x27;&#x27;</span> +</span><br><span class="line">                <span class="string">&quot;, husband=&quot;</span> + husband +</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><button type="button" class="tab-to-top" aria-label="scroll to top"><i class="fas fa-arrow-up"></i></button></div><div class="tab-item-content" id="多例-3"><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="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">beans</span> <span class="attr">xmlns</span>=<span class="string">&quot;http://www.springframework.org/schema/beans&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://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd&quot;</span>&gt;</span></span><br><span class="line"></span><br><span class="line">    <span class="tag">&lt;<span class="name">bean</span> <span class="attr">id</span>=<span class="string">&quot;hBean&quot;</span> <span class="attr">class</span>=<span class="string">&quot;com.powernode.spring6.bean2.Husband&quot;</span> <span class="attr">scope</span>=<span class="string">&quot;singleton&quot;</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">constructor-arg</span> <span class="attr">name</span>=<span class="string">&quot;name&quot;</span> <span class="attr">value</span>=<span class="string">&quot;张三&quot;</span>/&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">constructor-arg</span> <span class="attr">name</span>=<span class="string">&quot;wife&quot;</span> <span class="attr">ref</span>=<span class="string">&quot;wBean&quot;</span>/&gt;</span></span><br><span class="line">    <span class="tag">&lt;/<span class="name">bean</span>&gt;</span></span><br><span class="line"></span><br><span class="line">    <span class="tag">&lt;<span class="name">bean</span> <span class="attr">id</span>=<span class="string">&quot;wBean&quot;</span> <span class="attr">class</span>=<span class="string">&quot;com.powernode.spring6.bean2.Wife&quot;</span> <span class="attr">scope</span>=<span class="string">&quot;singleton&quot;</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">constructor-arg</span> <span class="attr">name</span>=<span class="string">&quot;name&quot;</span> <span class="attr">value</span>=<span class="string">&quot;小花&quot;</span>/&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">constructor-arg</span> <span class="attr">name</span>=<span class="string">&quot;husband&quot;</span> <span class="attr">ref</span>=<span class="string">&quot;hBean&quot;</span>/&gt;</span></span><br><span class="line">    <span class="tag">&lt;/<span class="name">bean</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;/<span class="name">beans</span>&gt;</span></span><br></pre></td></tr></table></figure><button type="button" class="tab-to-top" aria-label="scroll to top"><i class="fas fa-arrow-up"></i></button></div><div class="tab-item-content" id="多例-4"><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><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">@Test</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">testSingletonAndConstructor</span><span class="params">()</span>&#123;</span><br><span class="line">    <span class="type">ApplicationContext</span> <span class="variable">applicationContext</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">ClassPathXmlApplicationContext</span>(<span class="string">&quot;spring2.xml&quot;</span>);</span><br><span class="line">    <span class="type">Husband</span> <span class="variable">hBean</span> <span class="operator">=</span> applicationContext.getBean(<span class="string">&quot;hBean&quot;</span>, Husband.class);</span><br><span class="line">    <span class="type">Wife</span> <span class="variable">wBean</span> <span class="operator">=</span> applicationContext.getBean(<span class="string">&quot;wBean&quot;</span>, Wife.class);</span><br><span class="line">    System.out.println(hBean);</span><br><span class="line">    System.out.println(wBean);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><button type="button" class="tab-to-top" aria-label="scroll to top"><i class="fas fa-arrow-up"></i></button></div></div></div></li><li>运行程序后发现和多例模式的set注入一样，会抛出异常：<code>BeanCurrentlyInCreationException</code><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><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">Caused by: org.springframework.beans.factory.BeanCurrentlyInCreationException: Error creating bean with name <span class="string">&#x27;hBean&#x27;</span>: Requested bean is currently in creation: Is there an unresolvable circular reference?</span><br><span class="line">at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.beforeSingletonCreation(DefaultSingletonBeanRegistry.java:<span class="number">355</span>)</span><br><span class="line">at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:<span class="number">227</span>)</span><br><span class="line">at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:<span class="number">324</span>)</span><br><span class="line">at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:<span class="number">199</span>)</span><br><span class="line">at org.springframework.beans.factory.support.BeanDefinitionValueResolver.resolveReference(BeanDefinitionValueResolver.java:<span class="number">325</span>)</span><br><span class="line">... <span class="number">56</span> more</span><br></pre></td></tr></table></figure><div class="note danger no-icon flat"><p><strong>主要原因是因为通过构造方法注入导致的：因为构造方法注入会导致实例化对象的过程和对象属性赋值的过程没有分离开，必须在一起完成导致的。</strong></p></div><h2 id="Spring解决循环依赖的机理"><a href="#Spring解决循环依赖的机理" class="headerlink" title="Spring解决循环依赖的机理"></a>Spring解决循环依赖的机理</h2><div class="note warning no-icon flat"><p>Spring解决循环依赖的根本原因：Spring 在创建单例 Bean 时，利用 <code>三级缓存</code>，通过 <strong>提前暴露 Bean 的“半成品”（仅完成实例化，还没完成属性赋值和初始化）</strong>，把 <code>实例化对象</code> 和 <code>依赖注入</code> 两个阶段拆开执行，从而打破循环依赖。</p></div></li><li>使用Set注入，即在实例化BEan时，调用无参构造方法来完成。<strong>此时可以先不给属性赋值，可以提前将该Bean对象“曝光”给外界。</strong>，等到给Bean属性赋值时再调用Set方法完成</li><li>两个步骤是完全可以分离开去完成的，并且这两步不要求在同一个时间点上完成。</li><li>也就是说，Bean都是单例的，我们可以先把所有的单例Bean实例化出来，放到一个集合当中（我们可以称之为缓存），所有的单例Bean全部实例化完成之后，以后我们再慢慢的调用setter方法给属性赋值。这样就解决了循环依赖的问题。</li></ul><details class="folding-tag" ><summary> Spring底层源码 </summary>              <div class='content'>              <p><div class="img-wrap"><div class="img-bg"><img class="img" src="https://cdn.nlark.com/yuque/0/2022/png/21376908/1665456331018-18c45ae3-fa4c-4cd8-aabf-d9bace567693.png?x-oss-process=image%2Fwatermark%2Ctype_d3F5LW1pY3JvaGVp%2Csize_41%2Ctext_5Yqo5Yqb6IqC54K5%2Ccolor_FFFFFF%2Cshadow_50%2Ct_80%2Cg_se%2Cx_10%2Cy_10%2Fformat%2Cwebp"/></div>&lt;/div&gt;</p><ul><li>以上类中包含三个重要的属性：<ul><li><strong>Cache of singleton objects: bean name to bean instance.</strong> 单例对象的缓存：key存储bean名称，value存储Bean对象【一级缓存】</li><li><strong>Cache of early singleton objects: bean name to bean instance.</strong> 早期单例对象的缓存：key存储bean名称，value存储早期的Bean对象【二级缓存】</li><li><strong>Cache of singleton factories: bean name to ObjectFactory.</strong> 单例工厂缓存：key存储bean名称，value存储该Bean对应的ObjectFactory对象【三级缓存】</li></ul></li><li>这三个缓存本质上是三个Map集合。</li><li>在该类中还有一个方法：<code>addSingletonFactory()</code>，这个方法的作用是：将创建Bean对象的ObjectFactory提前曝光<br><div class="img-wrap"><div class="img-bg"><img class="img" src="https://cdn.nlark.com/yuque/0/2022/png/21376908/1665460724682-2222366d-cc07-43db-a8d0-fb27712b20a4.png?x-oss-process=image%2Fwatermark%2Ctype_d3F5LW1pY3JvaGVp%2Csize_31%2Ctext_5Yqo5Yqb6IqC54K5%2Ccolor_FFFFFF%2Cshadow_50%2Ct_80%2Cg_se%2Cx_10%2Cy_10%2Fformat%2Cwebp"/></div>&lt;/div&gt;<br>再看以下源码<br><div class="img-wrap"><div class="img-bg"><img class="img" src="https://cdn.nlark.com/yuque/0/2022/png/21376908/1665460240687-3d0794c4-e6ed-4653-9463-767a7f943ff9.png?x-oss-process=image%2Fwatermark%2Ctype_d3F5LW1pY3JvaGVp%2Csize_34%2Ctext_5Yqo5Yqb6IqC54K5%2Ccolor_FFFFFF%2Cshadow_50%2Ct_80%2Cg_se%2Cx_10%2Cy_10%2Fformat%2Cwebp"/></div>&lt;/div&gt;<br>Spring会先从一级缓存获取Bean对象，如果一级缓存中没有，则从二级缓存中获取，如果二级缓存中没有，则从三级缓存中获取之前曝光的ObejectFactory对象，通过ObjectFactory对象获取Bean实例，这样就解决了循环依赖的问题。</li></ul>              </div>            </details><div class="note danger no-icon flat"><p><strong>总结：</strong><br><strong>Spring只能解决<code>setter方法</code>注入的单例bean之间的循环依赖。</strong> ClassA依赖ClassB，ClassB又依赖ClassA，形成依赖闭环。Spring在创建ClassA对象后，不需要等给属性赋值，直接将其曝光到bean缓存当中。在解析ClassA的属性时，又发现依赖于ClassB，再次去获取ClassB，当解析ClassB的属性时，又发现需要ClassA的属性，但此时的ClassA已经被提前曝光加入了正在创建的bean的缓存中，则无需创建新的的ClassA的实例，直接从缓存中获取即可。从而解决循环依赖问题。</p></div><h1 id="回顾反射"><a href="#回顾反射" class="headerlink" title="回顾反射"></a>回顾反射</h1><h2 id="分析方法四要素"><a href="#分析方法四要素" class="headerlink" title="分析方法四要素"></a>分析方法四要素</h2><ul><li>首先回顾不使用反射机制调用一个方法需要几个要素参与?</li></ul><div class="tabs" id="反射"><ul class="nav-tabs"><li class="tab active"><button type="button" data-href="#反射-1">SystemService 类 %}</button></li><li class="tab"><button type="button" data-href="#反射-2">调用方法</button></li></ul><div class="tab-contents"><div class="tab-item-content active" id="反射-1"><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><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_">SystemService</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_">logout</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"></span><br><span class="line">    <span class="keyword">public</span> <span class="type">boolean</span> <span class="title function_">login</span><span class="params">(String username, String password)</span>&#123;</span><br><span class="line">        <span class="keyword">if</span> (<span class="string">&quot;admin&quot;</span>.equals(username) &amp;&amp; <span class="string">&quot;admin123&quot;</span>.equals(password)) &#123;</span><br><span class="line">            <span class="keyword">return</span> <span class="literal">true</span>;</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><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><button type="button" class="tab-to-top" aria-label="scroll to top"><i class="fas fa-arrow-up"></i></button></div><div class="tab-item-content" id="反射-2"><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><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_">ReflectTest01</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><br><span class="line">        <span class="comment">// 创建对象</span></span><br><span class="line">        <span class="type">SystemService</span> <span class="variable">systemService</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">SystemService</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">success</span> <span class="operator">=</span> systemService.login(<span class="string">&quot;admin&quot;</span>, <span class="string">&quot;admin123&quot;</span>);</span><br><span class="line"></span><br><span class="line">        System.out.println(success ? <span class="string">&quot;登录成功&quot;</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><button type="button" class="tab-to-top" aria-label="scroll to top"><i class="fas fa-arrow-up"></i></button></div></div></div><div class="note info no-icon flat"><p>调用一个方法一般需要知道该方法的四个要素</p><ul><li>调用哪个对象的方法（systemService）</li><li>哪个方法（login）</li><li>传什么参数（”admin”, “admin123”）</li><li>返回什么值（success）</li></ul></div><h2 id="获取Method"><a href="#获取Method" class="headerlink" title="获取Method"></a>获取Method</h2><ul><li>如果想通过反射机制调用一个方法，首先需要获取这个方法，在反射中Method实例代表一个方法，如何获得Method实例呢？<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><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">public</span> <span class="keyword">class</span> <span class="title class_">SystemService</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_">logout</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"></span><br><span class="line">    <span class="keyword">public</span> <span class="type">boolean</span> <span class="title function_">login</span><span class="params">(String username, String password)</span>&#123;</span><br><span class="line">        <span class="keyword">if</span> (<span class="string">&quot;admin&quot;</span>.equals(username) &amp;&amp; <span class="string">&quot;admin123&quot;</span>.equals(password)) &#123;</span><br><span class="line">            <span class="keyword">return</span> <span class="literal">true</span>;</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><br><span class="line">    </span><br><span class="line">    <span class="keyword">public</span> <span class="type">boolean</span> <span class="title function_">login</span><span class="params">(String password)</span>&#123;</span><br><span class="line">        <span class="keyword">if</span>(<span class="string">&quot;110&quot;</span>.equals(password))&#123;</span><br><span class="line">            <span class="keyword">return</span> <span class="literal">true</span>;</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><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>想要获取这个类的三个方法，首先需要获取这个类Class。<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="type">Class</span> <span class="variable">clazz</span> <span class="operator">=</span> Class.forName(<span class="string">&quot;com.powernode.reflect.SystemService&quot;</span>);</span><br></pre></td></tr></table></figure>拿到这个类以后，就可以调用<code>getDeclaredMethod()</code>方法来获取方法。<br>假设想要获取login(String username, String password)方法<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="type">Method</span> <span class="variable">loginMethod</span> <span class="operator">=</span> clazz.getDeclaredMethod(<span class="string">&quot;login&quot;</span>, String.class, String.class);</span><br></pre></td></tr></table></figure>获取login(String password)方法<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="type">Method</span> <span class="variable">loginMethod</span> <span class="operator">=</span> clazz.getDeclaredMethod(<span class="string">&quot;login&quot;</span>, String.class);</span><br></pre></td></tr></table></figure>获取一个方法，告诉Java程序，想要获取的方法名字是什么，该方法的形参类型是什么，这样才能获取到对应的方法，因为方法是支持重载的，所以获取指定方法必须明确方法名和形参类型。<br>假如想要获得下面的方法，代码该如何写？</li></ul><div class="tabs" id="获取方法"><ul class="nav-tabs"><li class="tab active"><button type="button" data-href="#获取方法-1">setAge方法</button></li><li class="tab"><button type="button" data-href="#获取方法-2">获取方法</button></li></ul><div class="tab-contents"><div class="tab-item-content active" id="获取方法-1"><figure class="highlight java"><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="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="built_in">this</span>.age = age;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><button type="button" class="tab-to-top" aria-label="scroll to top"><i class="fas fa-arrow-up"></i></button></div><div class="tab-item-content" id="获取方法-2"><p>其中setAge是方法名，int.class是形参的类型。<br><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="type">Method</span> <span class="variable">setAgeMethod</span> <span class="operator">=</span> clazz.getDeclaredMethod(<span class="string">&quot;setAge&quot;</span>, <span class="type">int</span>.class);</span><br></pre></td></tr></table></figure></p><button type="button" class="tab-to-top" aria-label="scroll to top"><i class="fas fa-arrow-up"></i></button></div></div></div><h2 id="调用Method"><a href="#调用Method" class="headerlink" title="调用Method"></a>调用Method</h2><ul><li>获取方法后，如果想调用该方法，就关联到四要素了<ul><li>调用哪个对象的方法</li><li>哪个方法</li><li>传什么参数</li><li>返回什么值</li></ul></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></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">SystemService</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_">logout</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"></span><br><span class="line">    <span class="keyword">public</span> <span class="type">boolean</span> <span class="title function_">login</span><span class="params">(String username, String password)</span>&#123;</span><br><span class="line">        <span class="keyword">if</span> (<span class="string">&quot;admin&quot;</span>.equals(username) &amp;&amp; <span class="string">&quot;admin123&quot;</span>.equals(password)) &#123;</span><br><span class="line">            <span class="keyword">return</span> <span class="literal">true</span>;</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><br><span class="line"></span><br><span class="line">    <span class="keyword">public</span> <span class="type">boolean</span> <span class="title function_">login</span><span class="params">(String password)</span>&#123;</span><br><span class="line">        <span class="keyword">if</span>(<span class="string">&quot;110&quot;</span>.equals(password))&#123;</span><br><span class="line">            <span class="keyword">return</span> <span class="literal">true</span>;</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><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>此时要调用方法：login(String username, String password)<br>第一步：创建对象(四要素之首：调用哪个对象的)<br><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">Class</span> <span class="variable">clazz</span> <span class="operator">=</span> Class.forName(<span class="string">&quot;com.powernode.reflect.SystemService&quot;</span>);</span><br><span class="line"><span class="type">Object</span> <span class="variable">obj</span> <span class="operator">=</span> clazz.newInstance();</span><br></pre></td></tr></table></figure></p><p>第二步：获取方法login(String,String)（四要素之一：哪个方法）<br><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="type">Method</span> <span class="variable">loginMethod</span> <span class="operator">=</span> clazz.getDeclaredMethod(<span class="string">&quot;login&quot;</span>, String.class, String.class);</span><br></pre></td></tr></table></figure></p><p>第三步：调用方法<br><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="type">Object</span> <span class="variable">retValue</span> <span class="operator">=</span> loginMethod.invoke(obj, <span class="string">&quot;admin&quot;</span>, <span class="string">&quot;admin123&quot;</span>);</span><br></pre></td></tr></table></figure></p><p>解说四要素：</p><ul><li>哪个对象：obj</li><li>哪个方法：loginMethod</li><li>传什么参数：”admin”, “admin123”</li><li>返回什么值：retValue</li></ul><div class="tabs" id="完整代码"><ul class="nav-tabs"><li class="tab active"><button type="button" data-href="#完整代码-1">测试程序</button></li><li class="tab"><button type="button" data-href="#完整代码-2">执行结果</button></li></ul><div class="tab-contents"><div class="tab-item-content active" id="完整代码-1"><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><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">public</span> <span class="keyword">class</span> <span class="title class_">ReflectTest02</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;com.powernode.reflect.SystemService&quot;</span>);</span><br><span class="line">        <span class="type">Object</span> <span class="variable">obj</span> <span class="operator">=</span> clazz.newInstance();</span><br><span class="line">        <span class="type">Method</span> <span class="variable">loginMethod</span> <span class="operator">=</span> clazz.getDeclaredMethod(<span class="string">&quot;login&quot;</span>, String.class, String.class);</span><br><span class="line">        <span class="type">Object</span> <span class="variable">retValue</span> <span class="operator">=</span> loginMethod.invoke(obj, <span class="string">&quot;admin&quot;</span>, <span class="string">&quot;admin123&quot;</span>);</span><br><span class="line">        System.out.println(retValue);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><button type="button" class="tab-to-top" aria-label="scroll to top"><i class="fas fa-arrow-up"></i></button></div><div class="tab-item-content" id="完整代码-2"><div class="img-wrap"><div class="img-bg"><img class="img" src="https://cdn.nlark.com/yuque/0/2022/png/21376908/1665471501974-88a80910-1c8e-495b-956f-d6b7a82bf5b4.png?x-oss-process=image%2Fwatermark%2Ctype_d3F5LW1pY3JvaGVp%2Csize_11%2Ctext_5Yqo5Yqb6IqC54K5%2Ccolor_FFFFFF%2Cshadow_50%2Ct_80%2Cg_se%2Cx_10%2Cy_10%2Fformat%2Cwebp"/></div></div><button type="button" class="tab-to-top" aria-label="scroll to top"><i class="fas fa-arrow-up"></i></button></div></div></div><p>既没有参数，又没有返回值的logout方法怎么调用呢？<br><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><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_">ReflectTest03</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;com.powernode.reflect.SystemService&quot;</span>);</span><br><span class="line">        <span class="type">Object</span> <span class="variable">obj</span> <span class="operator">=</span> clazz.newInstance();</span><br><span class="line">        <span class="type">Method</span> <span class="variable">logoutMethod</span> <span class="operator">=</span> clazz.getDeclaredMethod(<span class="string">&quot;logout&quot;</span>);</span><br><span class="line">        logoutMethod.invoke(obj);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></p><h2 id="知道属性名为属性赋值"><a href="#知道属性名为属性赋值" class="headerlink" title="知道属性名为属性赋值"></a>知道属性名为属性赋值</h2><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><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_">User</span> &#123;</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="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="built_in">this</span>.age = age;</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;name=&#x27;&quot;</span> + name + <span class="string">&#x27;\&#x27;&#x27;</span> +</span><br><span class="line">                <span class="string">&quot;, age=&quot;</span> + age +</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><ul><li>知道以下信息：<ul><li>类名是：com.powernode.reflect.User</li><li>该类中有String类型的name属性和int类型的age属性。</li><li>另外你也知道该类的设计符合javabean规范。（也就是说属性私有化，对外提供setter和getter方法）</li></ul></li></ul><ul><li>如何通过反射机制给User对象的name属性赋值zhangsan，给age属性赋值20岁？</li></ul><p>```java<br>public class UserTest {<br>    public static void main(String[] args) throws Exception{<br>        // 已知类名<br>        String className = “com.powernode.reflect.User”;<br>        // 已知属性名<br>        String propertyName = “age”;<br>        // 通过反射机制给User对象的age属性赋值20岁<br>        Class&lt;?&gt; clazz = Class.forName(className);<br>        Object obj = clazz.newInstance(); // 创建对象<br>        // 根据属性名获取setter方法名<br>        String setMethodName = “set” + propertyName.toUpperCase().charAt(0) + propertyName.substring(1);<br>        // 获取Method<br>        Method setMethod = clazz.getDeclaredMethod(setMethodName, int.class);<br>        // 调用Method<br>        setMethod.invoke(obj, 20);<br>        System.out.println(obj);<br>    }<br>}</p>]]></content>
    
    
    <summary type="html">进阶了解Spring</summary>
    
    
    
    <category term="Spring" scheme="https://silvan.chat/categories/Spring/"/>
    
    <category term="Java" scheme="https://silvan.chat/categories/Spring/Java/"/>
    
    
  </entry>
  
  <entry>
    <title>JavaWeb--JSP</title>
    <link href="https://silvan.chat/2025/09/26/JaveWeb-JSP/"/>
    <id>https://silvan.chat/2025/09/26/JaveWeb-JSP/</id>
    <published>2025-09-26T10:35:50.590Z</published>
    <updated>2025-09-26T11:29:01.507Z</updated>
    
    <content type="html"><![CDATA[<h1 id="概念"><a href="#概念" class="headerlink" title="概念"></a>概念</h1><ul><li><code>第一个JSP程序：</code><ul><li>在WEB-INF目录之外创建一个index.jsp文件，然后这个文件中没有任何内容。</li><li>将上面的项目部署后，启动服务器，打开浏览器，访问以下地址:<code>http://localhost:8080/jsp/index.jsp</code> 此时展现在大家面前的是一个空白页</li></ul></li><li><code>过程：</code><ul><li>实际上访问该index.jsp文件时，底层执行的是：index_jsp.class这个java程序。</li><li>这个index.jsp会被Tomcat翻译生成一个index_jsp.java文件，之后Tomcat会将这个java文件编译成一个class文件，最后由JVM来执行这个class文件。</li><li>访问index.jsp，实际上执行的是index_jsp.class中的方法</li></ul></li><li><code>JSP生命周期</code><ul><li>index.jsp访问的时候会自动翻译成index_jsp.java文件，该文件再编译为class文件，index_jsp这就是一个类。</li><li>index_jsp类继承了HttpJspBase，而HttpJspBase类继承了HttpServlet类，故index_jsp类也是一个Servlet</li><li>JSP实际上就是一个Servlet，<strong>Jsp的生命周期和Servlet一样，没有任何区别</strong></li><li>jsp和Servlet一样，都是单例的(假单例)。</li></ul></li><li><code>思考:为什么JSP文件的第一次访问速度较慢？</code><ul><li>大部分运维人员给客户演示项目，都需要先将所以JSP文件先访问一遍、</li><li>第一次访问JSP文件<ul><li>将jsp文件翻译生成Java文件</li><li>编译Java文件生成class文件</li><li>通过class去创建servlet对象</li><li>调用Servlet的init方法</li><li>最后调用servlet对象的service方法</li></ul></li><li>之后访问JSP文件<ul><li>此时直接调用servlet对象的service方法即可</li></ul></li></ul></li><li><code>JSP概念总结</code><ul><li>JSP是什么？<ul><li>JSP是java程序（JSP本质还是一个Servlet）</li><li>JSP是：JavaServer Pages的缩写。（基于Java语言实现的服务器端的页面。）</li><li>Servlet是JavaEE的13个子规范之一，那么JSP也是JavaEE的13个子规范之一。</li><li>JSP是一套规范。所有的web容器/web服务器都是遵循这套规范的，都是按照这套规范进行的“翻译”</li><li>每一个Web容器/Web服务器都会内置一个JSP翻译引擎</li></ul></li><li>对JSP进行错误调试时，依旧需要打开JSP文件对应的Java文件，检查代码</li><li>开发JSP最高境界：看着JSP代码，能想象到Java代码<h1 id="JSP语法"><a href="#JSP语法" class="headerlink" title="JSP语法"></a>JSP语法</h1></li></ul></li><li>在JSP文件中直接编写文字会被翻译到Servlet类的Service方法的<code>out.write(&quot;文字内容&quot;)</code>中，直接翻译到双引号里，被Java程序当做字符串来打印输出到浏览器</li><li></li></ul>]]></content>
    
    
      
      
    <summary type="html">&lt;h1 id=&quot;概念&quot;&gt;&lt;a href=&quot;#概念&quot; class=&quot;headerlink&quot; title=&quot;概念&quot;&gt;&lt;/a&gt;概念&lt;/h1&gt;&lt;ul&gt;
&lt;li&gt;&lt;code&gt;第一个JSP程序：&lt;/code&gt;&lt;ul&gt;
&lt;li&gt;在WEB-INF目录之外创建一个index.jsp文件，然后这个文</summary>
      
    
    
    
    <category term="JavaWeb" scheme="https://silvan.chat/categories/JavaWeb/"/>
    
    
    <category term="java" scheme="https://silvan.chat/tags/java/"/>
    
    <category term="javaWeb" scheme="https://silvan.chat/tags/javaWeb/"/>
    
  </entry>
  
  <entry>
    <title>Servlet</title>
    <link href="https://silvan.chat/2025/09/25/JaveWeb-Servlet/"/>
    <id>https://silvan.chat/2025/09/25/JaveWeb-Servlet/</id>
    <published>2025-09-25T06:49:31.957Z</published>
    <updated>2025-09-26T10:33:18.828Z</updated>
    
    <content type="html"><![CDATA[<h2 id="Servlet生命周期"><a href="#Servlet生命周期" class="headerlink" title="Servlet生命周期"></a>Servlet生命周期</h2><ul><li>网站中所有的Servlet接口实现类的实例对象，只能<strong>由Http服务器负责创建</strong>,开发人员不能手动创建Servlet接口实现类的实例对象</li><li>在默认的情况下，用户第一次请求的时候，自动创建Servlet接口实现类对象。(调用无参构造方法)</li><li>在手动配置情况下，要求Http服务器在启动时自动创建某个Servlet接口实现类的实例对象(填写一个大于0的整数即可)</li></ul><div class="tabs" id="servlet"><ul class="nav-tabs"><li class="tab active"><button type="button" data-href="#servlet-1">基于 XML ⽂件的配置⽅式</button></li><li class="tab"><button type="button" data-href="#servlet-2">基于注解的配置⽅式</button></li></ul><div class="tab-contents"><div class="tab-item-content active" id="servlet-1"><ul><li>基于 XML ⽂件的配置⽅式。 <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="tag">&lt;<span class="name">servlet</span>&gt;</span>  </span><br><span class="line">    <span class="tag">&lt;<span class="name">servlet-name</span>&gt;</span>hello<span class="tag">&lt;/<span class="name">servlet-name</span>&gt;</span> <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.bjpowernode.controller.OneServlet<span class="tag">&lt;/<span class="name">servlet-class</span>&gt;</span> <span class="comment">&lt;!--声明servlet接口实现类类路径--&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">load-on-startup</span>&gt;</span>30<span class="tag">&lt;<span class="name">load-on-startup</span>&gt;</span><span class="comment">&lt;!--填写一个大于0的整数即可--&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-mapping</span>&gt;</span> </span><br><span class="line">    <span class="tag">&lt;<span class="name">servlet-name</span>&gt;</span>hello<span class="tag">&lt;/<span class="name">servlet-name</span>&gt;</span> </span><br><span class="line">    <span class="tag">&lt;<span class="name">url-pattern</span>&gt;</span>/newhello<span class="tag">&lt;/<span class="name">url-pattern</span>&gt;</span> <span class="comment">&lt;!--设置简短请求别名,别名在书写时必须以&quot;/&quot;为开头--&gt;</span></span><br><span class="line"><span class="tag">&lt;/<span class="name">servlet-mapping</span>&gt;</span></span><br></pre></td></tr></table></figure></li></ul><button type="button" class="tab-to-top" aria-label="scroll to top"><i class="fas fa-arrow-up"></i></button></div><div class="tab-item-content" id="servlet-2"><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><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="meta">@WebServlet(&quot;/newhello&quot;)</span> </span><br><span class="line"></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">HelloServlet</span> <span class="keyword">implements</span> <span class="title class_">Servlet</span> &#123; </span><br><span class="line"></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><button type="button" class="tab-to-top" aria-label="scroll to top"><i class="fas fa-arrow-up"></i></button></div></div></div><div class="note danger no-icon flat"><p>关于Servlet类中方法的调用次数？</p><ul><li>构造方法只执行一次。</li><li>init方法只执行一次</li><li>service方法：用户发送一次请求则执行一次，发送N次请求则执行N次。</li><li>destroy方法只执行一次</li></ul></div><p>说明:</p><ul><li>用户在发送第一次请求的时候Servlet对象被实例化（AServlet的构造方法被执行了。并且执行的是无参数构造方法。）</li><li>AServlet对象被创建出来之后，Tomcat服务器马上调用了AServlet对象的init方法。</li><li>用户发送第一次请求的时候，init方法执行之后，Tomcat服务器马上调用AServlet对象的service方法。</li><li>用户在发送第二次，或者第三次，或者第四次请求的时候，Servlet对象并没有新建，还是使用之前创建好的Servlet对象，直接调用该Servlet对象的service方法.</li></ul><h3 id="注解详细解释"><a href="#注解详细解释" class="headerlink" title="注解详细解释"></a>注解详细解释</h3><p>我们的第一个注解：<br><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">jakarta.servlet.annotation.WebServlet</span><br></pre></td></tr></table></figure><br>在Servlet类上使用：@WebServlet，WebServlet注解中有哪些属性呢？</p><ul><li>name属性：用来指定Servlet的名字。等同于：<code>&lt;servlet-name&gt;</code></li><li>urlPatterns属性：用来指定Servlet的映射路径。可以指定多个字符串。<code>&lt;url-pattern&gt;</code></li><li>loadOnStartUp属性：用来指定在服务器启动阶段是否加载该Servlet。等同于：<code>&lt;load-on-startup&gt;</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><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">//@WebServlet(urlPatterns = &#123;&quot;/welcome1&quot;, &quot;/welcome2&quot;&#125;)</span></span><br><span class="line"><span class="comment">// 注意：当注解的属性是一个数组，并且数组中只有一个元素，大括号可以省略。</span></span><br><span class="line"><span class="comment">//@WebServlet(urlPatterns = &quot;/welcome&quot;)</span></span><br><span class="line"><span class="comment">// 这个value属性和urlPatterns属性一致，都是用来指定Servlet的映射路径的。</span></span><br><span class="line"><span class="comment">//@WebServlet(value = &#123;&quot;/welcome1&quot;, &quot;/welcome2&quot;&#125;)</span></span><br><span class="line"><span class="comment">// 如果注解的属性名是value的话，属性名也是可以省略的。</span></span><br><span class="line"><span class="comment">//@WebServlet(value = &quot;/welcome1&quot;)</span></span><br><span class="line"><span class="comment">//@WebServlet(&#123;&quot;/wel&quot;, &quot;/abc&quot;, &quot;/def&quot;&#125;)</span></span><br><span class="line"><span class="meta">@WebServlet(&quot;/wel&quot;)</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">WelcomeServlet</span> <span class="keyword">extends</span> <span class="title class_">HttpServlet</span> &#123;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h2 id="GenericServlet"><a href="#GenericServlet" class="headerlink" title="GenericServlet"></a>GenericServlet</h2><ol><li><p>使用MyServlet直接实现Servlet接口，需要将该接口中所有方法实现</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_">MyServlet</span> <span class="keyword">implements</span> <span class="title class_">Servlet</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_">init</span><span class="params">(ServletConfig servletConfig)</span> <span class="keyword">throws</span> ServletException &#123;</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> ServletConfig <span class="title function_">getServletConfig</span><span class="params">()</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"></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_">service</span><span class="params">(ServletRequest servletRequest, ServletResponse servletResponse)</span> <span class="keyword">throws</span> ServletException, IOException &#123;</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> String <span class="title function_">getServletInfo</span><span class="params">()</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"></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><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></li><li><p>我们进行改进</p></li></ol><p>我们让抽象类GenericServlet把不需要的方法实现即可，让service变成抽象类。<br><div class="tabs" id="genericservlet"><ul class="nav-tabs"><li class="tab active"><button type="button" data-href="#genericservlet-1">GenericServlet</button></li><li class="tab"><button type="button" data-href="#genericservlet-2">MyServlet</button></li></ul><div class="tab-contents"><div class="tab-item-content active" id="genericservlet-1"><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><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">abstract</span> <span class="keyword">class</span> <span class="title class_">GenericServlet</span>  <span class="keyword">implements</span> <span class="title class_">Servlet</span> &#123;</span><br><span class="line">    <span class="comment">//ServletConfig servletConfig形参，我们不知道传进来的是什么</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 servletConfig)</span> <span class="keyword">throws</span> ServletException &#123;</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> ServletConfig <span class="title function_">getServletConfig</span><span class="params">()</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"></span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">abstract</span> <span class="keyword">void</span> <span class="title function_">service</span><span class="params">(ServletRequest servletRequest, ServletResponse servletResponse)</span> <span class="keyword">throws</span> ServletException, IOException;</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_">getServletInfo</span><span class="params">()</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"></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><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><button type="button" class="tab-to-top" aria-label="scroll to top"><i class="fas fa-arrow-up"></i></button></div><div class="tab-item-content" id="genericservlet-2"><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><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="keyword">public</span> <span class="keyword">class</span> <span class="title class_">MyServlet</span>  <span class="keyword">extends</span> <span class="title class_">GenericServlet</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_">service</span><span class="params">(ServletRequest servletRequest, ServletResponse servletResponse)</span> <span class="keyword">throws</span> ServletException, IOException &#123;</span><br><span class="line"></span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><button type="button" class="tab-to-top" aria-label="scroll to top"><i class="fas fa-arrow-up"></i></button></div></div></div></p><ul><li>第一个问题MyServlet还能使用GenericServlet类中的init方法吗?<ul><li>可以。会执行GenericServlet类中的init方法。</li></ul></li><li>第二个问题：init方法是谁调用的？<ul><li>Tomcat服务器调用的。</li></ul></li><li>第三个问题：init方法中的ServletConfig对象是谁创建的？是谁传过来的？<ul><li>都是Tomcat干的。</li><li>Tomcat服务器先创建了ServletConfig对象，然后调用init方法，将ServletConfig对象传给了init方法。</li></ul></li></ul><p>Tomcat服务器伪代码：<br><div class="tabs" id="tomcat"><ul class="nav-tabs"><li class="tab active"><button type="button" data-href="#tomcat-1">Tomcat</button></li><li class="tab"><button type="button" data-href="#tomcat-2">GenericServlet</button></li><li class="tab"><button type="button" data-href="#tomcat-3">MyServlet</button></li></ul><div class="tab-contents"><div class="tab-item-content active" id="tomcat-1"><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><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_">Tomcat</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">// Tomcat服务器伪代码</span></span><br><span class="line">        <span class="comment">// 创建LoginServlet对象（通过反射机制，调用无参数构造方法来实例化MyServlet对象）</span></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;com.bjpowernode.javaweb.servlet.MyservletServlet&quot;</span>);</span><br><span class="line">        <span class="type">Object</span> <span class="variable">obj</span> <span class="operator">=</span> clazz.newInstance();</span><br><span class="line">        </span><br><span class="line">        <span class="comment">// 向下转型</span></span><br><span class="line">        <span class="type">Servlet</span> <span class="variable">servlet</span> <span class="operator">=</span> (Servlet)obj;</span><br><span class="line">        </span><br><span class="line">        <span class="comment">// 创建ServletConfig对象</span></span><br><span class="line">        <span class="comment">// Tomcat服务器负责将ServletConfig对象实例化出来。</span></span><br><span class="line">        <span class="comment">// 多态（Tomcat服务器完全实现了Servlet规范）</span></span><br><span class="line">        <span class="type">ServletConfig</span> <span class="variable">servletConfig</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">org</span>.apache.catalina.core.StandardWrapperFacade();</span><br><span class="line">        </span><br><span class="line">        <span class="comment">// 调用Servlet的init方法</span></span><br><span class="line">        <span class="comment">//将这个实参传入到那个方法里。</span></span><br><span class="line">        servlet.init(servletConfig);</span><br><span class="line">        </span><br><span class="line">        <span class="comment">// 调用Servlet的service方法</span></span><br><span class="line">        <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><button type="button" class="tab-to-top" aria-label="scroll to top"><i class="fas fa-arrow-up"></i></button></div><div class="tab-item-content" id="tomcat-2"><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><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="keyword">package</span> javax.servlet;</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.util.Enumeration;</span><br><span class="line"></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">abstract</span> <span class="keyword">class</span> <span class="title class_">GenericServlet</span> <span class="keyword">implements</span> <span class="title class_">Servlet</span>, ServletConfig,</span><br><span class="line">        java.io.Serializable &#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">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> <span class="keyword">transient</span> ServletConfig config;</span><br><span class="line"></span><br><span class="line">    <span class="comment">/*</span></span><br><span class="line"><span class="comment">        此构造方法啥也不做，因为Servlet的所有初始化任务都是由init方法完成的。</span></span><br><span class="line"><span class="comment">    */</span></span><br><span class="line">    <span class="keyword">public</span> <span class="title function_">GenericServlet</span><span class="params">()</span> &#123;</span><br><span class="line">        <span class="comment">// NOOP</span></span><br><span class="line">    &#125;</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">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="comment">// NOOP by default</span></span><br><span class="line">    &#125;</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">public</span> String <span class="title function_">getInitParameter</span><span class="params">(String name)</span> &#123;</span><br><span class="line">        <span class="keyword">return</span> getServletConfig().getInitParameter(name);</span><br><span class="line">    &#125;</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">public</span> Enumeration&lt;String&gt; <span class="title function_">getInitParameterNames</span><span class="params">()</span> &#123;</span><br><span class="line">        <span class="keyword">return</span> getServletConfig().getInitParameterNames();</span><br><span class="line">    &#125;</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">public</span> ServletConfig <span class="title function_">getServletConfig</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> config;</span><br><span class="line">    &#125;</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">public</span> ServletContext <span class="title function_">getServletContext</span><span class="params">()</span> &#123;</span><br><span class="line">        <span class="keyword">return</span> getServletConfig().getServletContext();</span><br><span class="line">    &#125;</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">public</span> String <span class="title function_">getServletInfo</span><span class="params">()</span> &#123;</span><br><span class="line">        <span class="keyword">return</span> <span class="string">&quot;&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="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="comment">/*该方法是形参，tomcat将实参传进来，然后通过this.config=config</span></span><br><span class="line"><span class="comment">          把局部变量变为全局变量。*/</span></span><br><span class="line">        <span class="built_in">this</span>.config = config;</span><br><span class="line">      <span class="comment">//Tomcat首先调用init()有参方法，然后调用init()无参方法。</span></span><br><span class="line">        <span class="built_in">this</span>.init();</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">   <span class="comment">/*目的是让子类重写init()方法的无参构造方法。*/</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">// NOOP by default</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">public</span> <span class="keyword">void</span> <span class="title function_">log</span><span class="params">(String msg)</span> &#123;</span><br><span class="line">        getServletContext().log(getServletName() + <span class="string">&quot;: &quot;</span> + msg);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line"></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">log</span><span class="params">(String message, Throwable t)</span> &#123;</span><br><span class="line">        getServletContext().log(getServletName() + <span class="string">&quot;: &quot;</span> + message, t);</span><br><span class="line">    &#125;</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">public</span> <span class="keyword">abstract</span> <span class="keyword">void</span> <span class="title function_">service</span><span class="params">(ServletRequest req, ServletResponse res)</span></span><br><span class="line">            <span class="keyword">throws</span> ServletException, IOException;</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">public</span> String <span class="title function_">getServletName</span><span class="params">()</span> &#123;</span><br><span class="line">        <span class="keyword">return</span> config.getServletName();</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><button type="button" class="tab-to-top" aria-label="scroll to top"><i class="fas fa-arrow-up"></i></button></div><div class="tab-item-content" id="tomcat-3"><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><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">package</span> GenericServlet;</span><br><span class="line"></span><br><span class="line"><span class="keyword">import</span> javax.servlet.*;</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="keyword">public</span> <span class="keyword">class</span> <span class="title class_">MyServlet</span>  <span class="keyword">extends</span> <span class="title class_">GenericServlet</span>&#123;</span><br><span class="line">    <span class="comment">//我们需要在MyServlet中重写init方法</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_">init</span><span class="params">()</span>&#123;</span><br><span class="line">        System.out.println(<span class="string">&quot;MyServlet init() method execute!&quot;</span>);</span><br><span class="line">    &#125;</span><br><span class="line"></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">public</span> <span class="keyword">void</span> <span class="title function_">service</span><span class="params">(ServletRequest servletRequest, ServletResponse servletResponse)</span> <span class="keyword">throws</span> ServletException, IOException &#123;</span><br><span class="line">        <span class="comment">//我想在MyServlet子类中使用Servlet对象怎么办?</span></span><br><span class="line">        ServletConfig config=<span class="built_in">this</span>.getServletConfig();</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><button type="button" class="tab-to-top" aria-label="scroll to top"><i class="fas fa-arrow-up"></i></button></div></div></div></p><h3 id="ServletConfig"><a href="#ServletConfig" class="headerlink" title="ServletConfig"></a>ServletConfig</h3><ul><li>什么是<code>ServletConfig</code>？ <ul><li>ServletConfig是一个接口。是Servlet规范中的一员。</li><li>ServletConfig对象中封装了标签中的配置信息。（web.xml文件中servlet的配置信息）</li></ul></li><li>一个Servlet对应一个ServletConfig对象。 </li><li>Servlet对象是Tomcat服务器创建，并且ServletConfig对象也是Tomcat服务器创建。并且默认情况下，他们都是在用户发送第一次请求的时候创建。 </li><li>Tomcat服务器调用Servlet对象的init方法的时候需要传一个ServletConfig对象的参数给init方法。 </li><li>ServletConfig接口的实现类是Tomcat服务器给实现的。（Tomcat服务器说的就是WEB服务器。） </li><li>ServletConfig接口有哪些常用的方法？ <figure class="highlight java"><table><tr><td class="gutter"><pre><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="keyword">public</span> String <span class="title function_">getInitParameter</span><span class="params">(String name)</span>; <span class="comment">// 通过初始化参数的name获取value</span></span><br><span class="line"><span class="keyword">public</span> Enumeration&lt;String&gt; <span class="title function_">getInitParameterNames</span><span class="params">()</span>; <span class="comment">// 获取所有的初始化参数的name</span></span><br><span class="line"><span class="keyword">public</span> ServletContext <span class="title function_">getServletContext</span><span class="params">()</span>; <span class="comment">// 获取ServletContext对象</span></span><br><span class="line"><span class="keyword">public</span> String <span class="title function_">getServletName</span><span class="params">()</span>; <span class="comment">// 获取Servlet的name</span></span><br></pre></td></tr></table></figure>以上方法在Servlet类当中，都可以使用this去调用。因为GenericServlet实现了ServletConfig接口。<h3 id="ServletContext-应用域"><a href="#ServletContext-应用域" class="headerlink" title="ServletContext(应用域)"></a>ServletContext(应用域)</h3></li><li>一个ServletContext对象通常对应的是一个web.xml文件。 </li><li>只要在同一个webapp当中，只要在同一个应用当中，所有的Servlet对象都是共享同一个ServletContext对象的。</li><li>ServletContext对象在服务器启动阶段创建，在服务器关闭的时候销毁。这就是ServletContext对象的生命周期。ServletContext对象是应用级对象。</li><li>ServletContext是一个接口，Tomcat服务器对ServletContext接口进行了实现。 <ul><li>ServletContext对象的创建也是Tomcat服务器来完成的。启动webapp的时候创建的。</li></ul></li></ul><p>ServletContext接口中有哪些常用的方法？<br><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="keyword">public</span> String <span class="title function_">getInitParameter</span><span class="params">(String name)</span>; <span class="comment">// 通过初始化参数的name获取value</span></span><br><span class="line"><span class="keyword">public</span> Enumeration&lt;String&gt; <span class="title function_">getInitParameterNames</span><span class="params">()</span>; <span class="comment">// 获取所有的初始化参数的name</span></span><br></pre></td></tr></table></figure><br>通过该两个方法可以获得以下的信息：<br><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></pre></td><td class="code"><pre><span class="line"><span class="comment">&lt;!--以上两个方法是ServletContext对象的方法，这个方法获取的是什么信息？是以下的配置信息--&gt;</span></span><br><span class="line"><span class="tag">&lt;<span class="name">context-param</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">param-name</span>&gt;</span>pageSize<span class="tag">&lt;/<span class="name">param-name</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">param-value</span>&gt;</span>10<span class="tag">&lt;/<span class="name">param-value</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;/<span class="name">context-param</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;<span class="name">context-param</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">param-name</span>&gt;</span>startIndex<span class="tag">&lt;/<span class="name">param-name</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">param-value</span>&gt;</span>0<span class="tag">&lt;/<span class="name">param-value</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;/<span class="name">context-param</span>&gt;</span></span><br><span class="line"><span class="comment">&lt;!--注意：以上的配置信息属于应用级的配置信息，一般一个项目中共享的配置信息会放到以上的标签当中。--&gt;</span></span><br><span class="line"><span class="comment">&lt;!--如果你的配置信息只是想给某一个servlet作为参考，那么你配置到servlet标签当中即可，使用ServletConfig对象来获取。--&gt;</span></span><br></pre></td></tr></table></figure><br>其他方法：<br><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><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">// 获取应用的根路径（非常重要），因为在java源代码当中有一些地方可能会需要应用的根路径，这个方法可以动态获取应用的根路径</span></span><br><span class="line"><span class="comment">// 在java源码当中，不要将应用的根路径写死，因为你永远都不知道这个应用在最终部署的时候，起一个什么名字。</span></span><br><span class="line"><span class="keyword">public</span> String <span class="title function_">getContextPath</span><span class="params">()</span>;</span><br><span class="line"><span class="comment">//String contextPath = application.getContextPath();</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> String <span class="title function_">getRealPath</span><span class="params">(String path)</span>;</span><br><span class="line"></span><br><span class="line"><span class="comment">// 通过ServletContext对象也是可以记录日志的</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">log</span><span class="params">(String message)</span>;</span><br><span class="line"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">log</span><span class="params">(String message, Throwable t)</span>;</span><br><span class="line"><span class="comment">// 这些日志信息记录到哪里了？</span></span><br><span class="line"><span class="comment">// localhost.2021-11-05.log</span></span><br><span class="line"><span class="comment">// Tomcat服务器的logs目录下都有哪些日志文件？</span></span><br><span class="line"><span class="comment">//catalina.2021-11-05.log 服务器端的java程序运行的控制台信息。</span></span><br><span class="line"><span class="comment">//localhost.2021-11-05.log ServletContext对象的log方法记录的日志信息存储到这个文件中。</span></span><br><span class="line"><span class="comment">//localhost_access_log.2021-11-05.txt 访问日志</span></span><br><span class="line"></span><br><span class="line"><span class="comment">// ServletContext对象还有另一个名字：应用域（后面还有其他域，例如：请求域、会话域）</span></span><br><span class="line"><span class="comment">// 如果所有的用户共享一份数据，并且这个数据很少的被修改，并且这个数据量很少，</span></span><br><span class="line"><span class="comment">// 可以将这些数据放到ServletContext这个应用域中</span></span><br><span class="line"><span class="comment">// 存（怎么向ServletContext应用域中存数据）</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">setAttribute</span><span class="params">(String name, Object value)</span>; <span class="comment">// map.put(k, v)</span></span><br><span class="line"><span class="comment">// 取（怎么从ServletContext应用域中取数据）</span></span><br><span class="line"><span class="keyword">public</span> Object <span class="title function_">getAttribute</span><span class="params">(String name)</span>; <span class="comment">// Object v = map.get(k)</span></span><br><span class="line"><span class="comment">// 删（怎么删除ServletContext应用域中的数据）</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">removeAttribute</span><span class="params">(String name)</span>; <span class="comment">// map.remove(k)</span></span><br></pre></td></tr></table></figure></p><div class="note warning no-icon flat"><p>以后我们编写Servlet类的时候，实际上是不会去直接继承GenericServlet类的，因为我们是B/S结构的系统，这种系统是基于HTTP超文本传输协议的，在Servlet规范当中，提供了一个类叫做HttpServlet，它是专门为HTTP协议准备的一个Servlet类。我们编写的Servlet类要继承HttpServlet。（HttpServlet是HTTP协议专用的。）使用HttpServlet处理HTTP协议更便捷。但是我们需要知道它的继承结构：<br><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><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">jakarta.servlet.Servlet（接口）【爷爷】</span><br><span class="line">jakarta.servlet.GenericServlet <span class="keyword">implements</span> <span class="title class_">Servlet</span>（抽象类）【儿子】</span><br><span class="line">jakarta.servlet.http.HttpServlet <span class="keyword">extends</span> <span class="title class_">GenericServlet</span>（抽象类）【孙子】</span><br><span class="line"></span><br><span class="line">我们以后编写的Servlet要继承HttpServlet类。</span><br></pre></td></tr></table></figure></p></div><h2 id="HttpServlet"><a href="#HttpServlet" class="headerlink" title="HttpServlet"></a>HttpServlet</h2><div class="note warning no-icon flat"><ul><li><strong>HttpServlet类是专门为HTTP协议准备的。比GenericServlet更加适合HTTP协议下的开发。</strong></li></ul></div><ul><li><p><strong>HttpServlet在哪个包下？</strong></p><ul><li>jakarta.servlet.http.HttpServlet</li></ul></li><li><p><strong>http包下都有哪些类和接口呢？</strong>jakarta.servlet.http.*; </p><ul><li>jakarta.servlet.http.HttpServlet （HTTP协议专用的Servlet类，抽象类）</li><li>jakarta.servlet.http.HttpServletRequest （HTTP协议专用的请求对象）</li><li>jakarta.servlet.http.HttpServletResponse （HTTP协议专用的响应对象）</li></ul></li><li><p><strong>HttpServletRequest对象中封装了什么信息？</strong> </p><ul><li>HttpServletRequest，简称<code>request对象</code>。</li><li>HttpServletRequest中封装了请求协议的全部内容。</li><li>Tomcat服务器（WEB服务器）将“请求协议”中的数据全部解析出来，然后将这些数据全部封装到request对象当中了。</li></ul></li></ul><div class="note warning no-icon flat"><ul><li>我们只需要面向HttpServletRequest，就可以获取请求协议中的数据。</li><li>HttpServletResponse对象是专门用来响应HTTP协议到浏览器的。</li></ul></div><ul><li><strong>回忆Servlet生命周期？</strong><ul><li>用户第一次请求 <ul><li>Tomcat服务器通过反射机制，调用无参数构造方法。创建Servlet对象。(web.xml文件中配置的Servlet类对应的对象。)</li><li>Tomcat服务器调用Servlet对象的init方法完成初始化。</li><li>Tomcat服务器调用Servlet对象的service方法处理请求。</li></ul></li><li>用户第二次请求 <ul><li>Tomcat服务器调用Servlet对象的service方法处理请求。</li></ul></li><li>用户第三次请求 <ul><li>Tomcat服务器调用Servlet对象的service方法处理请求。</li></ul></li><li>…. <ul><li>Tomcat服务器调用Servlet对象的service方法处理请求。</li></ul></li><li>服务器关闭 <ul><li>Tomcat服务器调用Servlet对象的destroy方法，做销毁之前的准备工作。</li><li>Tomcat服务器销毁Servlet对象。</li></ul></li></ul></li><li>HttpServlet源码分析：</li></ul><div class="tabs" id="httpservlet"><ul class="nav-tabs"><li class="tab active"><button type="button" data-href="#httpservlet-1">HelloServlet</button></li><li class="tab"><button type="button" data-href="#httpservlet-2">GenericServlet</button></li><li class="tab"><button type="button" data-href="#httpservlet-3">HttpServlet</button></li></ul><div class="tab-contents"><div class="tab-item-content active" id="httpservlet-1"><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><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_">HelloServlet</span> <span class="keyword">extends</span> <span class="title class_">HttpServlet</span> &#123;</span><br><span class="line"><span class="comment">// 用户第一次请求，创建HelloServlet对象的时候，会执行这个无参数构造方法。</span></span><br><span class="line"><span class="keyword">public</span> <span class="title function_">HelloServlet</span><span class="params">()</span> &#123;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><button type="button" class="tab-to-top" aria-label="scroll to top"><i class="fas fa-arrow-up"></i></button></div><div class="tab-item-content" id="httpservlet-2"><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><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">public</span> <span class="keyword">abstract</span> <span class="keyword">class</span> <span class="title class_">GenericServlet</span> <span class="keyword">implements</span> <span class="title class_">Servlet</span>, ServletConfig,</span><br><span class="line">        java.io.Serializable &#123;</span><br><span class="line">           </span><br><span class="line"><span class="comment">/* 用户第一次请求的时候，HelloServlet对象第一次被创建之后，因为HelloServlet</span></span><br><span class="line"><span class="comment">       没有init方法，HttpServlet也没有，所以执行GenericServlet的init有参构造.。  */</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">this</span>.config = config;</span><br><span class="line">        <span class="built_in">this</span>.init();</span><br><span class="line">    &#125;</span><br><span class="line"><span class="comment">// 用户第一次请求的时候，带有参数的init(ServletConfig config)执行之后，会执行这个没有参数的init()</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">// NOOP by default</span></span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><button type="button" class="tab-to-top" aria-label="scroll to top"><i class="fas fa-arrow-up"></i></button></div><div class="tab-item-content" id="httpservlet-3"><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><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="comment">// HttpServlet模板类。</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_">HttpServlet</span> <span class="keyword">extends</span> <span class="title class_">GenericServlet</span> &#123;</span><br><span class="line">    <span class="comment">// 用户发送第一次请求的时候这个service会执行(HelloServlet没有service,执行Httpservlet的)</span></span><br><span class="line">    <span class="comment">// 用户发送第N次请求的时候，这个service方法还是会执行。</span></span><br><span class="line">    <span class="comment">// 用户只要发送一次请求，这个service方法就会执行一次。</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_">service</span><span class="params">(ServletRequest req, ServletResponse res)</span></span><br><span class="line">        <span class="keyword">throws</span> ServletException, IOException &#123;</span><br><span class="line"></span><br><span class="line">        HttpServletRequest  request;</span><br><span class="line">        HttpServletResponse response;</span><br><span class="line"></span><br><span class="line">        <span class="keyword">try</span> &#123;</span><br><span class="line">            <span class="comment">// 将ServletRequest和ServletResponse向下转型为带有Http的HttpServletRequest和HttpServletResponse</span></span><br><span class="line">            request = (HttpServletRequest) req;</span><br><span class="line">            response = (HttpServletResponse) res;</span><br><span class="line">        &#125; <span class="keyword">catch</span> (ClassCastException e) &#123;</span><br><span class="line">            <span class="keyword">throw</span> <span class="keyword">new</span> <span class="title class_">ServletException</span>(lStrings.getString(<span class="string">&quot;http.non_http&quot;</span>));</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="comment">// 调用重载的service方法。</span></span><br><span class="line">        service(request, response);</span><br><span class="line">    &#125;</span><br><span class="line">    </span><br><span class="line">    <span class="comment">// 这个service方法的两个参数都是带有Http的。</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="keyword">protected</span> <span class="keyword">void</span> <span class="title function_">service</span><span class="params">(HttpServletRequest req, HttpServletResponse resp)</span></span><br><span class="line">        <span class="keyword">throws</span> ServletException, IOException &#123;</span><br><span class="line">        <span class="comment">// 获取请求方式</span></span><br><span class="line">        <span class="comment">// 这个请求方式最终可能是：&quot;&quot;</span></span><br><span class="line">        <span class="comment">// 注意：request.getMethod()方法获取的是请求方式，可能是七种之一：</span></span><br><span class="line">        <span class="comment">// GET POST PUT DELETE HEAD OPTIONS TRACE</span></span><br><span class="line">        <span class="type">String</span> <span class="variable">method</span> <span class="operator">=</span> req.getMethod();</span><br><span class="line"></span><br><span class="line">        <span class="comment">// 如果请求方式是GET请求，则执行doGet方法。</span></span><br><span class="line">        <span class="keyword">if</span> (method.equals(METHOD_GET)) &#123;</span><br><span class="line">            <span class="type">long</span> <span class="variable">lastModified</span> <span class="operator">=</span> getLastModified(req);</span><br><span class="line">            <span class="keyword">if</span> (lastModified == -<span class="number">1</span>) &#123;</span><br><span class="line">                <span class="comment">// servlet doesn&#x27;t support if-modified-since, no reason</span></span><br><span class="line">                <span class="comment">// to go through further expensive logic</span></span><br><span class="line">                doGet(req, resp);</span><br><span class="line">            &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">                <span class="type">long</span> ifModifiedSince;</span><br><span class="line">                <span class="keyword">try</span> &#123;</span><br><span class="line">                    ifModifiedSince = req.getDateHeader(HEADER_IFMODSINCE);</span><br><span class="line">                &#125; <span class="keyword">catch</span> (IllegalArgumentException iae) &#123;</span><br><span class="line">                    <span class="comment">// Invalid date header - proceed as if none was set</span></span><br><span class="line">                    ifModifiedSince = -<span class="number">1</span>;</span><br><span class="line">                &#125;</span><br><span class="line">                <span class="keyword">if</span> (ifModifiedSince &lt; (lastModified / <span class="number">1000</span> * <span class="number">1000</span>)) &#123;</span><br><span class="line">                    <span class="comment">// If the servlet mod time is later, call doGet()</span></span><br><span class="line">                    <span class="comment">// Round down to the nearest second for a proper compare</span></span><br><span class="line">                    <span class="comment">// A ifModifiedSince of -1 will always be less</span></span><br><span class="line">                    maybeSetLastModified(resp, lastModified);</span><br><span class="line">                    doGet(req, resp);</span><br><span class="line">                &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">                    resp.setStatus(HttpServletResponse.SC_NOT_MODIFIED);</span><br><span class="line">                &#125;</span><br><span class="line">            &#125;</span><br><span class="line"></span><br><span class="line">        &#125; <span class="keyword">else</span> <span class="keyword">if</span> (method.equals(METHOD_HEAD)) &#123;</span><br><span class="line">            <span class="type">long</span> <span class="variable">lastModified</span> <span class="operator">=</span> getLastModified(req);</span><br><span class="line">            maybeSetLastModified(resp, lastModified);</span><br><span class="line">            doHead(req, resp);</span><br><span class="line"></span><br><span class="line">        &#125; <span class="keyword">else</span> <span class="keyword">if</span> (method.equals(METHOD_POST)) &#123;</span><br><span class="line">            <span class="comment">// 如果请求方式是POST请求，则执行doPost方法。</span></span><br><span class="line">            doPost(req, resp);</span><br><span class="line"></span><br><span class="line">        &#125; <span class="keyword">else</span> <span class="keyword">if</span> (method.equals(METHOD_PUT)) &#123;</span><br><span class="line">            doPut(req, resp);</span><br><span class="line"></span><br><span class="line">        &#125; <span class="keyword">else</span> <span class="keyword">if</span> (method.equals(METHOD_DELETE)) &#123;</span><br><span class="line">            doDelete(req, resp);</span><br><span class="line"></span><br><span class="line">        &#125; <span class="keyword">else</span> <span class="keyword">if</span> (method.equals(METHOD_OPTIONS)) &#123;</span><br><span class="line">            doOptions(req,resp);</span><br><span class="line"></span><br><span class="line">        &#125; <span class="keyword">else</span> <span class="keyword">if</span> (method.equals(METHOD_TRACE)) &#123;</span><br><span class="line">            doTrace(req,resp);</span><br><span class="line"></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="comment">// Note that this means NO servlet supports whatever</span></span><br><span class="line">            <span class="comment">// method was requested, anywhere on this server.</span></span><br><span class="line">            <span class="comment">//</span></span><br><span class="line"></span><br><span class="line">            <span class="type">String</span> <span class="variable">errMsg</span> <span class="operator">=</span> lStrings.getString(<span class="string">&quot;http.method_not_implemented&quot;</span>);</span><br><span class="line">            Object[] errArgs = <span class="keyword">new</span> <span class="title class_">Object</span>[<span class="number">1</span>];</span><br><span class="line">            errArgs[<span class="number">0</span>] = method;</span><br><span class="line">            errMsg = MessageFormat.format(errMsg, errArgs);</span><br><span class="line"></span><br><span class="line">            resp.sendError(HttpServletResponse.SC_NOT_IMPLEMENTED, errMsg);</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">/*子类没有重写doGet就会走父类的，报405错误*/</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 req, HttpServletResponse resp)</span></span><br><span class="line">        <span class="keyword">throws</span> ServletException, IOException&#123;</span><br><span class="line">        <span class="comment">// 报405错误</span></span><br><span class="line">        <span class="type">String</span> <span class="variable">msg</span> <span class="operator">=</span> lStrings.getString(<span class="string">&quot;http.method_get_not_supported&quot;</span>);</span><br><span class="line">        sendMethodNotAllowed(req, resp, msg);</span><br><span class="line">    &#125;</span><br><span class="line">        <span class="comment">/*子类没有重写doPost就会走父类的，报405错误*/</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 req, HttpServletResponse resp)</span></span><br><span class="line">        <span class="keyword">throws</span> ServletException, IOException &#123;</span><br><span class="line">        <span class="comment">// 报405错误</span></span><br><span class="line">        <span class="type">String</span> <span class="variable">msg</span> <span class="operator">=</span> lStrings.getString(<span class="string">&quot;http.method_post_not_supported&quot;</span>);</span><br><span class="line">        sendMethodNotAllowed(req, resp, msg);</span><br><span class="line">    &#125;</span><br><span class="line">    </span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><button type="button" class="tab-to-top" aria-label="scroll to top"><i class="fas fa-arrow-up"></i></button></div></div></div><div class="note info no-icon flat"><ul><li>假设前端发送的请求是<code>get请求</code>，后端程序员重写的方法是<code>doPost</code></li><li>假设前端发送的请求是<code>post请求</code>，后端程序员重写的方法是<code>doGet</code>会发生什么呢？<ul><li>发生405这样的一个错误。405表示前端的错误，发送的请求方式不对。和服务器不一致。不是服务器需要的请求方式。</li><li>405错误，前端收到这个错误，前端会进行相应的处理。 比如：提示用户，请使用正确的请求方式。</li></ul></li><li>通过以上源代码可以知道：只要HttpServlet类中的doGet方法或doPost方法执行了，必然405.</li><li><p>怎么避免405的错误呢？</p><ul><li>后端重写了doGet方法，前端一定要发get请求。</li><li>后端重写了doPost方法，前端一定要发post请求。</li><li>这样可以避免405错误。</li></ul></li><li><p><strong>这种前端到底需要发什么样的请求，其实应该后端说了算。后端让发什么方式，前端就得发什么方式。</strong></p></li></ul></div><ul><li><p>我们编写的HelloServlet直接继承HttpServlet，直接重写HttpServlet类中的service()方法行吗？ </p><ul><li>可以，但是这样将会享受不到405错误。享受不到HTTP协议专属的东西。</li></ul></li><li><p>最终的一个Servlet类的开发步骤： </p><ul><li>第一步：编写一个Servlet类，直接继承HttpServlet</li><li>第二步：重写doGet方法或者重写doPost方法，到底重写谁，javaweb程序员说了算。</li><li>第三步：将Servlet类配置到web.xml文件当中。</li><li>第四步：准备前端的页面（form表单），form表单中指定请求路径即可。</li></ul></li></ul><h3 id="欢迎资源文件"><a href="#欢迎资源文件" class="headerlink" title="欢迎资源文件"></a>欢迎资源文件</h3><ul><li><strong>前提：</strong><ul><li>用户可以记住网站名，但是不会记住网站资源文件名</li></ul></li><li><strong>默认欢迎资源文件:</strong><ul><li>用户发送了一个针对某个网站的【默认请求】时，此时由Http服务器自动从当前网站返回的资源文件</li><li>正常请求： <a href="http://localhost:8080/myWeb/index.html">http://localhost:8080/myWeb/index.html</a></li><li>默认请求： <a href="http://localhost:8080/myWeb/">http://localhost:8080/myWeb/</a></li></ul></li><li><strong>设置当前网站的默认欢迎资源文件规则：</strong><ul><li>规则位置:网站/web/WEB-INF/web.xml</li><li>规则命令:<ul><li><welcome-file-list></li><li><welcome-file>login.html&lt;/welcome-file&gt;</li><li>&lt;/welcome-file-list&gt;</li></ul></li></ul></li><li>网站设置自定义默认文件定位规则，此时Tomcat自带定位规则将失效</li></ul><ul><li>动态资源：Servlet类。 </li><li>步骤： </li><li>第一步：写一个Servlet </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></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">WelcomeServlet</span> <span class="keyword">extends</span> <span class="title class_">HttpServlet</span> &#123;</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, HttpServletResponse response)</span> <span class="keyword">throws</span> ServletException, IOException &#123;</span><br><span class="line">        response.setContentType(<span class="string">&quot;text/html&quot;</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">        out.print(<span class="string">&quot;&lt;h1&gt;welcome to bjpowernode!&lt;/h1&gt;&quot;</span>);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><ul><li>第二步：在web.xml文件中配置servlet </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></pre></td><td class="code"><pre><span class="line">&lt;servlet&gt;</span><br><span class="line">    &lt;servlet-name&gt;welcomeServlet&lt;/servlet-name&gt;</span><br><span class="line">    &lt;servlet-class&gt;com.bjpowernode.javaweb.servlet.WelcomeServlet&lt;/servlet-class&gt;</span><br><span class="line">&lt;/servlet&gt;</span><br><span class="line">&lt;servlet-mapping&gt;</span><br><span class="line">    &lt;servlet-name&gt;welcomeServlet&lt;/servlet-name&gt;</span><br><span class="line">    &lt;url-pattern&gt;http:<span class="comment">//localhost:8080/myWeb/login.html&lt;/url-pattern&gt;</span></span><br><span class="line">&lt;/servlet-mapping&gt;</span><br></pre></td></tr></table></figure><p> 第三步：在web.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></pre></td><td class="code"><pre><span class="line">&lt;welcome-file-list&gt;</span><br><span class="line">    &lt;welcome-file&gt;http:<span class="comment">//localhost:8080/myWeb/login.html&lt;/welcome-file&gt;</span></span><br><span class="line">&lt;/welcome-file-list&gt;</span><br></pre></td></tr></table></figure><div class="note info no-icon flat"><ul><li>关于WEB-INF目录<ul><li>在WEB-INF目录下新建了一个文件：welcome.html</li><li>打开浏览器访问：<a href="http://localhost:8080/servlet07/WEB-INF/welcome.html">http://localhost:8080/servlet07/WEB-INF/welcome.html</a> 出现了404错误。</li><li><strong>注意：放在WEB-INF目录下的资源是受保护的。在浏览器上不能够通过路径直接访问。所以像HTML、CSS、JS、image等静态资源一定要放到WEB-INF目录之外。</strong></li></ul></li></ul></div><h2 id="HttpServletRequest接口详解"><a href="#HttpServletRequest接口详解" class="headerlink" title="HttpServletRequest接口详解"></a>HttpServletRequest接口详解</h2><ul><li><code>HttpServletRequest</code>是一个接口，全限定名称：jakarta.servlet.http.HttpServletRequest </li><li><p>HttpServletRequest对象中都有什么信息？都包装了什么信息？ </p><ul><li><strong>HttpServletRequest对象是Tomcat服务器负责创建的。</strong>这个对象中封装了HTTP的请求协议。</li><li>实际上是用户发送请求的时候，遵循了HTTP协议，发送的是HTTP的请求协议，Tomcat服务器将HTTP协议中的信息以及数据全部解析出来，然后Tomcat服务器把这些信息封装到HttpServletRequest对象当中，传给了我们javaweb程序员。</li><li>javaweb程序员面向HttpServletRequest接口编程，调用方法就可以获取到请求的信息了。</li></ul></li><li><p>request和response对象的生命周期？ </p><ul><li>request对象和response对象，一个是<code>请求对象</code>，一个是<code>响应对象</code>。这两个对象只在当前请求中有效。</li><li>一次请求对应一个request。</li><li>两次请求则对应两个request。</li></ul></li><li>…..</li></ul><h3 id="常用的方法"><a href="#常用的方法" class="headerlink" title="常用的方法"></a>常用的方法</h3><h4 id="获取用户提交的数据"><a href="#获取用户提交的数据" class="headerlink" title="获取用户提交的数据"></a>获取用户提交的数据</h4><ul><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></pre></td><td class="code"><pre><span class="line">Map&lt;String,String[]&gt; getParameterMap() 这个是获取Map</span><br><span class="line">Enumeration&lt;String&gt; <span class="title function_">getParameterNames</span><span class="params">()</span> 这个是获取Map集合中所有的key</span><br><span class="line">String[] getParameterValues(String name) 根据key获取Map集合的value</span><br><span class="line">String <span class="title function_">getParameter</span><span class="params">(String name)</span>  获取value这个一维数组当中的第一个元素。这个方法最常用。</span><br><span class="line"><span class="comment">// 以上的4个方法，和获取用户提交的数据有关系。</span></span><br></pre></td></tr></table></figure><details class="folding-tag" ><summary> 思考：如果是你，前端的form表单提交了数据之后，你准备怎么存储这些数据，你准备采用什么样的数据结构去存储这些数据呢？ </summary>              <div class='content'>              <ul><li>前端提交的数据格式：username=abc&amp;userpwd=111&amp;aihao=s&amp;aihao=d&amp;aihao=tt </li><li>我会采用Map集合来存储： </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">Map&lt;String,String&gt;</span><br><span class="line">    key存储String</span><br><span class="line">    value存储String</span><br><span class="line">    这种想法对吗？不对。</span><br><span class="line">    如果采用以上的数据结构存储会发现key重复的时候value覆盖。</span><br><span class="line">    key         value</span><br><span class="line">    ---------------------</span><br><span class="line">    username    abc</span><br><span class="line">    userpwd     <span class="number">111</span></span><br><span class="line">    aihao       s</span><br><span class="line">    aihao       d</span><br><span class="line">    aihao       tt</span><br><span class="line">    这样是不行的，因为map的key不能重复。</span><br><span class="line">Map&lt;String, String[]&gt;</span><br><span class="line">    key存储String</span><br><span class="line">    value存储String[]</span><br><span class="line">    keyvalue</span><br><span class="line">    -------------------------------</span><br><span class="line">    username&#123;<span class="string">&quot;abc&quot;</span>&#125;</span><br><span class="line">    userpwd&#123;<span class="string">&quot;111&quot;</span>&#125;</span><br><span class="line">    aihao&#123;<span class="string">&quot;s&quot;</span>,<span class="string">&quot;d&quot;</span>,<span class="string">&quot;tt&quot;</span>&#125;</span><br></pre></td></tr></table></figure><ul><li>注意：前端表单提交数据的时候，假设提交了120这样的“数字”，其实是以字符串”120”的方式提交的，所以服务器端获取到的一定是一个字符串的”120”，而不是一个数字。（前端永远提交的是字符串，后端获取的也永远是字符串。）</li></ul>              </div>            </details><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><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">//username=zhangsan&amp;userpwd=123&amp;interest=s&amp;interest=d</span></span><br><span class="line"><span class="comment">/*Map&lt;String,String[]&gt;</span></span><br><span class="line"><span class="comment">     keyvalue</span></span><br><span class="line"><span class="comment">  ---------------------------</span></span><br><span class="line"><span class="comment">  &quot;username&quot;&#123;&quot;zhangsan&quot;&#125;</span></span><br><span class="line"><span class="comment">  &quot;userpwd&quot;    &#123;&quot;123&quot;&#125;</span></span><br><span class="line"><span class="comment">  &quot;interest&quot;&#123;&quot;s&quot;, &quot;d&quot;&#125;  */</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_">doPost</span><span class="params">(HttpServletRequest request, HttpServletResponse response)</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">/*1.最常用的方式  String getParameter(String name)*/</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">userpwd</span> <span class="operator">=</span> request.getParameter(<span class="string">&quot;userpwd&quot;</span>);</span><br><span class="line">  </span><br><span class="line">  System.out.println(username);</span><br><span class="line">         System.out.println(userpwd);</span><br><span class="line">  </span><br><span class="line">  </span><br><span class="line"><span class="comment">/*2.既然是checkbox，调用方法：request.getParameterValues(&quot;interest&quot;)*/</span></span><br><span class="line">      String[] interests = request.getParameterValues(<span class="string">&quot;interest&quot;</span>);</span><br><span class="line"></span><br><span class="line">  <span class="keyword">for</span>(String  i : interests)&#123;</span><br><span class="line">            System.out.println(i);</span><br><span class="line">        &#125;</span><br><span class="line"></span><br><span class="line">  <span class="comment">/*3.直接通过getParameterNames()这个方法，可以直接获取这个Map集合的所有key*/</span></span><br><span class="line">        Enumeration&lt;String&gt; names = request.getParameterNames();</span><br><span class="line">        <span class="keyword">while</span>(names.hasMoreElements())&#123;</span><br><span class="line">            <span class="type">String</span> <span class="variable">name</span> <span class="operator">=</span> names.nextElement();</span><br><span class="line">            System.out.println(name);</span><br><span class="line">        &#125;</span><br><span class="line"></span><br><span class="line">  </span><br><span class="line">  <span class="comment">/*4.获取参数Map集合*/</span></span><br><span class="line">        Map&lt;String,String[]&gt; parameterMap = request.getParameterMap();</span><br><span class="line">        <span class="comment">// 遍历Map集合(获取Map集合中所有的key，遍历)</span></span><br><span class="line">        Set&lt;String&gt; keys = parameterMap.keySet();</span><br><span class="line">        Iterator&lt;String&gt; it = keys.iterator();</span><br><span class="line">        <span class="keyword">while</span>(it.hasNext())&#123;</span><br><span class="line">            <span class="type">String</span> <span class="variable">key</span> <span class="operator">=</span> it.next();</span><br><span class="line">  System.out.print(key+<span class="string">&quot;=&quot;</span>);</span><br><span class="line">            <span class="comment">// 通过key获取value</span></span><br><span class="line">            String[] values = parameterMap.get(key);</span><br><span class="line">          <span class="keyword">for</span>(String value : values)&#123;</span><br><span class="line">                System.out.print(value+<span class="string">&quot;,&quot;</span>);</span><br><span class="line">            &#125;</span><br><span class="line"></span><br><span class="line">            System.out.println();</span><br><span class="line">          </span><br><span class="line">          <span class="comment">/*4.输出结果</span></span><br><span class="line"><span class="comment">          username=zhangsan,</span></span><br><span class="line"><span class="comment">userpwd=123,</span></span><br><span class="line"><span class="comment">interest=s,d,</span></span><br><span class="line"><span class="comment"></span></span><br><span class="line"><span class="comment">&#125;</span></span><br></pre></td></tr></table></figure><h4 id="HttpServletRequest接口的其他常用方法："><a href="#HttpServletRequest接口的其他常用方法：" class="headerlink" title="HttpServletRequest接口的其他常用方法："></a>HttpServletRequest接口的其他常用方法：</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></pre></td><td class="code"><pre><span class="line"><span class="comment">// 获取客户端的IP地址</span></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><br><span class="line"><span class="comment">// get请求在请求行上提交数据。</span></span><br><span class="line"><span class="comment">// post请求在请求体中提交数据。</span></span><br><span class="line"><span class="comment">// 设置请求体的字符集。（显然这个方法是处理POST请求的乱码问题。这种方式并不能解决get请求的乱码问题。）</span></span><br><span class="line"><span class="comment">// Tomcat10之后，request请求体当中的字符集默认就是UTF-8，不需要设置字符集，不会出现乱码问题。</span></span><br><span class="line"><span class="comment">// Tomcat9前（包括9在内），如果前端请求体提交的是中文，后端获取之后出现乱码，怎么解决这个乱码？执行以下代码。</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="comment">// 在Tomcat9之前（包括9），响应中文也是有乱码的，怎么解决这个响应的乱码？</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">// 在Tomcat10之后，包括10在内，响应中文的时候就不在出现乱码问题了。以上代码就不需要设置UTF-8了。</span></span><br><span class="line"></span><br><span class="line"><span class="comment">// 注意一个细节</span></span><br><span class="line"><span class="comment">// 在Tomcat10包括10在内之后的版本，中文将不再出现乱码。（这也体现了中文地位的提升。）</span></span><br><span class="line"></span><br><span class="line"><span class="comment">// get请求乱码问题怎么解决？</span></span><br><span class="line"><span class="comment">// get请求发送的时候，数据是在请求行上提交的，不是在请求体当中提交的。</span></span><br><span class="line"><span class="comment">// get请求乱码怎么解决</span></span><br><span class="line"><span class="comment">// 方案：修改CATALINA_HOME/conf/server.xml配置文件</span></span><br><span class="line">&lt;Connector URIEncoding=<span class="string">&quot;UTF-8&quot;</span> /&gt;</span><br><span class="line"><span class="comment">// 注意：从Tomcat8之后，URIEncoding的默认值就是UTF-8，所以GET请求也没有乱码问题了。</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">contextPath</span> <span class="operator">=</span> request.getContextPath();</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><br><span class="line"></span><br><span class="line"><span class="comment">// 获取请求的URI</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">// /aaa/testRequest</span></span><br><span class="line"></span><br><span class="line"><span class="comment">// 获取servlet path</span></span><br><span class="line"><span class="type">String</span> <span class="variable">servletPath</span> <span class="operator">=</span> request.getServletPath(); <span class="comment">//   /testRequest</span></span><br></pre></td></tr></table></figure><h3 id="请求域对象request"><a href="#请求域对象request" class="headerlink" title="请求域对象request"></a>请求域对象request</h3><ul><li><code>请求域</code>对象要比<code>应用域</code>对象范围小很多。生命周期短很多。请求域只在一次请求内有效。</li><li>一个请求对象request对应一个请求域对象。一次请求结束之后，这个请求域就销毁了。</li></ul><ul><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></pre></td><td class="code"><pre><span class="line"><span class="comment">// 存（怎么向request请求域中存数据）</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">setAttribute</span><span class="params">(String name, Object value)</span>; <span class="comment">// map.put(k, v)</span></span><br><span class="line"><span class="comment">// 取（怎么从request请求域中取数据）</span></span><br><span class="line"><span class="keyword">public</span> Object <span class="title function_">getAttribute</span><span class="params">(String name)</span>; <span class="comment">// Object v = map.get(k)</span></span><br><span class="line"><span class="comment">// 删（怎么删除request请求域中的数据）</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">removeAttribute</span><span class="params">(String name)</span>; <span class="comment">// map.remove(k)</span></span><br></pre></td></tr></table></figure><ul><li><strong>请求域和应用域的选用原则:</strong>尽量使用小的域对象，因为小的域对象占用的资源较少。</li></ul><div class="note dafult no-icon flat"><p>两个Servlet怎么共享数据？</p><ul><li>将数据放到ServletContext应用域当中，当然是可以的，但是应用域范围太大，占用资源太多。不建议使用。</li><li>可以将数据放到request域当中，然后AServlet转发到BServlet，保证AServlet和BServlet在同一次请求当中，这样就可以做到两个Servlet，或者多个Servlet共享同一份数据。</li></ul></div><h3 id="转发"><a href="#转发" class="headerlink" title="转发"></a>转发</h3><p><strong>原理：</strong>是由WEB服务器来控制的。A资源跳转到B资源，这个跳转动作是Tomcat服务器内部完成的。</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="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;/b&quot;</span>);</span><br><span class="line"><span class="comment">// 第二步：调用转发器的forward方法完成跳转/转发</span></span><br><span class="line">dispatcher.forward(request,response);</span><br><span class="line">            </span><br><span class="line"><span class="comment">// 第一步和第二步代码可以联合在一起。</span></span><br><span class="line"> request.getRequestDispatcher(<span class="string">&quot;/b&quot;</span>).forward(request,response);</span><br><span class="line"></span><br><span class="line"><span class="comment">//注意:转发的时候是一次请求,会将当前的request和response对象传递给下一个servlet.</span></span><br><span class="line">         </span><br></pre></td></tr></table></figure><ul><li>转发的下一个资源必须是一个Servlet吗？<ul><li>不一定，只要是Tomcat服务器当中的合法资源，都是可以转发的。例如：html….</li></ul></li></ul><div class="note warning no-icon flat"><p>注意：转发的时候，路径的写法要注意，转发的路径以“/”开始，不加项目名。</p></div><p><strong>关于request对象中两个非常容易混淆的方法：</strong><br><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><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">// uri?username=zhangsan&amp;userpwd=123&amp;sex=1</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><br><span class="line"><span class="comment">// 之前一定是执行过：request.setAttribute(&quot;name&quot;, new Object())</span></span><br><span class="line"><span class="type">Object</span> <span class="variable">obj</span> <span class="operator">=</span> request.getAttribute(<span class="string">&quot;name&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></pre></td></tr></table></figure><br><div class="img-wrap"><div class="img-bg"><img class="img" src="https://raw.githubusercontent.com/silvan2077/markdown_pic/main/Picgo/20250925213814.png"/></div></div></p><div class="note warning no-icon flat"><p>特点:<br>在请求转发过程中，浏览器只发送一个了个Http请求协议包。<br>参与本次请求的所有Servlet共享同一个请求协议包，因此这些Servlet接收的请求方式与浏览器发送的请求方式保持一致</p></div><h3 id="重定向"><a href="#重定向" class="headerlink" title="重定向"></a>重定向</h3><p><strong><em>工作原理:</em></strong>由浏览器来完成。具体跳转到哪个资源，是浏览器说了算。<br><strong><em>实现命令：</em></strong><br><figure class="highlight java"><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">response.sendRedirect(<span class="string">&quot;请求地址&quot;</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></p><p><strong><em>特征:</em></strong></p><ul><li>请求地址：<ul><li>可以把当前网站内部的资源文件地址发送给浏览器 （/网站名/资源文件名）</li><li>可以把其他网站资源文件地址发送给浏览器(<a href="http://ip地址:端口号/网站名/资源文件名">http://ip地址:端口号/网站名/资源文件名</a>)</li></ul></li><li>请求次数：    <ul><li>浏览器至少发送两次请求，但是只有第一次请求是用户手动发送。</li><li>后续请求都是浏览器自动发送的。</li></ul></li><li>请求方式：<ul><li>重定向解决方案中，通过地址栏通知浏览器发起下一次请求，因此通过重定向解决方案调用的资源文件接收的请求方式一定是<code>GET</code><br><strong><em>缺点:</em></strong><br>重定向解决方案需要在浏览器与服务器之间进行多次往返，大量时间消耗在往返次数上，增加用户等待服务时间<div class="img-wrap"><div class="img-bg"><img class="img" src="https://raw.githubusercontent.com/silvan2077/markdown_pic/main/Picgo/20250925214354.png"/></div></div></li></ul></li></ul><h4 id="转发和重定向区别"><a href="#转发和重定向区别" class="headerlink" title="转发和重定向区别"></a>转发和重定向区别</h4><p>转发和重定向应该如何选择?什么时候使用转发，什么时候使用重定向?</p><ul><li>如果在上一个Servlet当中向request域当中绑定了数据，希望从下一个Servlet当中把request域里面的数据取出来，使用转发机制。</li><li>剩下所有的请求均使用重定向。(重定向使用较多。)</li><li>他俩跳转的资源只要是服务器内部合法的资源即可。(Servlet,JSP,HTML)…….</li><li>转发会存在浏览器刷新问题,而重定向不会</li></ul><h2 id="Session和Cookie"><a href="#Session和Cookie" class="headerlink" title="Session和Cookie"></a>Session和Cookie</h2><h3 id="Session"><a href="#Session" class="headerlink" title="Session"></a>Session</h3><ul><li>什么是会话？<ul><li>用户打开浏览器，进行一系列操作，然后最终将浏览器关闭，这个整个过程叫做：一次会话。会话在服务器端也有一个对应的java对象，这个java对象叫做：session。</li><li>什么是一次请求：用户在浏览器上点击了一下，然后到页面停下来，可以粗略认为是一次请求。请求对应的服务器端的java对象是：<code>request</code></li></ul></li></ul><div class="note warning no-icon flat"><ul><li>一个会话当中包含多次请求。（一次会话对应N次请求。）</li></ul></div><ul><li>在java的servlet规范当中，session对应的类名：<code>HttpSession</code>（jarkata.servlet.http.HttpSession）</li><li>session机制属于B/S结构的一部分。如果使用php语言开发WEB项目，同样也是有session这种机制的。session机制实际上是一个规范。不同的语言对这种会话机制都有实现。</li><li>session对象最主要的作用是：<code>保存会话状态。</code>（用户登录成功了，这是一种登录成功的状态，你怎么把登录成功的状态一直保存下来呢？使用session对象可以保留会话状态。）</li><li>为什么需要session对象来保存会话状态呢？<ul><li>因为HTTP协议是一种<code>无状态协议</code>。</li><li>什么是无状态：请求的时候，B和S是连接的，但是请求结束之后，连接就断了。HTTP协议为什么要设计成这样？因为这样的无状态协议，可以<strong>降低服务器的压力</strong>。请求的瞬间是连接的，请求结束之后，连接断开，这样服务器压力小。</li><li>B和S断开以后，关闭浏览器这个动作，服务器都不会知道</li></ul></li><li>张三打开一个浏览器A，李四打开一个浏览器B，访问服务器之后，在服务器端会生成：<ul><li>张三专属的session对象</li><li>李四专属的session对象</li><li>每个人通过getSession()方法获取的session对象获得的都是他们专属的session对象</li></ul></li><li>为什么不使用request对象保存会话状态？为什么不使用ServletContext对象保存会话状态？<ul><li>request.setAttribute()存，request.getAttribute()取，ServletContext也有这个方法。request是请求域。ServletContext是应用域。</li><li>ServletContext对象的域太大。request对象域太小，均不适合保存会话状态。</li><li>request请求域（HttpServletRequest）、session会话域（HttpSession）、application域（ServletContext）</li><li><strong>request &lt; session &lt; application</strong></li></ul></li></ul><p><strong><em>session的实现原理：</em></strong></p><ul><li>JSESSIONID=xxxxxx 这个是以Cookie的形式保存在浏览器的内存中的。浏览器只要关闭。这个cookie就没有了。</li><li>session列表是一个Map，map的key是sessionid，map的value是session对象。</li><li>用户第一次请求，服务器生成session对象，同时生成id，将id发送给浏览器。</li><li>用户第二次请求，自动将浏览器内存中的id发送给服务器，服务器根据id查找session对象。</li><li><p>关闭浏览器，内存消失，cookie消失，sessionid消失，会话等同于结束</p><div class="img-wrap"><div class="img-bg"><img class="img" src="https://raw.githubusercontent.com/silvan2077/markdown_pic/main/Picgo/20250925215605.png"/></div></div></li><li><p>Cookie禁用了，session还能找到吗？</p><ul><li>cookie禁用是什么意思？服务器正常发送cookie给浏览器，但是浏览器不要了。<strong>拒收了。并不是服务器不发了。</strong></li><li>找不到了。每一次请求都会获取到新的session对象。</li><li>cookie禁用了，session机制还能实现吗？<ul><li>可以。需要使用URL重写机制。</li><li><a href="http://localhost:8080/servlet12/test/session;jsessionid=19D1C99560DCBF84839FA43D58F56E16">http://localhost:8080/servlet12/test/session;jsessionid=19D1C99560DCBF84839FA43D58F56E16</a></li><li>URL重写机制会提高开发者的成本。开发人员在编写任何请求路径的时候，后面都要添加一个sessionid，给开发带来了很大的难度，很大的成本。所以大部分的网站都是这样设计的：你要是禁用cookie，你就别用了。</li></ul></li></ul></li></ul><div class="note danger no-icon flat"><ul><li>总结一下到目前位置我们所了解的域对象：<ul><li>request（对应的类名：HttpServletRequest）<ul><li>请求域（请求级别的）</li></ul></li><li>session（对应的类名：HttpSession）<ul><li>会话域（用户级别的）</li></ul></li><li>application（对应的类名：ServletContext）<ul><li>应用域（项目级别的，所有用户共享的。）</li></ul></li><li>这三个域对象的大小关系<ul><li>request &lt; session &lt; application</li></ul></li><li>他们三个域对象都有以下三个公共的方法：<ul><li>setAttribute（向域当中绑定数据）</li><li>getAttribute（从域当中获取数据）</li><li>removeAttribute（删除域当中的数据）</li></ul></li></ul></li><li><strong>使用原则：尽量使用小的域。</strong></li></ul></div><h3 id="cookie"><a href="#cookie" class="headerlink" title="cookie"></a>cookie</h3><ul><li>session的实现原理中，<strong>每一个session对象都会关联一个sessionid</strong>，例如：<ul><li>JSESSIONID=41C481F0224664BDB28E95081D23D5B8</li><li>以上的这个键值对数据其实就是cookie对象。</li><li>对于session关联的cookie来说，这个cookie是被保存在浏览器的“运行内存”当中。</li><li>只要浏览器不关闭，用户再次发送请求的时候，会自动将运行内存中的cookie发送给服务器。</li><li>例如，这个Cookie: JSESSIONID=41C481F0224664BDB28E95081D23D5B8就会再次发送给服务器。</li><li>服务器就是根据41C481F0224664BDB28E95081D23D5B8这个值来找到对应的session对象的。</li></ul></li><li><strong>cookie怎么生成？cookie保存在什么地方？cookie有啥用？浏览器什么时候会发送cookie，发送哪些cookie给服务器？？？？？？？</strong><ul><li>cookie最终是保存在浏览器客户端上的。</li><li>可以保存在运行内存中。（浏览器只要关闭cookie就消失了。）</li><li>也可以保存在硬盘文件中。（永久保存。）</li></ul></li><li><strong>cookie有啥用呢？</strong><ul><li>cookie和session机制其实都是为了<code>保存会话</code>的状态。</li><li>cookie是将会话的状态保存在浏览器客户端上。（cookie数据存储在浏览器客户端上的。）</li><li>session是将会话的状态保存在服务器端上。（session对象是存储在服务器上。）</li><li>为什么要有cookie和session机制呢？因为<strong>HTTP协议是无状态,无连接协议。</strong></li></ul></li></ul><details class="folding-tag" blue><summary> cookie的经典案例 </summary>              <div class='content'>              <ul><li>京东商城，在未登录的情况下，向购物车中放几件商品。然后关闭商城，再次打开浏览器，访问京东商城的时候，购物车中的商品还在，这是怎么做的？<strong>没有登录，为什么购物车中还有商品呢？</strong><ul><li>将购物车中的商品编号放到cookie当中，cookie保存在硬盘文件当中。这样即使关闭浏览器。硬盘上的cookie还在。下一次再打开京东商城的时候，查看购物车的时候，会自动读取本地硬盘中存储的cookie，拿到商品编号，动态展示购物车中的商品。<ul><li>京东存储购物车中商品的cookie可能是这样的：productIds=xxxxx,yyyy,zzz,kkkk</li><li>注意：cookie如果清除掉，购物车中的商品就消失了。</li></ul></li></ul></li><li><strong>126邮箱中有一个功能：十天内免登录</strong><ul><li>这个功能也是需要cookie来实现的。</li><li>怎么实现的呢？<ul><li>用户输入正确的用户名和密码，并且同时选择十天内免登录。登录成功后。浏览器客户端会保存一个cookie，这个cookie中保存了用户名和密码等信息，这个cookie是保存在硬盘文件当中的，十天有效。在十天内用户再次访问126的时候，浏览器自动提交126的关联的cookie给服务器，服务器接收到cookie之后，获取用户名和密码，验证，通过之后，自动登录成功。</li></ul></li><li>怎么让cookie失效？<ul><li>十天过后自动失效。</li><li>或者改密码。</li><li>或者在客户端浏览器上清除cookie。</li></ul></li></ul></li></ul>              </div>            </details><ul><li>cookie机制和session机制其实都不属于java中的机制，实际上<strong>cookie机制和session机制都是HTTP协议的一部分</strong>。php开发中也有cookie和session机制，只要是你是做web开发，不管是什么编程语言，cookie和session机制都是需要的。</li><li>HTTP协议中规定：任何一个cookie都是由<code>name</code>和<code>value</code>组成的。<strong>name和value都是字符串类型的。</strong></li><li>在java的servlet中，对cookie提供了哪些支持呢？<ul><li>提供了一个Cookie类来专门表示cookie数据。jakarta.servlet.http.Cookie;</li><li>java程序怎么把cookie数据发送给浏览器呢？response.addCookie(cookie);</li></ul></li><li>在HTTP协议中是这样规定的：当浏览器发送请求的时候，会自动携带该path下的cookie数据给服务器。（URL）</li><li><strong>关于cookie的有效时间</strong><ul><li>怎么用java设置cookie的有效时间<ul><li>cookie.setMaxAge(60 * 60); 设置cookie在一小时之后失效。</li></ul></li><li>没有设置有效时间：默认保存在浏览器的运行内存中，浏览器关闭则cookie消失。</li><li>只要设置cookie的有效时间 &gt; 0，这个cookie一定会存储到硬盘文件当中。</li><li>设置cookie的有效时间 = 0 呢？<ul><li>cookie被删除，同名cookie被删除。</li></ul></li><li>设置cookie的有效时间 &lt; 0 呢？<ul><li>保存在运行内存中。和不设置一样。</li></ul></li></ul></li><li><strong>关于cookie的path，cookie关联的路径：</strong><ul><li>假设现在发送的请求路径是“<a href="http://localhost:8080/servlet13/cookie/generate”生成的cookie，如果cookie没有设置path，默认的path是什么？">http://localhost:8080/servlet13/cookie/generate”生成的cookie，如果cookie没有设置path，默认的path是什么？</a></li><li>默认的path是：<a href="http://localhost:8080/servlet13/cookie">http://localhost:8080/servlet13/cookie</a> 以及它的子路径。</li><li>也就是说，以后只要浏览器的请求路径是<a href="http://localhost:8080/servlet13/cookie">http://localhost:8080/servlet13/cookie</a> 这个路径以及这个路径下的子路径，cookie都会被发送到服务器。</li></ul></li><li><strong>手动设置cookie的path</strong>    <ul><li>cookie.setPath(“/servlet13”); 表示只要是这个servlet13项目的请求路径，都会提交这个cookie给服务器。</li></ul></li></ul><p>浏览器发送cookie给服务器了，服务器中的java程序怎么接收？<br><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><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">Cookie[] cookies = request.getCookies(); <span class="comment">// 这个方法可能返回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="comment">// 获取cookie的name</span></span><br><span class="line">        <span class="type">String</span> <span class="variable">name</span> <span class="operator">=</span> cookie.getName();</span><br><span class="line">        <span class="comment">// 获取cookie的value</span></span><br><span class="line">        <span class="type">String</span> <span class="variable">value</span> <span class="operator">=</span> cookie.getValue();</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></p><p>使用cookie实现一下十天内免登录功能。</p><h2 id="过滤器和监听器"><a href="#过滤器和监听器" class="headerlink" title="过滤器和监听器"></a>过滤器和监听器</h2><h3 id="过滤器"><a href="#过滤器" class="headerlink" title="过滤器"></a>过滤器</h3><ul><li><p>当前的OA项目存在什么缺陷？ </p><ul><li>DeptServlet、EmpServlet、OrderServlet。每一个Servlet都是处理自己相关的业务。在这些Servlet执行之前都是需要判断用户是否登录了。如果用户登录了，可以继续操作，如果没有登录，需要用户登录。这段判断用户是否登录的代码是固定的，并且在每一个Servlet类当中都需要编写，显然代码没有得到重复利用。包括每一个Servlet都要解决中文乱码问题，也有公共的代码。这些代码目前都是重复编写，并没有达到复用。怎么解决这个问题? </li></ul></li><li><p><strong>可以使用Servlet规范中的Filter过滤器来解决用户登录问题。</strong></p></li><li><p><strong>Filter是什么，有什么用，执行原理是什么？</strong></p><ul><li>Filter是过滤器。</li><li>Filter可以在Servlet这个目标程序执行之前添加代码。也可以在目标Servlet执行之后添加代码。之前之后都可以添加过滤规则。</li><li>一般情况下，都是在过滤器当中编写公共代码。</li></ul></li><li><p>一个过滤器怎么写呢？ </p><ul><li>第一步：编写一个Java类实现一个接口：jarkata.servlet.Filter。并且实现这个接口当中所有的方法。 <ul><li><code>init方法</code>：在Filter对象第一次被创建之后调用，并且只调用一次。</li><li><code>doFilter方法</code>：只要用户发送一次请求，则执行一次。发送N次请求，则执行N次。在这个方法中编写过滤规则。</li><li><code>destroy方法</code>：在Filter对象被释放/销毁之前调用，并且只调用一次。</li></ul></li><li>第二步：在web.xml文件中对Filter进行配置。这个配置和Servlet很像。 </li></ul></li></ul><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></pre></td><td class="code"><pre><span class="line"><span class="tag">&lt;<span class="name">filter</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">filter-name</span>&gt;</span>filter2<span class="tag">&lt;/<span class="name">filter-name</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">filter-class</span>&gt;</span>com.bjpowernode.javaweb.servlet.Filter2<span class="tag">&lt;/<span class="name">filter-class</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;/<span class="name">filter</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;<span class="name">filter-mapping</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">filter-name</span>&gt;</span>filter2<span class="tag">&lt;/<span class="name">filter-name</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">url-pattern</span>&gt;</span>*.do<span class="tag">&lt;/<span class="name">url-pattern</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;/<span class="name">filter-mapping</span>&gt;</span></span><br></pre></td></tr></table></figure><ul><li>或者使用注解：@WebFilter({“*.do”})</li></ul><div class="note warning no-icon flat"><ul><li>注意： <ul><li>Servlet对象默认情况下，在服务器启动的时候是不会新建对象的。</li><li>Filter对象默认情况下，在服务器启动的时候会新建对象。</li><li>Servlet是单例的。Filter也是单例的。（单实例。</li></ul></li></ul></div><ul><li></li><li><p>目标Servlet是否执行，取决于两个条件： </p><ul><li>条件一：在过滤器当中是否编写了：<code>chain.doFilter(request, response)</code></li><li>条件二：用户发送的请求路径是否和Servlet的请求路径一致。</li></ul></li><li><p>chain.doFilter(request, response); 这行代码的作用： </p><ul><li>执行下一个过滤器，如果下面没有过滤器了，执行最终的Servlet。</li></ul></li></ul><div class="note warning no-icon flat"><p><strong>注意：Filter的优先级，天生的就比Servlet优先级高。</strong>若/a.do 对应一个Filter，也对应一个Servlet。那么一定是先执行Filter，然后再执行Servlet。</p></div><ul><li><p>关于Filter的配置路径： </p><ul><li>/a.do、/b.do、/dept/save。这些配置方式都是精确匹配。</li><li>/* 匹配所有路径。</li><li>*.do 后缀匹配。不要以 / 开始</li><li>/dept/*  前缀匹配。</li></ul></li><li><p>在web.xml文件中进行配置的时候，Filter的执行顺序是什么？ </p><ul><li>依靠filter-mapping标签的配置位置，越靠上优先级越高。</li></ul></li><li><p><strong>过滤器的调用顺序，遵循栈数据结构。</strong></p></li><li><p>使用@WebFilter的时候，Filter的执行顺序是怎样的呢？ </p><ul><li>执行顺序是：比较Filter这个类名。</li><li>比如：FilterA和FilterB，则先执行FilterA。</li><li>比如：Filter1和Filter2，则先执行Filter1.</li></ul></li><li><p><strong>Filter的生命周期？</strong></p><ul><li>和Servlet对象生命周期一致。</li><li>唯一的区别：Filter默认情况下，在服务器启动阶段就实例化。Servlet不会。</li></ul></li><li><p>Filter过滤器这里有一个设计模式：   </p><ul><li>责任链设计模式。</li><li>过滤器最大的优点： <ul><li>在程序编译阶段不会确定调用顺序。因为Filter的调用顺序是配置到web.xml文件中的，只要修改web.xml配置文件中filter-mapping的顺序就可以调整Filter的执行顺序。显然Filter的执行顺序是在程序运行阶段动态组合的。那么这种设计模式被称为责任链设计模式。</li></ul></li><li>责任链设计模式最大的核心思想： <ul><li>在程序运行阶段，动态的组合程序的调用顺序。</li></ul></li></ul></li></ul><ul><li><strong>过滤器的实现原理</strong></li></ul><div class="img-wrap"><div class="img-bg"><img class="img" src="https://raw.githubusercontent.com/silvan2077/markdown_pic/main/Picgo/20250926182404.png"/></div></div><h3 id="Listener监听器"><a href="#Listener监听器" class="headerlink" title="Listener监听器"></a>Listener监听器</h3><ul><li><p>什么是监听器？</p><ul><li>监听器是Servlet规范中的一员。就像Filter一样。Filter也是Servlet规范中的一员。</li><li>在Servlet中，所有的监听器接口都是以“Listener”结尾。</li></ul></li><li><p>监听器有什么用？</p><ul><li>监听器实际上是Servlet规范留给我们javaweb程序员的特殊时机。</li><li>特殊的时刻如果想执行这段代码，你需要想到使用对应的监听器。</li></ul></li><li><p><strong>Servlet规范中提供了哪些监听器？</strong></p><ul><li>jakarta.servlet包下：<ul><li>ServletContextListener</li><li>ServletContextAttributeListener</li><li>ServletRequestListener</li><li>ServletRequestAttributeListener</li></ul></li><li>jakarta.servlet.http包下：<ul><li>HttpSessionListener</li><li>HttpSessionAttributeListener<ul><li>该监听器需要使用@WebListener注解进行标注。</li><li>该监听器监听的是什么？是session域中数据的变化。只要数据变化，则执行相应的方法。主要监测点在session域对象上。</li></ul></li><li>HttpSessionBindingListener<ul><li>该监听器不需要使用<code>@WebListener</code>进行标注。</li><li>假设User类实现了该监听器，那么User对象在被放入session的时候触发bind事件，User对象从session中删除的时候，触发unbind事件。</li><li>假设Customer类没有实现该监听器，那么Customer对象放入session或者从session删除的时候，不会触发bind和unbind事件。</li></ul></li><li>HttpSessionIdListener<ul><li>session的id发生改变的时候，监听器中的唯一一个方法就会被调用。</li></ul></li></ul></li></ul></li><li><p>HttpSessionActivationListener</p><ul><li><strong>监听session对象的<code>钝化</code>和<code>活化</code>的。</strong></li><li>钝化：session对象从内存存储到硬盘文件。</li><li>活化：从硬盘文件把session恢复到内存。</li></ul></li><li><p>实现一个监听器的步骤：以ServletContextListener为例。</p><ul><li>第一步：编写一个类实现ServletContextListener接口。并且实现里面的方法。<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="keyword">void</span> <span class="title function_">contextInitialized</span><span class="params">(ServletContextEvent event)</span></span><br><span class="line"><span class="keyword">void</span> <span class="title function_">contextDestroyed</span><span class="params">(ServletContextEvent event)</span></span><br></pre></td></tr></table></figure></li><li>第二步：在web.xml文件中对ServletContextListener进行配置，如下：<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="tag">&lt;<span class="name">listener</span>&gt;</span></span><br><span class="line">  <span class="tag">&lt;<span class="name">listener-class</span>&gt;</span>com.bjpowernode.javaweb.listener.MyServletContextListener<span class="tag">&lt;/<span class="name">listener-class</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;/<span class="name">listener</span>&gt;</span></span><br></pre></td></tr></table></figure></li></ul></li><li><p>当然，第二步也可以不使用配置文件，也可以用注解，例如：@WebListener</p><div class="note warning no-icon flat"><ul><li>注意：所有监听器中的方法都是不需要javaweb程序员调用的，由服务器来负责调用？什么时候被调用呢？<ul><li>当某个特殊的事件发生（特殊的事件发生其实就是某个时机到了。）之后，被web服务器自动调用。</li></ul></li></ul></div></li></ul><p>思考一个业务场景：</p><ul><li><p>请编写一个功能，<strong>记录该网站实时的在线用户的个数</strong></p><ul><li>我们可以通过服务器端有没有分配session对象来判断在线人数，因为一个session代表了一个用户。有一个session就代表有一个用户。如果你采用这种逻辑去实现的话，session有多少个，在线用户就有多少个。这种方式的话：HttpSessionListener够用了。session对象只要新建，则count++，然后将count存储到ServletContext域当中，在页面展示在线人数即可。</li><li>业务发生改变了，只统计登录的用户的在线数量，这个该怎么办？<ul><li>session.setAttribute(“user”, userObj); </li><li>用户登录的标志是什么？session中曾经存储过User类型的对象。那么这个时候可以让User类型的对象实现HttpSessionBindingListener监听器，只要User类型对象存储到session域中，则count++，然后将count++存储到ServletContext对象中。页面展示在线人数即可。</li></ul></li></ul></li><li><p><strong>实现oa项目中当前登录在线的人数</strong></p><ul><li>什么代表着用户登录了？<ul><li>session.setAttribute(“user”, userObj); User类型的对象只要往session中存储过，表示有新用户登录。</li></ul></li><li>什么代表着用户退出了？<ul><li>session.removeAttribute(“user”); User类型的对象从session域中移除了。</li><li>或者有可能是session销毁了。（session超时）</li></ul></li></ul></li></ul>]]></content>
    
    
      
      
    <summary type="html">&lt;h2 id=&quot;Servlet生命周期&quot;&gt;&lt;a href=&quot;#Servlet生命周期&quot; class=&quot;headerlink&quot; title=&quot;Servlet生命周期&quot;&gt;&lt;/a&gt;Servlet生命周期&lt;/h2&gt;&lt;ul&gt;
&lt;li&gt;网站中所有的Servlet接口实现类的实例对象，只能&lt;s</summary>
      
    
    
    
    <category term="JaveWeb" scheme="https://silvan.chat/categories/JaveWeb/"/>
    
    
    <category term="java" scheme="https://silvan.chat/tags/java/"/>
    
    <category term="javaweb" scheme="https://silvan.chat/tags/javaweb/"/>
    
  </entry>
  
</feed>
