<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0"
     xmlns:atom="http://www.w3.org/2005/Atom"
     xmlns:content="http://purl.org/rss/1.0/modules/content/">
    <channel>
        <title>yw</title>
        <link>https://yww52.com/</link>
        
        <image>
            <url>https://yww52.com/favicon.jpg</url>
            <title>yw</title>
            <link>https://yww52.com/</link>
        </image>
        
        <atom:link href="https://yww52.com/rss2.xml" rel="self" type="application/rss+xml"/>
        
        <description>debug myself</description>
        <pubDate>Mon, 26 Jan 2026 08:15:14 GMT</pubDate>
        <generator>https://hexo.io/</generator>
        
        <item>
            <title>自建busuanzi网站计数服务</title>
            <link>https://yww52.com/posts/5315dfbb/</link>
            <guid>https://yww52.com/posts/5315dfbb/</guid>
            <pubDate>Sat, 09 Aug 2025 16:00:00 GMT</pubDate>
            
            
            
            
            
            <description></description>
            
            
            
            
            
            <content:encoded><![CDATA[<img src="https://img.yww52.com/2025/8/2025-8-10top_img.jpg" alt="自建busuanzi网站计数服务" style="max-width:100%;height:auto;margin-bottom:15px;"
  /><h1 id="序言"><a href="#序言" class="headerlink" title="序言"></a>序言</h1><p> <a href="https://busuanzi.ibruce.info/">不蒜子(busuanzi)</a> 是一个广受欢迎的免费网页计数服务，能够轻松实现网站的页面浏览量(PV)和独立访客数(UV)统计。许多基于Hexo的网站都在使用busuanzi来实现访问统计功能，我的博客当前使用的主题是<code>Butterfly</code>，这个主题也是通过集成busuanzi计数器来实现网页计数。</p><p>然而，随着使用人数的不断增多，这个由作者为爱发电的免费服务开始出现稳定性问题，访问速度也变得起伏不定。</p><p>因此，我决定自己开发一个服务来代替busuanzi，为我的网站提供稳定的计数服务。</p><h1 id="技术栈选择"><a href="#技术栈选择" class="headerlink" title="技术栈选择"></a>技术栈选择</h1><p>我简单分析了一下，发现busuanzi这个网页计数服务实现起来并不复杂，主要是通过获取访问请求的来源地址来实现PV和UV的计数增长，因此服务的核心就是一个统计接口。</p><p>基于这个分析，技术栈的选择也相对明确：</p><ol><li><p><strong>接口开发</strong>：由于我最熟悉Java，所以选择使用SpringBoot3和Java17来实现这个接口。（只实现一个接口的话，用SpringBoot感觉有些重，后续有时间可以考虑用Go重写一遍）</p></li><li><p><strong>数据存储</strong>：数据库选择很多，但考虑到服务需要记录的数据主要是网站的PV和UV，对于请求IP等信息并非必须存储，所以我选择使用Redis。Redis对于这类频繁操作的数据处理速度非常快，而且配置好Redis的持久化后，也不怎么需要担心数据丢失问题。如果还是不太放心，可以在增加一个定时任务，定时从Redis中读取数据，存储到关系型数据库当中去，但是我感觉没有必要。 </p></li></ol><h1 id="服务分析"><a href="#服务分析" class="headerlink" title="服务分析"></a>服务分析</h1><h2 id="前端脚本"><a href="#前端脚本" class="headerlink" title="前端脚本"></a>前端脚本</h2><p>为了避免修改<code>Butterfly</code>主题中集成busuanzi的代码（以免在后续主题升级时进行频繁修改），需要让自建服务的接口访问方式和数据显示方式与原版busuanzi完全一致。因此，首先需要分析当前busuanzi的具体工作流程。</p><p>访问官网，可以看到官网提示的信息，只需要两行代码即可搞定计数。</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">&lt;script <span class="keyword">async</span> src=<span class="string">&quot;//busuanzi.ibruce.info/busuanzi/2.3/busuanzi.pure.mini.js&quot;</span>&gt;&lt;/script&gt;</span><br><span class="line"><span class="language-xml"><span class="tag">&lt;<span class="name">span</span> <span class="attr">id</span>=<span class="string">&quot;busuanzi_container_site_pv&quot;</span>&gt;</span>本站总访问量<span class="tag">&lt;<span class="name">span</span> <span class="attr">id</span>=<span class="string">&quot;busuanzi_value_site_pv&quot;</span>&gt;</span><span class="tag">&lt;/<span class="name">span</span>&gt;</span>次<span class="tag">&lt;/<span class="name">span</span>&gt;</span></span></span><br></pre></td></tr></table></figure><p>从这两行代码可以看出，busuanzi通过引入<code>busuanzi.pure.mini.js</code>脚本来修改<code>busuanzi_container_site_pv</code>标签的内容，从而实现计数显示。数据获取流程的核心就在这个脚本中。由于该脚本经过了代码压缩，我使用<code>claude 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><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><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></pre></td><td class="code"><pre><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * Busuanzi 不蒜子统计脚本</span></span><br><span class="line"><span class="comment"> * 用于网站PV/UV统计显示</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">var</span> bszCaller, bszTag;</span><br><span class="line"><span class="comment">// DOM Ready 功能实现</span></span><br><span class="line">(<span class="keyword">function</span>(<span class="params"></span>) &#123;</span><br><span class="line">    <span class="keyword">var</span> interval, </span><br><span class="line">        domReadyCallback, </span><br><span class="line">        executeCallbacks,</span><br><span class="line">        isReady = <span class="literal">false</span>, </span><br><span class="line">        readyCallbacks = [];</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 添加ready回调函数</span></span><br><span class="line">    ready = <span class="keyword">function</span>(<span class="params">callback</span>) &#123;</span><br><span class="line">        <span class="keyword">if</span> (isReady || <span class="variable language_">document</span>.<span class="property">readyState</span> === <span class="string">&quot;interactive&quot;</span> || <span class="variable language_">document</span>.<span class="property">readyState</span> === <span class="string">&quot;complete&quot;</span>) &#123;</span><br><span class="line">            <span class="keyword">return</span> callback.<span class="title function_">call</span>(<span class="variable language_">document</span>);</span><br><span class="line">        &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">            readyCallbacks.<span class="title function_">push</span>(<span class="keyword">function</span>(<span class="params"></span>) &#123;</span><br><span class="line">                <span class="keyword">return</span> callback.<span class="title function_">call</span>(<span class="variable language_">this</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="variable language_">this</span>;</span><br><span class="line">    &#125;;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 执行所有等待的回调函数</span></span><br><span class="line">    executeCallbacks = <span class="keyword">function</span>(<span class="params"></span>) &#123;</span><br><span class="line">        <span class="keyword">for</span> (<span class="keyword">var</span> i = <span class="number">0</span>, length = readyCallbacks.<span class="property">length</span>; i &lt; length; i++) &#123;</span><br><span class="line">            readyCallbacks[i].<span class="title function_">apply</span>(<span class="variable language_">document</span>);</span><br><span class="line">        &#125;</span><br><span class="line">        readyCallbacks = [];</span><br><span class="line">    &#125;;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// DOM准备完成后的处理</span></span><br><span class="line">    domReadyCallback = <span class="keyword">function</span>(<span class="params"></span>) &#123;</span><br><span class="line">        <span class="keyword">if</span> (!isReady) &#123;</span><br><span class="line">            isReady = <span class="literal">true</span>;</span><br><span class="line">            executeCallbacks.<span class="title function_">call</span>(<span class="variable language_">window</span>);</span><br><span class="line">            </span><br><span class="line">            <span class="comment">// 移除事件监听器</span></span><br><span class="line">            <span class="keyword">if</span> (<span class="variable language_">document</span>.<span class="property">removeEventListener</span>) &#123;</span><br><span class="line">                <span class="variable language_">document</span>.<span class="title function_">removeEventListener</span>(<span class="string">&quot;DOMContentLoaded&quot;</span>, domReadyCallback, <span class="literal">false</span>);</span><br><span class="line">            &#125; <span class="keyword">else</span> <span class="keyword">if</span> (<span class="variable language_">document</span>.<span class="property">attachEvent</span>) &#123;</span><br><span class="line">                <span class="variable language_">document</span>.<span class="title function_">detachEvent</span>(<span class="string">&quot;onreadystatechange&quot;</span>, domReadyCallback);</span><br><span class="line">                <span class="keyword">if</span> (<span class="variable language_">window</span> == <span class="variable language_">window</span>.<span class="property">top</span>) &#123;</span><br><span class="line">                    <span class="built_in">clearInterval</span>(interval);</span><br><span class="line">                    interval = <span class="literal">null</span>;</span><br><span class="line">                &#125;</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 绑定DOM Ready事件</span></span><br><span class="line">    <span class="keyword">if</span> (<span class="variable language_">document</span>.<span class="property">addEventListener</span>) &#123;</span><br><span class="line">        <span class="comment">// 现代浏览器</span></span><br><span class="line">        <span class="variable language_">document</span>.<span class="title function_">addEventListener</span>(<span class="string">&quot;DOMContentLoaded&quot;</span>, domReadyCallback, <span class="literal">false</span>);</span><br><span class="line">    &#125; <span class="keyword">else</span> <span class="keyword">if</span> (<span class="variable language_">document</span>.<span class="property">attachEvent</span>) &#123;</span><br><span class="line">        <span class="comment">// IE浏览器兼容</span></span><br><span class="line">        <span class="variable language_">document</span>.<span class="title function_">attachEvent</span>(<span class="string">&quot;onreadystatechange&quot;</span>, <span class="keyword">function</span>(<span class="params"></span>) &#123;</span><br><span class="line">            <span class="keyword">if</span> (<span class="regexp">/loaded|complete/</span>.<span class="title function_">test</span>(<span class="variable language_">document</span>.<span class="property">readyState</span>)) &#123;</span><br><span class="line">                <span class="title function_">domReadyCallback</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">// IE浏览器特殊处理</span></span><br><span class="line">        <span class="keyword">if</span> (<span class="variable language_">window</span> == <span class="variable language_">window</span>.<span class="property">top</span>) &#123;</span><br><span class="line">            interval = <span class="built_in">setInterval</span>(<span class="keyword">function</span>(<span class="params"></span>) &#123;</span><br><span class="line">                <span class="keyword">try</span> &#123;</span><br><span class="line">                    <span class="keyword">if</span> (!isReady) &#123;</span><br><span class="line">                        <span class="variable language_">document</span>.<span class="property">documentElement</span>.<span class="title function_">doScroll</span>(<span class="string">&quot;left&quot;</span>);</span><br><span class="line">                    &#125;</span><br><span class="line">                &#125; <span class="keyword">catch</span> (error) &#123;</span><br><span class="line">                    <span class="keyword">return</span>;</span><br><span class="line">                &#125;</span><br><span class="line">                <span class="title function_">domReadyCallback</span>();</span><br><span class="line">            &#125;, <span class="number">5</span>);</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;)();</span><br><span class="line"></span><br><span class="line"><span class="comment">// JSONP 调用器对象</span></span><br><span class="line">bszCaller = &#123;</span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     * 发起JSONP请求获取统计数据</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@param</span> &#123;<span class="type">string</span>&#125; <span class="variable">url</span> - 请求URL</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@param</span> &#123;<span class="type">function</span>&#125; <span class="variable">callback</span> - 回调函数</span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    <span class="attr">fetch</span>: <span class="keyword">function</span>(<span class="params">url, callback</span>) &#123;</span><br><span class="line">        <span class="comment">// 生成随机回调函数名</span></span><br><span class="line">        <span class="keyword">var</span> callbackName = <span class="string">&quot;BusuanziCallback_&quot;</span> + <span class="title class_">Math</span>.<span class="title function_">floor</span>(<span class="title class_">Math</span>.<span class="title function_">random</span>() * <span class="number">1099511627776</span>);</span><br><span class="line">        </span><br><span class="line">        <span class="comment">// 在全局作用域中注册回调函数</span></span><br><span class="line">        <span class="variable language_">window</span>[callbackName] = <span class="variable language_">this</span>.<span class="title function_">evalCall</span>(callback);</span><br><span class="line">        </span><br><span class="line">        <span class="comment">// 替换URL中的回调函数名</span></span><br><span class="line">        url = url.<span class="title function_">replace</span>(<span class="string">&quot;=BusuanziCallback&quot;</span>, <span class="string">&quot;=&quot;</span> + callbackName);</span><br><span class="line">        </span><br><span class="line">        <span class="comment">// 创建script标签进行JSONP请求</span></span><br><span class="line">        <span class="keyword">var</span> scriptTag = <span class="variable language_">document</span>.<span class="title function_">createElement</span>(<span class="string">&quot;SCRIPT&quot;</span>);</span><br><span class="line">        scriptTag.<span class="property">type</span> = <span class="string">&quot;text/javascript&quot;</span>;</span><br><span class="line">        scriptTag.<span class="property">defer</span> = <span class="literal">true</span>;</span><br><span class="line">        scriptTag.<span class="property">src</span> = url;</span><br><span class="line">        scriptTag.<span class="property">referrerPolicy</span> = <span class="string">&quot;no-referrer-when-downgrade&quot;</span>;</span><br><span class="line">        </span><br><span class="line">        <span class="comment">// 将script标签添加到head中</span></span><br><span class="line">        <span class="variable language_">document</span>.<span class="title function_">getElementsByTagName</span>(<span class="string">&quot;HEAD&quot;</span>)[<span class="number">0</span>].<span class="title function_">appendChild</span>(scriptTag);</span><br><span 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> &#123;<span class="type">function</span>&#125; <span class="variable">callback</span> - 原始回调函数</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@returns</span> &#123;<span class="type">function</span>&#125; 包装后的回调函数</span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    <span class="attr">evalCall</span>: <span class="keyword">function</span>(<span class="params">callback</span>) &#123;</span><br><span class="line">        <span class="keyword">return</span> <span class="keyword">function</span>(<span class="params">data</span>) &#123;</span><br><span class="line">            <span class="title function_">ready</span>(<span class="keyword">function</span>(<span class="params"></span>) &#123;</span><br><span class="line">                <span class="keyword">try</span> &#123;</span><br><span class="line">                    <span class="comment">// 执行回调并清理script标签</span></span><br><span class="line">                    <span class="title function_">callback</span>(data);</span><br><span class="line">                    <span class="keyword">if</span> (scriptTag &amp;&amp; scriptTag.<span class="property">parentElement</span>) &#123;</span><br><span class="line">                        scriptTag.<span class="property">parentElement</span>.<span class="title function_">removeChild</span>(scriptTag);</span><br><span class="line">                    &#125;</span><br><span class="line">                &#125; <span class="keyword">catch</span> (error) &#123;</span><br><span class="line">                    <span class="comment">// 出错时隐藏统计显示</span></span><br><span class="line">                    bszTag.<span class="title function_">hides</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><span class="line"></span><br><span class="line"><span class="comment">// 发起统计数据请求</span></span><br><span class="line">bszCaller.<span class="title function_">fetch</span>(<span class="string">&quot;//busuanzi.ibruce.info/busuanzi?jsonpCallback=BusuanziCallback&quot;</span>, <span class="keyword">function</span>(<span class="params">data</span>) &#123;</span><br><span class="line">    <span class="comment">// 更新页面统计数据并显示</span></span><br><span class="line">    bszTag.<span class="title function_">texts</span>(data);</span><br><span class="line">    bszTag.<span class="title function_">shows</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">bszTag = &#123;</span><br><span class="line">    <span class="comment">// 支持的统计类型</span></span><br><span class="line">    <span class="attr">bszs</span>: [<span class="string">&quot;site_pv&quot;</span>, <span class="string">&quot;page_pv&quot;</span>, <span class="string">&quot;site_uv&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 class="doctag">@param</span> &#123;<span class="type">object</span>&#125; <span class="variable">data</span> - 包含统计数据的对象</span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    <span class="attr">texts</span>: <span class="keyword">function</span>(<span class="params">data</span>) &#123;</span><br><span class="line">        <span class="variable language_">this</span>.<span class="property">bszs</span>.<span class="title function_">map</span>(<span class="keyword">function</span>(<span class="params">type</span>) &#123;</span><br><span class="line">            <span class="keyword">var</span> element = <span class="variable language_">document</span>.<span class="title function_">getElementById</span>(<span class="string">&quot;busuanzi_value_&quot;</span> + type);</span><br><span class="line">            <span class="keyword">if</span> (element) &#123;</span><br><span class="line">                element.<span class="property">innerHTML</span> = data[type];</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;);</span><br><span class="line">    &#125;,</span><br><span class="line"></span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     * 隐藏所有统计容器</span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    <span class="attr">hides</span>: <span class="keyword">function</span>(<span class="params"></span>) &#123;</span><br><span class="line">        <span class="variable language_">this</span>.<span class="property">bszs</span>.<span class="title function_">map</span>(<span class="keyword">function</span>(<span class="params">type</span>) &#123;</span><br><span class="line">            <span class="keyword">var</span> container = <span class="variable language_">document</span>.<span class="title function_">getElementById</span>(<span class="string">&quot;busuanzi_container_&quot;</span> + type);</span><br><span class="line">            <span class="keyword">if</span> (container) &#123;</span><br><span class="line">                container.<span class="property">style</span>.<span class="property">display</span> = <span class="string">&quot;none&quot;</span>;</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;);</span><br><span class="line">    &#125;,</span><br><span class="line"></span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     * 显示所有统计容器</span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    <span class="attr">shows</span>: <span class="keyword">function</span>(<span class="params"></span>) &#123;</span><br><span class="line">        <span class="variable language_">this</span>.<span class="property">bszs</span>.<span class="title function_">map</span>(<span class="keyword">function</span>(<span class="params">type</span>) &#123;</span><br><span class="line">            <span class="keyword">var</span> container = <span class="variable language_">document</span>.<span class="title function_">getElementById</span>(<span class="string">&quot;busuanzi_container_&quot;</span> + type);</span><br><span class="line">            <span class="keyword">if</span> (container) &#123;</span><br><span class="line">                container.<span class="property">style</span>.<span class="property">display</span> = <span class="string">&quot;inline&quot;</span>;</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;;</span><br></pre></td></tr></table></figure><p>虽然对JavaScript不是很熟悉，但这个脚本逻辑相对清晰，不难读懂。以下是该脚本的核心工作流程：</p><ol><li>等待浏览器DOM准备</li><li>DOM准备完成后，发起JSONP请求</li><li>解析请求返回的标签数据，如果请求错误，数据异常就隐藏所有统计容器</li><li>获取到计数的数据后，更新携带有ID为<code>busuanzi_value_</code>或<code>busuanzi_container_</code>统计标签的数据，然后修改容器的<code>display</code>属性，从而实现容器和内容的显示</li></ol><blockquote><p>JSONP其实就是GET请求，主要是利用<code>&lt;script&gt;</code>标签可以跨域加载资源的特性，将数据请求伪装成脚本加载，服务器返回的也不是纯数据，而是一个函数调用，从而绕过浏览器的同源策略限制。</p></blockquote><p>前端操作通过这个简单的脚本即可实现，接下来分析后端接口的实现方式。</p><h2 id="后端接口"><a href="#后端接口" class="headerlink" title="后端接口"></a>后端接口</h2><p>网页计数服务的核心功能是记录URL的UV和PV数据。这些指标又分为站点级别和页面级别，理论上应该有四个数据（但busuanzi不计算页面UV，因此只有三个），这里按四个指标数据计算。下面逐一分析这些数据的计算方法。</p><h3 id="PV"><a href="#PV" class="headerlink" title="PV"></a>PV</h3><p>PV，即浏览量，根据定义也不难知道计算的方法。当前端访问接口的时候，站点浏览量进行加1，页面浏览量进行加1既可以获得这两个PV数据量。</p><p>那么后端接口如何获知是哪个页面发起的请求呢？回头查看busuanzi脚本并未发现页面的相关信息，因此需要观察busuanzi脚本发送的具体HTTP请求信息。</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><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">curl ^&quot;https://busuanzi.ibruce.info/busuanzi?jsonpCallback=BusuanziCallback_12592539439^&quot; ^</span><br><span class="line">  -H ^&quot;accept: */*^&quot; ^</span><br><span class="line">  -H ^&quot;accept-language: zh-CN,zh;q=0.9^&quot; ^</span><br><span class="line">  -H ^&quot;referer: https://yww52.com/^&quot; ^</span><br><span class="line">  -H ^&quot;sec-ch-ua: ^\^&quot;Not;A=Brand^\^&quot;;v=^\^&quot;99^\^&quot;, ^\^&quot;Google Chrome^\^&quot;;v=^\^&quot;139^\^&quot;, ^\^&quot;Chromium^\^&quot;;v=^\^&quot;139^\^&quot;^&quot; ^</span><br><span class="line">  -H ^&quot;sec-ch-ua-mobile: ?0^&quot; ^</span><br><span class="line">  -H ^&quot;sec-ch-ua-platform: ^\^&quot;Windows^\^&quot;^&quot; ^</span><br><span class="line">  -H ^&quot;sec-fetch-dest: script^&quot; ^</span><br><span class="line">  -H ^&quot;sec-fetch-mode: no-cors^&quot; ^</span><br><span class="line">  -H ^&quot;sec-fetch-site: cross-site^&quot; ^</span><br><span class="line">  -H ^&quot;sec-fetch-storage-access: active^&quot; ^</span><br><span class="line">  -H ^&quot;user-agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/139.0.0.0 Safari/537.36^&quot;</span><br></pre></td></tr></table></figure><p>经过仔细观察，容易发现请求头中的<code>referer</code>字段记录了当前页面的信息。切换到不同页面时，该字段也会相应变为相应页面的URL。基于这个信息，就可以计算出所需的两个PV数据：</p><ol><li><strong>site_pv</strong>（站点浏览量）：从<code>referer</code>请求头中提取站点Host地址，在Redis中存储一个键值对，并对该值进行递增操作</li><li><strong>page_pv</strong>（页面浏览量）：直接使用<code>referer</code>请求头信息作为键，在Redis中存储键值对，并对该值进行递增操作</li></ol><h3 id="UV"><a href="#UV" class="headerlink" title="UV"></a>UV</h3><p>UV（独立访客数）的统计重点在于两个关键概念：<code>每日</code>和<code>独立访客</code>。</p><p>对于每日统计，除了存储UV值外，还需要维护一个当日访客集合。当新请求到达时，通过查询该集合可以判断当前访客今日是否已经访问过。若已访问，则UV数据保持不变；若为首次访问，则UV数据递增。</p><p>独立访客的识别相对复杂，因为Hexo博客是纯静态页面，难以直接区分不同访客。前面的请求脚本中也未发现访客标识相关数据，于是我推测访客信息应该记录在HTTP请求中。为了对标busuanzi的实现机制，需要进一步分析具体请求。最好重新打开浏览器或清理缓存，以观察UV增长时的请求响应信息。</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></pre></td><td class="code"><pre><span class="line">content-length 109</span><br><span class="line">content-type application/json</span><br><span class="line">date Sun, 10 Aug 2025 07:55:37 GMT</span><br><span class="line">server nginx/1.14.1</span><br><span class="line">set-cookie busuanziId=8CBB7C5BBCF54C86B90DAD230C7B5275; Path=/; httponly; secure; SameSite=None; Domain=busuanzi.ibruce.info; Secure</span><br></pre></td></tr></table></figure><p>从响应请求头可以看出，busuanzi采用了浏览器Cookie机制来识别独立访客。服务端在响应中设置Cookie，浏览器会在后续请求中自动携带该Cookie。通过检测请求中是否包含Cookie，服务端可以判断是否为首次访问。基于这个机制，可以得出UV数据的计算方法：</p><ol><li><strong>site_uv</strong>（站点每日独立访客数）：从<code>referer</code>请求头获取站点Host地址。若请求不包含Cookie，则生成新Cookie并添加至当日站点访客集合，同时返回Cookie并将UV数据递增。若请求已携带Cookie，则不进行任何增长操作</li><li><strong>page_uv</strong>（页面每日独立访客数）：使用<code>referer</code>请求头信息作为页面标识。若请求不包含Cookie，则生成新Cookie并添加至当日页面访客集合，同时返回Cookie并将UV数据递增。若请求已携带Cookie，则不进行任何增长操作</li></ol><h1 id="服务实现"><a href="#服务实现" class="headerlink" title="服务实现"></a>服务实现</h1><h2 id="接口实现"><a href="#接口实现" class="headerlink" title="接口实现"></a>接口实现</h2><p>这里我先展示所有使用到的Redis的键和类型，方便后续查找修改。</p><p><code>host</code>表示站点地址，<code>url</code>表示资源地址，<code>date</code>表示日期。</p><div class="table-container"><table><thead><tr><th style="text-align:center">数据名称</th><th style="text-align:center">Redis键名</th><th style="text-align:center">Redis类型</th></tr></thead><tbody><tr><td style="text-align:center">site_pv（站点浏览量）</td><td style="text-align:center">pv:{host}</td><td style="text-align:center">string</td></tr><tr><td style="text-align:center">page_pv（页面浏览量）</td><td style="text-align:center">pv:page:{url}</td><td style="text-align:center">string</td></tr><tr><td style="text-align:center">site_uv（站点每日独立访客数）</td><td style="text-align:center">uv:{host}</td><td style="text-align:center">string</td></tr><tr><td style="text-align:center">每日站点访客数（缓存7天）</td><td style="text-align:center">uvtime:{date}:{host}</td><td style="text-align:center">set</td></tr><tr><td style="text-align:center">page_uv（页面每日独立访客数）</td><td style="text-align:center">uv:page:{url}</td><td style="text-align:center">string</td></tr><tr><td style="text-align:center">每日页面访客数（缓存7天）</td><td style="text-align:center">uvtime:page:{date}:{url}</td><td style="text-align:center">set</td></tr></tbody></table></div><blockquote><p>对于Redis数据结构的选择，个人认为使用string和set这两种数据结构完全足够了，大部分博客不是什么高流量的网站，string结构的数据很好用而且也很准确，没有多大必要使用hyperloglog或者zset，特别是数据量少的情况。</p></blockquote><p>关于具体的实现，服务分析当中已经写的很明白了，这个服务其实就是一个核心的请求接口，逻辑和代码我就不一一贴出来了，我已经上传到Github`上面了，感兴趣可以自行研究，附带一下仓库地址。</p><p><a href="https://github.com/whyneh/busuanzi">https://github.com/whyneh/busuanzi</a></p><h2 id="优化部分"><a href="#优化部分" class="headerlink" title="优化部分"></a>优化部分</h2><p>服务代码除了适配原有的busuanzi的<code>JSONP</code>请求外，还自行进行了一些优化。以下是我进行优化的方向：</p><ol><li>因为后端是自己编写的服务，所以不需要前端使用JSONP的方式进行跨域处理，所以直接使用<code>fetch</code>的GET请求即可。</li><li>原版busuanzi是使用了cookie进行用户管理，我改为了使用浏览器<code>localStorage</code>进行存储管理用户信息，这样数据更加可以精准。</li></ol><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><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><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></pre></td><td class="code"><pre><span class="line">(function () &#123;</span><br><span class="line">    &#x27;use strict&#x27;;</span><br><span class="line"></span><br><span class="line">    // 配置项</span><br><span class="line">    const CONFIG = &#123;</span><br><span class="line">        // 统计项名称列表</span><br><span class="line">        STATISTICS: [&quot;site_pv&quot;, &quot;page_pv&quot;, &quot;site_uv&quot;, &quot;page_uv&quot;],</span><br><span class="line">        // API地址，请根据实际情况修改</span><br><span class="line">        API_URL: &quot;https://xxxx.com/busuanzi/api&quot;,</span><br><span class="line">        // localStorage中存储身份标识的key</span><br><span class="line">        LOCALSTORAGE_KEY: &quot;bsz-id&quot;,</span><br><span class="line">        // busuanzi的元素容器和元素值前缀</span><br><span class="line">        ITEM_VALUE_PREFIX: &quot;busuanzi_value_&quot;,</span><br><span class="line">        ITEM_CONTAINER_PREFIX: &quot;busuanzi_container_&quot;,</span><br><span class="line">        // 请求超时时间（毫秒）</span><br><span class="line">        REQUEST_TIMEOUT: 5000,</span><br><span class="line">        // 重试次数</span><br><span class="line">        RETRY_COUNT: 3</span><br><span class="line">    &#125;;</span><br><span class="line"></span><br><span class="line">    /**</span><br><span class="line">     * DOM Ready 检测函数</span><br><span class="line">     *</span><br><span class="line">     * @param &#123;Function&#125; callback - DOM准备完成后执行的回调函数</span><br><span class="line">     */</span><br><span class="line">    function domReady(callback) &#123;</span><br><span class="line">        if (document.readyState === &quot;loading&quot;) &#123;</span><br><span class="line">            document.addEventListener(&quot;DOMContentLoaded&quot;, callback, &#123; once: true &#125;);</span><br><span class="line">        &#125; else &#123;</span><br><span class="line">            // DOM已经准备完成，立即执行</span><br><span class="line">            callback();</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    /**</span><br><span class="line">     * 创建带超时的fetch请求</span><br><span class="line">     *</span><br><span class="line">     * @param &#123;string&#125; url      请求URL</span><br><span class="line">     * @param &#123;object&#125; options  fetch选项</span><br><span class="line">     * @param &#123;number&#125; timeout  超时时间</span><br><span class="line">     * @returns &#123;Promise&#125;       fetch Promise</span><br><span class="line">     */</span><br><span class="line">    function fetchWithTimeout(url, options, timeout) &#123;</span><br><span class="line">        return Promise.race([</span><br><span class="line">            fetch(url, options),</span><br><span class="line">            new Promise((_, reject) =&gt;</span><br><span class="line">                setTimeout(() =&gt; reject(new Error(&#x27;Request timeout&#x27;)), timeout)</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><br><span class="line">     * 保存用户身份标识</span><br><span class="line">     * @param &#123;string&#125; userId - 用户身份标识</span><br><span class="line">     */</span><br><span class="line">    function saveUserId(userId) &#123;</span><br><span class="line">        try &#123;</span><br><span class="line">            if (userId &amp;&amp; userId !== &quot;&quot;) &#123;</span><br><span class="line">                localStorage.setItem(CONFIG.LOCALSTORAGE_KEY, userId);</span><br><span class="line">            &#125;</span><br><span class="line">        &#125; catch (error) &#123;</span><br><span class="line">            console.warn(&#x27;无法保存到localStorage:&#x27;, error);</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    /**</span><br><span class="line">     * 获取或生成用户身份标识</span><br><span class="line">     * @returns &#123;string|null&#125; 用户身份标识</span><br><span class="line">     */</span><br><span class="line">    function getUserId() &#123;</span><br><span class="line">        try &#123;</span><br><span class="line">            return localStorage.getItem(CONFIG.LOCALSTORAGE_KEY);</span><br><span class="line">        &#125; catch (error) &#123;</span><br><span class="line">            console.warn(&#x27;无法访问localStorage:&#x27;, error);</span><br><span class="line">            return null;</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    /**</span><br><span class="line">     * 隐藏所有统计容器（出错时调用）</span><br><span class="line">     */</span><br><span class="line">    function hideStatistics() &#123;</span><br><span class="line">        CONFIG.STATISTICS.forEach(item =&gt; &#123;</span><br><span class="line">            try &#123;</span><br><span class="line">                const containerElement = document.getElementById(CONFIG.ITEM_CONTAINER_PREFIX + item);</span><br><span class="line">                if (containerElement) &#123;</span><br><span class="line">                    containerElement.style.display = &quot;none&quot;;</span><br><span class="line">                &#125;</span><br><span class="line">            &#125; catch (error) &#123;</span><br><span class="line">                console.warn(`隐藏统计项 $&#123;item&#125; 时出错:`, error);</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><br><span class="line">     * 发送统计请求</span><br><span class="line">     *</span><br><span class="line">     * @param &#123;number&#125; retryCount - 剩余重试次数</span><br><span class="line">     */</span><br><span class="line">    function fetchStatistics(retryCount = CONFIG.RETRY_COUNT) &#123;</span><br><span class="line">        // 构建请求头</span><br><span class="line">        const headers = new Headers();</span><br><span class="line">        headers.append(&quot;X-Referer&quot;, window.location.href);</span><br><span class="line"></span><br><span class="line">        const userId = getUserId();</span><br><span class="line">        if (userId) &#123;</span><br><span class="line">            headers.append(&quot;Authorization&quot;, userId);</span><br><span class="line">        &#125;</span><br><span class="line"></span><br><span class="line">        // 发送超时请求</span><br><span class="line">        fetchWithTimeout(CONFIG.API_URL, &#123;</span><br><span class="line">            method: &quot;GET&quot;,</span><br><span class="line">            headers: headers</span><br><span class="line">        &#125;, CONFIG.REQUEST_TIMEOUT)</span><br><span class="line">            // 请求成功处理</span><br><span class="line">            .then(response =&gt; &#123;</span><br><span class="line">                if (!response.ok) &#123;</span><br><span class="line">                    throw new Error(`HTTP error! status: $&#123;response.status&#125;`);</span><br><span class="line">                &#125;</span><br><span class="line"></span><br><span class="line">                // 保存响应头中的身份标识</span><br><span class="line">                const bszId = response.headers.get(&quot;Authorization&quot;);</span><br><span class="line">                if (bszId) &#123;</span><br><span class="line">                    saveUserId(bszId);</span><br><span class="line">                &#125;</span><br><span class="line"></span><br><span class="line">                return response.json();</span><br><span class="line">            &#125;)</span><br><span class="line">            // 成功数据处理</span><br><span class="line">            .then(data =&gt; &#123;</span><br><span class="line">                // 遍历每项统计数据，然后进行更改</span><br><span class="line">                CONFIG.STATISTICS.forEach(item =&gt; &#123;</span><br><span class="line">                    try &#123;</span><br><span class="line">                        // 更新统计数值</span><br><span class="line">                        const valueElement = document.getElementById(CONFIG.ITEM_VALUE_PREFIX + item);</span><br><span class="line">                        if (valueElement) &#123;</span><br><span class="line">                            valueElement.innerHTML = data[item] || 0;</span><br><span class="line">                        &#125;</span><br><span class="line"></span><br><span class="line">                        // 显示统计容器</span><br><span class="line">                        const containerElement = document.getElementById(CONFIG.ITEM_CONTAINER_PREFIX + item);</span><br><span class="line">                        if (containerElement) &#123;</span><br><span class="line">                            containerElement.style.display = &quot;inline&quot;;</span><br><span class="line">                        &#125;</span><br><span class="line">                    &#125; catch (error) &#123;</span><br><span class="line">                        console.warn(`更新统计项 $&#123;item&#125; 时出错:`, error);</span><br><span class="line">                    &#125;</span><br><span class="line">                &#125;);</span><br><span class="line">            &#125;)</span><br><span class="line">            .catch(error =&gt; &#123;</span><br><span class="line">                console.error(&#x27;busuanzi统计请求失败:&#x27;, error);</span><br><span class="line"></span><br><span class="line">                // 重试机制</span><br><span class="line">                if (retryCount &gt; 0) &#123;</span><br><span class="line">                    console.log(`正在重试... 剩余重试次数: $&#123;retryCount&#125;`);</span><br><span class="line">                    setTimeout(() =&gt; &#123;</span><br><span class="line">                        fetchStatistics(retryCount - 1);</span><br><span class="line">                    &#125;, 1000); // 1秒后重试</span><br><span class="line">                &#125; else &#123;</span><br><span class="line">                    console.error(&#x27;所有重试都失败，隐藏统计显示&#x27;);</span><br><span class="line">                    hideStatistics();</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">    // 等待DOM准备完成后初始化</span><br><span class="line">    domReady(fetchStatistics);</span><br><span class="line"></span><br><span class="line">&#125;)();</span><br></pre></td></tr></table></figure><h1 id="服务部署"><a href="#服务部署" class="headerlink" title="服务部署"></a>服务部署</h1><p>这次涉及的服务有两个，一个是<code>SpringBoot</code>的Java服务，一个是<code>Redis</code>服务，如果是要部署的话，就需要分开部署。</p><p>这里我使用了<code>docker-compose</code>来进行部署，也可以更加方便，以下是三个所需的配置文件。</p><ol><li><p>服务的Dockerfile</p><figure class="highlight dockerfile"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><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">FROM</span> eclipse-temurin:<span class="number">17</span>-jre-alpine</span><br><span class="line"><span class="keyword">LABEL</span><span class="language-bash"> maintainer=<span class="string">&quot;yww@yww52.com&quot;</span></span></span><br><span class="line"></span><br><span class="line"><span class="comment"># 设置时区</span></span><br><span class="line"><span class="keyword">ENV</span> TZ=Asia/Shanghai</span><br><span class="line"><span class="keyword">RUN</span><span class="language-bash"> apk add tzdata \</span></span><br><span class="line"><span class="language-bash">    &amp;&amp; <span class="built_in">ln</span> -snf /usr/share/zoneinfo/<span class="variable">$TZ</span> /etc/localtime \</span></span><br><span class="line"><span class="language-bash">    &amp;&amp; <span class="built_in">echo</span> <span class="variable">$TZ</span> &gt; /etc/timezone \</span></span><br><span class="line"><span class="language-bash">    &amp;&amp; apk del tzdata \</span></span><br><span class="line"><span class="language-bash">    &amp;&amp; <span class="built_in">rm</span> -rf /var/cache/apk/*</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># 定义应用相关环境变量</span></span><br><span class="line"><span class="keyword">ENV</span> JAVA_OPTS=<span class="string">&quot;-Xms512m -Xmx2048m -Xmn614m -Xss256k -XX:SurvivorRatio=6 -XX:+UseG1GC -XX:MetaspaceSize=256m -XX:MaxMetaspaceSize=512m&quot;</span></span><br><span class="line"><span class="keyword">ENV</span> APP_HOME=/app</span><br><span class="line"></span><br><span class="line"><span class="comment"># 创建非root用户并设置权限</span></span><br><span class="line"><span class="keyword">RUN</span><span class="language-bash"> adduser -D appuser &amp;&amp; \</span></span><br><span class="line"><span class="language-bash">    <span class="built_in">mkdir</span> -p <span class="variable">$APP_HOME</span> &amp;&amp; \</span></span><br><span class="line"><span class="language-bash">    <span class="built_in">chown</span> -R appuser:appuser <span class="variable">$APP_HOME</span></span></span><br><span class="line"><span class="keyword">USER</span> appuser</span><br><span class="line"></span><br><span class="line"><span class="comment"># 设置工作目录和应用目录</span></span><br><span class="line"><span class="keyword">WORKDIR</span><span class="language-bash"> <span class="variable">$APP_HOME</span></span></span><br><span class="line"></span><br><span class="line"><span class="comment"># 复制已打包的 JAR 文件</span></span><br><span class="line"><span class="keyword">COPY</span><span class="language-bash"> busuanzi-1.0.jar <span class="variable">$APP_HOME</span>/app.jar</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># 暴露应用端口</span></span><br><span class="line"><span class="keyword">EXPOSE</span> <span class="number">10010</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># 启动命令（使用环境变量配置 JVM 参数）</span></span><br><span class="line"><span class="keyword">ENTRYPOINT</span><span class="language-bash"> [<span class="string">&quot;sh&quot;</span>, <span class="string">&quot;-c&quot;</span>, <span class="string">&quot;java <span class="variable">$JAVA_OPTS</span> -jar app.jar&quot;</span>]</span></span><br></pre></td></tr></table></figure></li><li><p>redis的配置文件，这里的路径是容器内的路径，尽量不要修改</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><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><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">dir /data/redis</span><br><span class="line">bind 0.0.0.0</span><br><span class="line">port 6379</span><br><span class="line"></span><br><span class="line"># 日志文件存放</span><br><span class="line">logfile &quot;/data/log/redis.log&quot;</span><br><span class="line"></span><br><span class="line"># 启用AOF持久化</span><br><span class="line">appendonly yes</span><br><span class="line">appendfilename &quot;appendonly.aof&quot;</span><br><span class="line">appendfsync everysec</span><br><span class="line"></span><br><span class="line"># RDB持久化配置（可选）</span><br><span class="line">save 900 1</span><br><span class="line">save 300 10</span><br><span class="line">save 60 10000</span><br><span class="line"></span><br><span class="line"># 日志级别</span><br><span class="line">loglevel notice</span><br></pre></td></tr></table></figure></li><li><p>服务编排文件<code>docker-compose.yml</code></p><figure class="highlight yaml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><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="attr">version:</span> <span class="string">&#x27;3.8&#x27;</span></span><br><span class="line"></span><br><span class="line"><span class="attr">services:</span></span><br><span class="line">  <span class="comment"># 这个名字修改了，下面依赖服务名也要修改，springboot配置文件Redis连接服务名也要修改</span></span><br><span class="line">  <span class="attr">busuanzi-redis:</span></span><br><span class="line">    <span class="attr">image:</span> <span class="string">redis:7-alpine</span></span><br><span class="line">    <span class="attr">container_name:</span> <span class="string">busuanzi-redis</span></span><br><span class="line">    <span class="comment"># 可自行修改宿主机端口，同一个网络服务是联通的，不映射端口直接删除没有影响</span></span><br><span class="line">    <span class="attr">ports:</span></span><br><span class="line">      <span class="bullet">-</span> <span class="string">&quot;16379:6379&quot;</span></span><br><span class="line">    <span class="attr">volumes:</span></span><br><span class="line">      <span class="comment"># 持久化数据目录映射到宿主机</span></span><br><span class="line">      <span class="bullet">-</span> <span class="string">/data/docker/busuanzi/data:/data/redis</span></span><br><span class="line">      <span class="comment"># 日志映射到宿主机</span></span><br><span class="line">      <span class="bullet">-</span> <span class="string">/data/docker/busuanzi/log:/data/log</span></span><br><span class="line">      <span class="comment"># Redis配置文件映射到宿主机</span></span><br><span class="line">      <span class="bullet">-</span> <span class="string">/data/docker/busuanzi/redis.conf:/usr/local/etc/redis/redis.conf</span></span><br><span class="line">    <span class="attr">command:</span> <span class="string">redis-server</span> <span class="string">/usr/local/etc/redis/redis.conf</span> <span class="string">--appendonly</span> <span class="literal">yes</span></span><br><span class="line">    <span class="attr">restart:</span> <span class="string">unless-stopped</span></span><br><span class="line">    <span class="attr">networks:</span></span><br><span class="line">      <span class="bullet">-</span> <span class="string">busuanzi-network</span></span><br><span class="line"></span><br><span class="line">  <span class="attr">busuanzi:</span></span><br><span class="line">    <span class="attr">build:</span> </span><br><span class="line">      <span class="attr">context:</span> <span class="string">.</span></span><br><span class="line">      <span class="attr">dockerfile:</span> <span class="string">Dockerfile</span></span><br><span class="line">    <span class="attr">container_name:</span> <span class="string">busuanzi</span></span><br><span class="line">    <span class="attr">ports:</span></span><br><span class="line">      <span class="bullet">-</span> <span class="string">&quot;10010:10010&quot;</span></span><br><span class="line">    <span class="attr">depends_on:</span></span><br><span class="line">      <span class="bullet">-</span> <span class="string">busuanzi-redis</span></span><br><span class="line">    <span class="attr">restart:</span> <span class="string">unless-stopped</span></span><br><span class="line">    <span class="attr">networks:</span></span><br><span class="line">      <span class="bullet">-</span> <span class="string">busuanzi-network</span></span><br><span class="line"></span><br><span class="line"><span class="attr">networks:</span></span><br><span class="line">  <span class="attr">busuanzi-network:</span></span><br><span class="line">    <span class="attr">driver:</span> <span class="string">bridge</span></span><br></pre></td></tr></table></figure></li></ol><p>将上述三个配置文件和springboot打包生成的jar包放在一个文件夹中，然后执行<code>docker-compose up -d</code>即可运行整个编排服务。</p><p>服务部署好后，自行使用nginx进行反向代理，推荐使用<a href="https://www.lxware.cn/">1panel</a>,里面的网站部署使用了<code>OpenResty</code>，直接创建一个反向代理即可。如果安装了<code>1panel</code>，docker相关环境基本全部自动安装好，无需自己手动在安装环境，可以一步到位。</p><h1 id="busuanzi数据迁移"><a href="#busuanzi数据迁移" class="headerlink" title="busuanzi数据迁移"></a>busuanzi数据迁移</h1><p>不太想放弃原有的busuanzi数据，可以直接将原有的busuanzi数据迁移到服务部署的redis当中，我讲一下比较简单的一个流程。</p><ol><li>获取当前站点的sitemap</li><li>然后根据sitemap获取到站点所有的页面URL</li><li>然后根据每个页面的URL去请求原有的busuanzi接口</li><li>将得到的数据设置到服务的Redis当中即可</li></ol>]]></content:encoded>
            
            
            <category domain="https://yww52.com/categories/%E6%88%91%E7%9A%84%E6%8A%80%E6%9C%AF%E5%AD%A6%E4%B9%A0%E7%89%A9%E8%AF%AD%E6%9E%9C%E7%84%B6%E6%9C%89%E9%97%AE%E9%A2%98/">我的技术学习物语果然有问题</category>
            
            <category domain="https://yww52.com/categories/%E6%88%91%E7%9A%84%E6%8A%80%E6%9C%AF%E5%AD%A6%E4%B9%A0%E7%89%A9%E8%AF%AD%E6%9E%9C%E7%84%B6%E6%9C%89%E9%97%AE%E9%A2%98/%E9%A1%B9%E7%9B%AE%E5%BC%80%E5%8F%91/">项目开发</category>
            
            
            
            <comments>https://yww52.com/posts/5315dfbb/#disqus_thread</comments>
            
        </item>
        
        <item>
            <title>Java图片处理</title>
            <link>https://yww52.com/posts/91f4854c/</link>
            <guid>https://yww52.com/posts/91f4854c/</guid>
            <pubDate>Mon, 17 Apr 2023 16:00:00 GMT</pubDate>
            
            <description>最近经常接触Java的图片处理，所以特地来记录一下。</description>
            
            
            
            <content:encoded><![CDATA[<img src="https://img.yww52.com/2023/4/2023-4-18top_img.jpg" alt="Java图片处理" style="max-width:100%;height:auto;margin-bottom:15px;"
  /><h1 id="开发环境"><a href="#开发环境" class="headerlink" title="开发环境"></a>开发环境</h1><p>本次处理图片主要使用的依赖是<code>opencv</code>，具体的方法可以去参照官网。</p><ol><li><p>首先去<a href="https://opencv.org/releases/">opencv官网</a>去下载与系统对应的jar包和依赖文件。（这里以windows举例）</p></li><li><p>选择windows版本<code>opencv-4.7.0-windows.exe</code>，然后进行安装。(linux的版本需要先进行编译后获取动态库和jar包)</p></li><li><p>在安装目录的<code>opencv/build/java</code>的目录下，获得<code>jar</code>包和动态库文件。（注意是安装目录里）</p></li><li><p>在maven项目引入<code>opencv</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></pre></td><td class="code"><pre><span class="line"><span class="comment">&lt;!-- OpenCv  --&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.opencv<span class="tag">&lt;/<span class="name">groupId</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">artifactId</span>&gt;</span>opencv<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.7.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>system<span class="tag">&lt;/<span class="name">scope</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">systemPath</span>&gt;</span>$&#123;basedir&#125;/src/main/resources/lib/opencv-470.jar<span class="tag">&lt;/<span class="name">systemPath</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>opencv</code>动态库</p><p>由于opencv的开发语言问题，所以Java在使用opencv相关类的时候，需要先加载对应的动态库才行，不然直接使用会出现异常。需要注意linux和windows的动态库是不一样的。</p></li></ol><blockquote><p>好像最新版本的opencv（4.7.0）不支持Java8，只能有Java11，如果用Java8的话，可以使用4.6.0的版本。</p><p>如果觉得opencv配置环境过于麻烦，可以使用Javacv，Javacv其实就是调用opencv进行操作的，虽然官方缺少文档（好像速度也慢一些，封装过的也正常），不过和opencv的调用方法都差不多。</p></blockquote><h1 id="linux的编译"><a href="#linux的编译" class="headerlink" title="linux的编译"></a>linux的编译</h1><p>上述的动态库<code>dll</code>格式是windows上使用的，要想在对应的linux上使用，还得在linux上编译opencv的源码，获得对应的<code>jar包</code>和<code>so动态库</code>文件（jar包应该是不分系统的）。</p><h1 id="加载动态库"><a href="#加载动态库" class="headerlink" title="加载动态库"></a>加载动态库</h1><p>因为Java调用的<code>opencv</code>都是原生<code>native</code>方法，所以使用opencv的方法之前，需要加载动态库。</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><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"> * 加载opencv动态库</span></span><br><span class="line"><span class="comment"> * win: opencv_java470.dll</span></span><br><span class="line"><span class="comment"> * linux: opencv_java470.so</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_">load</span><span class="params">()</span> &#123;</span><br><span class="line">    System.load(ResourceUtil.getResource(<span class="string">&quot;lib/opencv_java470.dll&quot;</span>).getPath());</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><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></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_">InitConfig</span> &#123;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@PostConstruct</span></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">initDll</span><span class="params">()</span> &#123;</span><br><span class="line">        OpencvUtil.load();</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h1 id="opencv基础操作"><a href="#opencv基础操作" class="headerlink" title="opencv基础操作"></a>opencv基础操作</h1><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></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">void</span> <span class="title function_">main</span><span class="params">(String[] args)</span> &#123;</span><br><span class="line">        OpencvUtil.load();</span><br><span class="line">        <span class="type">String</span> <span class="variable">filePath</span> <span class="operator">=</span> <span class="string">&quot;C:/Users/11419/Desktop/project/image-process/src/main/resources/test/1.jpg&quot;</span>;</span><br><span class="line">        <span class="comment">// Mat是opencv表示一张图片的基础类</span></span><br><span class="line">        <span class="type">Mat</span> <span class="variable">mat</span> <span class="operator">=</span> Imgcodecs.imread(filePath);</span><br><span class="line"></span><br><span class="line">        <span class="comment">// Mat其实就是一个像素矩阵</span></span><br><span class="line">        System.out.println(mat.rows());</span><br><span class="line">        System.out.println(mat.cols());</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>, rows = mat.rows(); i &lt; rows; i++) &#123;</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>, cols = mat.cols(); j &lt; cols; j++) &#123;</span><br><span class="line">                <span class="type">double</span>[] data = mat.get(i, j);</span><br><span class="line">                System.out.println(Arrays.toString(data));</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">        mat.release();</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><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><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_">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">void</span> <span class="title function_">main</span><span class="params">(String[] args)</span> &#123;</span><br><span class="line">        OpencvUtil.load();</span><br><span class="line">        <span class="type">String</span> <span class="variable">filePath</span> <span class="operator">=</span> <span class="string">&quot;C:/Users/yww/Desktop/project/image-process/src/main/resources/test/1.jpg&quot;</span>;</span><br><span class="line">        <span class="type">String</span> <span class="variable">output</span> <span class="operator">=</span> <span class="string">&quot;C:/Users/yww/Desktop/project/image-process/src/main/resources/test/2.jpg&quot;</span>;</span><br><span class="line"></span><br><span class="line">        <span class="type">Mat</span> <span class="variable">mat</span> <span class="operator">=</span> Imgcodecs.imread(filePath);</span><br><span class="line">        <span class="comment">// 写出图片</span></span><br><span class="line">        Imgcodecs.imwrite(output, mat);</span><br><span class="line">        <span class="comment">// 释放内存</span></span><br><span class="line">        mat.release();</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><blockquote><p><code>Imgcodecs.imread()</code>这个方法，偶然碰见过一次保存失败的情况，成功运行却没有保存文件，所以还可以通过字节方式保存。</p></blockquote><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><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></span><br><span class="line"><span class="comment"> * <span class="doctag">@param</span> mat       图片矩阵</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@param</span> filePath  文件路径</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_">saveImage</span><span class="params">(Mat mat, String filePath)</span> &#123;</span><br><span class="line">    saveImage(mat, filePath, <span class="string">&quot;png&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="comment"> * <span class="doctag">@param</span> mat       图片矩阵</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@param</span> filePath  文件路径</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@param</span> ext       保存文件后缀</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_">saveImage</span><span class="params">(Mat mat, String filePath, String ext)</span> &#123;</span><br><span class="line">    <span class="type">MatOfByte</span> <span class="variable">matOfByte</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">MatOfByte</span>();</span><br><span class="line">    Imgcodecs.imencode(<span class="string">&quot;.&quot;</span> + ext, mat, matOfByte);</span><br><span class="line">    <span class="type">byte</span>[] byteArray = matOfByte.toArray();</span><br><span class="line">    FileUtil.writeBytes(byteArray, filePath);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h2 id="释放图片"><a href="#释放图片" class="headerlink" title="释放图片"></a>释放图片</h2><p>在Java中使用opencv的话，这个操作比不可少。在opencv的库中，几乎都是<code>native</code>方法，所以导致对象的内存管理大多都不是由Java虚拟机管理，需要手动进行释放内存，不然会出现服务的使用内存不断升高，直至内存不够崩溃。而且期间不会出现内存溢出，dump文件也找不到占用很多内存的Mat类。所以释放图片内存是需要注意的。</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><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_">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">void</span> <span class="title function_">main</span><span class="params">(String[] args)</span> &#123;</span><br><span class="line">        OpencvUtil.load();</span><br><span class="line">        <span class="type">String</span> <span class="variable">filePath</span> <span class="operator">=</span> <span class="string">&quot;C:/Users/yww/Desktop/project/image-process/src/main/resources/test/1.jpg&quot;</span>;</span><br><span class="line">        <span class="type">String</span> <span class="variable">output</span> <span class="operator">=</span> <span class="string">&quot;C:/Users/yww/Desktop/project/image-process/src/main/resources/test/2.jpg&quot;</span>;</span><br><span class="line"></span><br><span class="line">        <span class="type">Mat</span> <span class="variable">mat</span> <span class="operator">=</span> Imgcodecs.imread(filePath);</span><br><span class="line">        <span class="comment">// 写出图片</span></span><br><span class="line">        Imgcodecs.imwrite(output, mat);</span><br><span class="line">        <span class="comment">// 释放内存</span></span><br><span class="line">        mat.release();</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>如果你使用了<code>lombok</code>，可以使用注解<code>@Cleanup</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="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">void</span> <span class="title function_">main</span><span class="params">(String[] args)</span> &#123;</span><br><span class="line">        OpencvUtil.load();</span><br><span class="line">        <span class="type">String</span> <span class="variable">filePath</span> <span class="operator">=</span> <span class="string">&quot;C:/Users/yww/Desktop/project/image-process/src/main/resources/test/1.jpg&quot;</span>;</span><br><span class="line">        <span class="type">String</span> <span class="variable">output</span> <span class="operator">=</span> <span class="string">&quot;C:/Users/yww/Desktop/project/image-process/src/main/resources/test/2.jpg&quot;</span>;</span><br><span class="line">        <span class="meta">@Cleanup(value = &quot;release&quot;)</span></span><br><span class="line">        <span class="type">Mat</span> <span class="variable">mat</span> <span class="operator">=</span> Imgcodecs.imread(filePath);</span><br><span class="line">        <span class="comment">// 写出图片</span></span><br><span class="line">        Imgcodecs.imwrite(output, mat);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h1 id="获取图片基本信息"><a href="#获取图片基本信息" class="headerlink" title="获取图片基本信息"></a>获取图片基本信息</h1><p>获取图片的基本信息，使用opencv并不方便，所以可以使用其他的库来进行。</p><h2 id="获取图片DPI"><a href="#获取图片DPI" class="headerlink" title="获取图片DPI"></a>获取图片DPI</h2><p>暂时有两种方法可以获取图片的DPI。</p><ol><li><p>commons-imaging依赖</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><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"> * 获取图片的DPI，获取不到返回-1</span></span><br><span class="line"><span class="comment"> * 依赖于commons-imaging</span></span><br><span class="line"><span class="comment"> * &lt;dependency&gt;</span></span><br><span class="line"><span class="comment"> *   &lt;groupId&gt;org.apache.commons&lt;/groupId&gt;</span></span><br><span class="line"><span class="comment"> *   &lt;artifactId&gt;commons-imaging&lt;/artifactId&gt;</span></span><br><span class="line"><span class="comment"> *   &lt;version&gt;$&#123;commons-imaging.version&#125;&lt;/version&gt;</span></span><br><span class="line"><span class="comment"> * &lt;/dependency&gt;</span></span><br><span class="line"><span class="comment"> *</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@param</span> filePath  图片文件位置</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@return</span>          图片DPI</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">int</span> <span class="title function_">getDpi1</span><span class="params">(String filePath)</span> &#123;</span><br><span class="line">    <span class="type">ImageInfo</span> <span class="variable">imageInfo</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">        imageInfo = Imaging.getImageInfo(FileUtil.file(filePath));</span><br><span class="line">    &#125; <span class="keyword">catch</span> (ImageReadException | IOException e) &#123;</span><br><span class="line">        <span class="keyword">throw</span> <span class="keyword">new</span> <span class="title class_">RuntimeException</span>(<span class="string">&quot;获取图片信息出错！&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">if</span> (<span class="literal">null</span> == imageInfo) &#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> imageInfo.getPhysicalWidthDpi();</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br><span class="line">   </span><br></pre></td></tr></table></figure></li><li><p>metadata-extractor依赖</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><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"> * 获取图片的DPI，获取不到返回-1</span></span><br><span class="line"><span class="comment"> * 依赖于metadata-extractor</span></span><br><span class="line"><span class="comment"> * &lt;dependency&gt;</span></span><br><span class="line"><span class="comment"> *   &lt;groupId&gt;com.drewnoakes&lt;/groupId&gt;</span></span><br><span class="line"><span class="comment"> *   &lt;artifactId&gt;metadata-extractor&lt;/artifactId&gt;</span></span><br><span class="line"><span class="comment"> *   &lt;version&gt;$&#123;metadata.version&#125;&lt;/version&gt;</span></span><br><span class="line"><span class="comment"> * &lt;/dependency&gt;</span></span><br><span class="line"><span class="comment"> *</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@param</span> filePath  图片文件位置</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@return</span>          图片DPI</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">int</span> <span class="title function_">getDpi2</span><span class="params">(String filePath)</span> &#123;</span><br><span class="line">    <span class="type">Metadata</span> <span class="variable">metadata</span> <span class="operator">=</span> <span class="literal">null</span>;</span><br><span class="line">    <span class="type">int</span> <span class="variable">res</span> <span class="operator">=</span> -<span class="number">1</span>;</span><br><span class="line">    <span class="keyword">try</span> &#123;</span><br><span class="line">        metadata = ImageMetadataReader.readMetadata(FileUtil.file(filePath));</span><br><span class="line">        <span class="keyword">for</span> (Directory directory : metadata.getDirectories()) &#123;</span><br><span class="line">            <span class="comment">// 遍历图片信息，寻找水平分辨率</span></span><br><span class="line">            <span class="keyword">for</span> (Tag tag : directory.getTags()) &#123;</span><br><span class="line">                <span class="keyword">if</span> (<span class="string">&quot;X Resolution&quot;</span>.equals(tag.getTagName())) &#123;</span><br><span class="line">                    res = Convert.toInt(tag.getDescription());</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 class="keyword">catch</span> (ImageProcessingException | IOException e) &#123;</span><br><span class="line">        <span class="keyword">throw</span> <span class="keyword">new</span> <span class="title class_">RuntimeException</span>(<span class="string">&quot;获取图片信息出错！&quot;</span>);</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">return</span> res;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></li></ol><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></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> filePath  图片文件位置</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@return</span>          [width, height] 水平分辨率 x 垂直分辨率</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">int</span>[] getResolution(String filePath) &#123;</span><br><span class="line">    <span class="meta">@Cleanup(value = &quot;release&quot;)</span></span><br><span class="line">    <span class="type">Mat</span> <span class="variable">mat</span> <span class="operator">=</span> Imgcodecs.imread(filePath);</span><br><span class="line">    <span class="type">int</span> <span class="variable">widthResolution</span> <span class="operator">=</span> mat.width();</span><br><span class="line">    <span class="type">int</span> <span class="variable">heightResolution</span> <span class="operator">=</span> mat.height();</span><br><span class="line">    <span class="keyword">return</span> <span class="keyword">new</span> <span class="title class_">int</span>[]&#123;widthResolution, heightResolution&#125;;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><blockquote><p>这些操作实质是获取图片的基本信息得到的，所以图片大部分的基本信息都可以通过这些方法修改得到。</p></blockquote><h2 id="图片旋转"><a href="#图片旋转" class="headerlink" title="图片旋转"></a>图片旋转</h2><h3 id="Graphics2D旋转"><a href="#Graphics2D旋转" class="headerlink" title="Graphics2D旋转"></a>Graphics2D旋转</h3><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * 使用Graphics2D进行旋转图片</span></span><br><span class="line"><span class="comment"> *</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@param</span> src       输入路径</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@param</span> dst       输出路径</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@param</span> degree    旋转角度</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_">rotateImage</span><span class="params">(String src, String dst, <span class="type">double</span> degree)</span> &#123;</span><br><span class="line">    <span class="comment">// 读取图片</span></span><br><span class="line">    <span class="type">BufferedImage</span> <span class="variable">image</span> <span class="operator">=</span> ImgUtil.read(src);</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 获取图片宽，高，类型</span></span><br><span class="line">    <span class="type">int</span> <span class="variable">width</span> <span class="operator">=</span> image.getWidth();</span><br><span class="line">    <span class="type">int</span> <span class="variable">height</span> <span class="operator">=</span> image.getHeight();</span><br><span class="line">    <span class="type">int</span> <span class="variable">type</span> <span class="operator">=</span> image.getType();</span><br><span class="line">    <span class="comment">// 创建Graphics2D</span></span><br><span class="line">    <span class="type">BufferedImage</span> <span class="variable">res</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">BufferedImage</span>(width, height, type);</span><br><span class="line">    <span class="type">Graphics2D</span> <span class="variable">graphics</span>  <span class="operator">=</span> res.createGraphics();</span><br><span class="line">    <span class="comment">// 设置图形渲染选项，指定双线性插值算法作为图像缩放时的默认算法</span></span><br><span class="line">    graphics.setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BILINEAR);</span><br><span class="line">    <span class="comment">// 设置图片底色</span></span><br><span class="line">    graphics.setBackground(Color.WHITE);</span><br><span class="line">    <span class="comment">// 填充底图</span></span><br><span class="line">    graphics.fillRect(<span class="number">0</span>, <span class="number">0</span>, width, height);</span><br><span class="line">    <span class="comment">// 按中心点旋转图片</span></span><br><span class="line">    graphics.rotate(Math.toRadians(degree), width &gt;&gt; <span class="number">1</span>, height &gt;&gt; <span class="number">1</span>);</span><br><span class="line">    graphics.drawImage(image, <span class="number">0</span>, <span class="number">0</span>, <span class="literal">null</span>);</span><br><span class="line">    <span class="comment">// 关闭Graphics2D</span></span><br><span class="line">    graphics.dispose();</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 写出图片</span></span><br><span class="line">    ImgUtil.write(res, FileUtil.file(dst));</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h3 id="opencv旋转"><a href="#opencv旋转" class="headerlink" title="opencv旋转"></a>opencv旋转</h3><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br></pre></td><td class="code"><pre><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     * 图片旋转</span></span><br><span class="line"><span class="comment">     *</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@param</span> image 输入图片</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@param</span> angle 旋转角度</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> Mat <span class="title function_">rotate</span><span class="params">(Mat image, <span class="type">double</span> angle)</span> &#123;</span><br><span class="line">        <span class="type">int</span> <span class="variable">w</span> <span class="operator">=</span> image.cols();</span><br><span class="line">        <span class="type">int</span> <span class="variable">h</span> <span class="operator">=</span> image.rows();</span><br><span class="line">&gt; 这种方法自定义程度很高，但是消耗的性能也会增加，暂无需求的话推荐使用opencv的方法。</span><br><span class="line"></span><br><span class="line">### opencv方法</span><br><span class="line"></span><br><span class="line">```Java</span><br><span class="line">    <span class="comment">/**</span></span><br><span 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> image 输入图片</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@param</span> angle 旋转角度</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> Mat <span class="title function_">rotate</span><span class="params">(Mat image, <span class="type">double</span> angle)</span> &#123;</span><br><span class="line">        <span class="type">int</span> <span class="variable">w</span> <span class="operator">=</span> image.cols();</span><br><span class="line">        <span class="type">int</span> <span class="variable">h</span> <span class="operator">=</span> image.rows();</span><br><span class="line"></span><br><span class="line">        <span class="comment">// 定义参数</span></span><br><span class="line">        <span class="type">double</span> <span class="variable">scale</span> <span class="operator">=</span> <span class="number">1.0</span>;</span><br><span class="line">        <span class="type">Point</span> <span class="variable">center</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">Point</span>((<span class="type">double</span>) w / <span class="number">2</span>, (<span class="type">double</span>) h / <span class="number">2</span>);</span><br><span class="line"></span><br><span class="line">        <span class="comment">// 定义旋转矩阵（需要注意的是此处正角度是逆时针旋转，所以取反）</span></span><br><span class="line">        <span class="meta">@Cleanup(value = &quot;release&quot;)</span></span><br><span class="line">        <span class="type">Mat</span> <span class="variable">rotationMatrix</span> <span class="operator">=</span> Imgproc.getRotationMatrix2D(center, -angle, scale);</span><br><span class="line"></span><br><span class="line">        <span class="comment">// 旋转矩阵</span></span><br><span class="line">        <span class="type">Mat</span> <span class="variable">rotatedImage</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">Mat</span>();</span><br><span class="line">        <span class="type">Scalar</span> <span class="variable">backgroundColor</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">Scalar</span>(<span class="number">255</span>, <span class="number">255</span>, <span class="number">255</span>);</span><br><span class="line">        Imgproc.warpAffine(image, rotatedImage, rotationMatrix, <span class="keyword">new</span> <span class="title class_">Size</span>(w, h), Imgproc.INTER_LINEAR, Core.BORDER_CONSTANT, backgroundColor);</span><br><span class="line"></span><br><span class="line">        <span class="keyword">return</span> rotatedImage;</span><br><span class="line">    &#125;</span><br></pre></td></tr></table></figure><pre><code>    if (angle &lt; 0) &#123;        angle = 360 + angle;    &#125;    Point center = new Point((double) w / 2, (double) h / 2);    double scale = 1.0;    Mat rotationMatrix = Imgproc.getRotationMatrix2D(center, angle, scale);    // 设置填充颜色为白色    Scalar backgroundColor = new Scalar(255, 255, 255);    Mat rotatedImage = new Mat();    Imgproc.warpAffine(image, rotatedImage, rotationMatrix, new Size(w, h), Imgproc.INTER_LINEAR, Core.BORDER_CONSTANT, backgroundColor);    rotationMatrix.release();    return rotatedImage;&#125;</code></pre><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><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></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">```Java</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">     * @param srcImgPath    图片路径</span><br><span class="line">     */</span><br><span class="line">    public static void correctImg(String srcImgPath) &#123;</span><br><span class="line">        FileOutputStream fos = null;</span><br><span class="line">        try &#123;</span><br><span class="line">            // 原始图片</span><br><span class="line">            File srcFile = new File(srcImgPath);</span><br><span class="line">            // 获取偏转角度</span><br><span class="line">            int angle = getAngle(srcFile);</span><br><span class="line">            if (angle != 90 &amp;&amp; angle != 270) &#123;</span><br><span class="line">                return;</span><br><span class="line">            &#125;</span><br><span class="line">            // 原始图片缓存</span><br><span class="line">            BufferedImage srcImg = ImageIO.read(srcFile);</span><br><span class="line">            // 宽高互换</span><br><span class="line">            // 原始宽度</span><br><span class="line">            int imgWidth = srcImg.getHeight();</span><br><span class="line">            // 原始高度</span><br><span class="line">            int imgHeight = srcImg.getWidth();</span><br><span class="line">            // 中心点位置</span><br><span class="line">            double centerWidth = ((double) imgWidth) / 2;</span><br><span class="line">            double centerHeight = ((double) imgHeight) / 2;</span><br><span class="line">            // 图片缓存</span><br><span class="line">            BufferedImage targetImg = new BufferedImage(imgWidth, imgHeight, BufferedImage.TYPE_INT_RGB);</span><br><span class="line">            // 旋转对应角度</span><br><span class="line">            Graphics2D graphics = targetImg.createGraphics();</span><br><span class="line">            graphics.rotate(Math.toRadians(angle), centerWidth, centerHeight);</span><br><span class="line">            graphics.drawImage(srcImg, (imgWidth - srcImg.getWidth()) / 2, (imgHeight - srcImg.getHeight()) / 2, null);</span><br><span class="line">            graphics.rotate(Math.toRadians(-angle), centerWidth, centerHeight);</span><br><span class="line">            graphics.dispose();</span><br><span class="line">            // 输出图片</span><br><span class="line">            fos = new FileOutputStream(srcFile);</span><br><span class="line">            ImageIO.write(targetImg, &quot;jpg&quot;, fos);</span><br><span class="line">        &#125; catch (Exception e) &#123;</span><br><span class="line">            e.printStackTrace();</span><br><span class="line">        &#125; finally &#123;</span><br><span class="line">            if (fos != null) &#123;</span><br><span class="line">                try &#123;</span><br><span class="line">                    fos.flush();</span><br><span class="line">                    fos.close();</span><br><span class="line">                &#125; catch (Exception e) &#123;</span><br><span class="line">                    e.printStackTrace();</span><br><span class="line">                &#125;</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    /**</span><br><span class="line">     * 获取图片旋转角度</span><br><span class="line">     *</span><br><span class="line">     * @param file  上传图片</span><br><span class="line">     * @return      图片旋转角度</span><br><span class="line">     */</span><br><span class="line">    private static int getAngle(File file) throws Exception &#123;</span><br><span class="line">        Metadata metadata = ImageMetadataReader.readMetadata(file);</span><br><span class="line">        for (Directory directory : metadata.getDirectories()) &#123;</span><br><span class="line">            for (Tag tag : directory.getTags()) &#123;</span><br><span class="line">                if (&quot;Orientation&quot;.equals(tag.getTagName())) &#123;</span><br><span class="line">                    String orientation = tag.getDescription();</span><br><span class="line">                    if (orientation.contains(&quot;90&quot;)) &#123;</span><br><span class="line">                        return 90;</span><br><span class="line">                    &#125; else if (orientation.contains(&quot;180&quot;)) &#123;</span><br><span class="line">                        return 180;</span><br><span class="line">                    &#125; else if (orientation.contains(&quot;270&quot;)) &#123;</span><br><span class="line">                        return 270;</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">        return 0;</span><br><span class="line">    &#125;</span><br></pre></td></tr></table></figure><h2 id="灰度化"><a href="#灰度化" class="headerlink" title="灰度化"></a>灰度化</h2><p>大多数彩色图片的每个像素都是由红绿蓝三个通道组成的，为了更方便的处理图像，将这三个通道的值按一定比例进行加权平均，得到一个单通道的灰度值，用来表示该像素的颜色亮度。大多数图片处理都会先进行图片灰度化，因为只有一个通道，处理起来方便很多。opencv的灰度化操作，只需要调用方法即可。灰度化操作的话，主要是要考虑图片通道数量的问题。</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * 图片灰度化</span></span><br><span class="line"><span class="comment"> *</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@param</span> mat       图像矩阵</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> Mat <span class="title function_">gray</span><span class="params">(Mat mat)</span> &#123;</span><br><span class="line">    <span class="type">Mat</span> <span class="variable">gray</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">Mat</span>();</span><br><span class="line">    <span class="comment">// 获取图片的通道数，根据不同通道进行处理</span></span><br><span class="line">    <span class="type">int</span> <span class="variable">channel</span> <span class="operator">=</span> mat.channels();</span><br><span class="line"></span><br><span class="line">    <span class="keyword">if</span> (channel == <span class="number">1</span>) &#123;</span><br><span class="line">        <span class="comment">// 单通道图片无需进行灰度化操作</span></span><br><span class="line">        gray = mat.clone();</span><br><span class="line">    &#125; <span class="keyword">else</span> <span class="keyword">if</span> (channel == <span class="number">2</span>) &#123;</span><br><span class="line">        <span class="comment">// 双通道图片，将两个通道的值相加再除以2，得到灰度值</span></span><br><span class="line">        <span class="meta">@Cleanup(value = &quot;release&quot;)</span></span><br><span class="line">        <span class="type">Mat</span> <span class="variable">temp</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">Mat</span>();</span><br><span class="line">        Core.addWeighted(mat, <span class="number">0.5</span>, mat, <span class="number">0.5</span>, <span class="number">0</span>, temp);</span><br><span class="line">        Imgproc.cvtColor(temp, gray, Imgproc.COLOR_BGR2GRAY);</span><br><span class="line">    &#125; <span class="keyword">else</span> <span class="keyword">if</span> (channel == <span class="number">4</span> || channel == <span class="number">3</span>) &#123;</span><br><span class="line">        <span class="comment">// 三通道和四通道的图片，使用标准的灰度化方式，考虑Alpha分量</span></span><br><span class="line">        Imgproc.cvtColor(mat, gray, Imgproc.COLOR_BGR2GRAY);</span><br><span class="line">    &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">        <span class="comment">// 其他通道，暂不支持灰度化操作</span></span><br><span class="line">        <span class="keyword">throw</span> <span class="keyword">new</span> <span class="title class_">UnsupportedOperationException</span>(<span class="string">&quot;不支持灰度化的通道数： --&gt;&quot;</span> + channel);</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">return</span> gray;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h2 id="高斯滤波"><a href="#高斯滤波" class="headerlink" title="高斯滤波"></a>高斯滤波</h2><p>处理图像时，往往需要对图像进行平滑操作以去除噪声，同时也可以模糊图像以达到柔化的效果。高斯滤波（Gaussian blur）就是一种常用的平滑滤波器，其通过对图像像素进行加权平均的方式来实现平滑和模糊的效果。高斯滤波的原理是使用一个高斯核（Gaussian kernel）对图像像素进行加权平均。一般来说，高斯核的大小或标准差越大，平滑效果越明显，图像的细节丢失也会越多。</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><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"> * 第四个和第五个参数为标准差，设置为0，则根据核大小自动计算</span></span><br><span class="line"><span class="comment"> *</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@param</span> mat       图像矩阵</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> Mat <span class="title function_">gaussianBlur</span><span class="params">(Mat mat)</span> &#123;</span><br><span class="line">    <span class="type">Mat</span> <span class="variable">blurred</span> <span class="operator">=</span> mat.clone();</span><br><span class="line">    Imgproc.GaussianBlur(mat, blurred, <span class="keyword">new</span> <span class="title class_">Size</span>(<span class="number">3</span>, <span class="number">3</span>), <span class="number">0</span>, <span class="number">0</span>);</span><br><span class="line">    <span class="keyword">return</span> blurred;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h2 id="图像二值化"><a href="#图像二值化" class="headerlink" title="图像二值化"></a>图像二值化</h2><p>图像二值化很好理解，就是把一张彩色图片或者灰度图片转化为黑白二值图像，即每个像素只有两种可能的选值，通常是黑色（0）和白色（255）。主要用于分割某些图片的用途。</p><p>二值化具体步骤：</p><ol><li>灰度化。这一步是因为在大多数情况下，只考虑像素的亮度信息就足够了，灰度图像的每个像素值表示亮度的强度，通常在0（黑色）到255（白色）之间。</li><li>选择一个阈值，阈值是一个灰度值，用于判断像素应该被归类为黑色还是白色。选值方法有很多，大概有以下三种<ul><li>全局阈值：简单地选择一个固定的全局阈值，将所有像素比较与该阈值，高于阈值的像素设置为白色，低于阈值的像素设置为黑色。</li><li>局部阈值：根据像素的局部区域来选择阈值，每个像素的阈值根据其周围像素的统计特性（如均值、方差等）来确定。</li><li>自适应阈值：根据图像的不同区域自适应地选择阈值。常见的方法有基于局部均值、基于Otsu算法等。</li></ul></li><li>图片进行二值化处理，对于每个像素，如果其灰度值大于阈值，则将其设为白色（255），否则设为黑色（0）。</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="comment">/**</span></span><br><span 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> src       图片矩阵</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> Mat <span class="title function_">threshold</span><span class="params">(Mat src)</span> &#123;</span><br><span class="line">    <span class="meta">@Cleanup(value = &quot;release&quot;)</span></span><br><span class="line">    <span class="type">Mat</span> <span class="variable">gray</span> <span class="operator">=</span> gray(src);</span><br><span class="line">    <span class="type">Mat</span> <span class="variable">threshold</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">Mat</span>();</span><br><span class="line">    Imgproc.threshold(gray, threshold, <span class="number">120</span>, <span class="number">255</span>, Imgproc.THRESH_BINARY);</span><br><span class="line">    <span class="keyword">return</span> threshold;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h2 id="图像膨胀与腐蚀"><a href="#图像膨胀与腐蚀" class="headerlink" title="图像膨胀与腐蚀"></a>图像膨胀与腐蚀</h2><p>腐蚀与膨胀主要用于处理二值化图像或者是灰度图像。</p><h3 id="膨胀"><a href="#膨胀" class="headerlink" title="膨胀"></a>膨胀</h3><p>膨胀主要作用是对二值化物体边界点进行扩充，将与物体接触的所有背景点合并到该物体中，使边界向外部扩张。</p><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><span class="line">9</span><br><span class="line">10</span><br><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><br><span class="line"> *  图片膨胀</span><br><span class="line"> *</span><br><span class="line"> * @param src       图片矩阵</span><br><span class="line"> * @return          图像矩阵</span><br><span class="line"> */</span><br><span class="line">public static Mat erode(Mat src) &#123;</span><br><span class="line">    Mat kernel = Imgproc.getStructuringElement(Imgproc.MORPH_RECT, new Size(5, 5));</span><br><span class="line">    @Cleanup(value = &quot;release&quot;)</span><br><span class="line">    Mat gray = gray(src);</span><br><span class="line">    Mat erode = new Mat();</span><br><span class="line">    Imgproc.erode(gray, erode, kernel);</span><br><span class="line">    return erode;</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>可将两个有细小连通的物体分开，去除毛刺，小凸起等噪点。如果两个物体间有细小的连通，当结构足够大时，可以将两个物体分开。</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><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> src       图片矩阵</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> Mat <span class="title function_">dilate</span><span class="params">(Mat src)</span> &#123;</span><br><span class="line">    <span class="type">Mat</span> <span class="variable">kernel</span> <span class="operator">=</span> Imgproc.getStructuringElement(Imgproc.MORPH_RECT, <span class="keyword">new</span> <span class="title class_">Size</span>(<span class="number">5</span>, <span class="number">5</span>));</span><br><span class="line">    <span class="meta">@Cleanup(value = &quot;release&quot;)</span></span><br><span class="line">    <span class="type">Mat</span> <span class="variable">gray</span> <span class="operator">=</span> gray(src);</span><br><span class="line">    <span class="type">Mat</span> <span class="variable">dilate</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">Mat</span>();</span><br><span class="line">    Imgproc.dilate(gray, dilate, kernel);</span><br><span class="line">    <span class="keyword">return</span> dilate;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h2 id="边缘检测"><a href="#边缘检测" class="headerlink" title="边缘检测"></a>边缘检测</h2><p>边缘是指图像中颜色或亮度发生急剧变化的区域，通常是物体之间的边界或物体内部的纹理。边缘检测算法旨在在图像中找到这些边缘并将其标记出来。具体的步骤如下：</p><ol><li>图像灰度化</li><li>此处还可以进行（高斯模糊，二值化，图片膨胀，图片腐蚀等操作去除噪点）</li><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><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><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="comment"> * <span class="doctag">@param</span> mat   图像矩阵</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> Mat <span class="title function_">canny</span><span class="params">(Mat mat)</span> &#123;</span><br><span class="line">    <span class="keyword">return</span> canny(mat, <span class="number">60</span>, <span class="number">200</span>, <span class="number">3</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="comment"> *  一般来说threshold2大于threshold1，保证能够检测到真正的边缘</span></span><br><span class="line"><span class="comment"> *  threshold1一般设置为图像灰度级的20%-30%</span></span><br><span class="line"><span class="comment"> *  threshold2一般设置为threshold1的三倍</span></span><br><span class="line"><span class="comment"> *</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@param</span> mat           图片矩阵</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@param</span> threshold1    边缘的阈值，用于检测强边缘</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@param</span> threshold2    边缘的阈值，用于检测弱边缘</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@param</span> apertureSize  Sobel算子的大小，一般为3，5或7</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> Mat <span class="title function_">canny</span><span class="params">(Mat mat, <span class="type">int</span> threshold1, <span class="type">int</span> threshold2, <span class="type">int</span> apertureSize)</span> &#123;</span><br><span class="line">    <span class="comment">// 进行高斯平滑</span></span><br><span class="line">    <span class="meta">@Cleanup(value = &quot;release&quot;)</span></span><br><span class="line">    <span class="type">Mat</span> <span class="variable">blurred</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">Mat</span>();</span><br><span class="line">    Imgproc.GaussianBlur(mat, blurred, <span class="keyword">new</span> <span class="title class_">Size</span>(<span class="number">3</span>, <span class="number">3</span>), <span class="number">0</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="meta">@Cleanup(value = &quot;release&quot;)</span></span><br><span class="line">    <span class="type">Mat</span> <span class="variable">gray</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">Mat</span>();</span><br><span class="line">    Imgproc.cvtColor(blurred, gray, Imgproc.COLOR_BGR2GRAY);</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 进行边缘检测</span></span><br><span class="line">    <span class="type">Mat</span> <span class="variable">canny</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">Mat</span>();</span><br><span class="line">    Imgproc.Canny(gray, canny, threshold1, threshold2, apertureSize);</span><br><span class="line"></span><br><span class="line">    <span class="keyword">return</span> canny;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h1 id="一些图片的实际应用"><a href="#一些图片的实际应用" class="headerlink" title="一些图片的实际应用"></a>一些图片的实际应用</h1><h2 id="检测图片亮度"><a href="#检测图片亮度" class="headerlink" title="检测图片亮度"></a>检测图片亮度</h2><p>网上关于图片亮度检测的方法，大概有两种。</p><h3 id="图片平均亮度值"><a href="#图片平均亮度值" class="headerlink" title="图片平均亮度值"></a>图片平均亮度值</h3><p>使用图片的平均灰度值来代表图片的平均亮度。</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br></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"> * mean 获取Mat中各个通道的均值</span></span><br><span class="line"><span class="comment"> *</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@param</span> filePath  图片路径</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> <span class="type">double</span> <span class="title function_">brightness</span><span class="params">(String filePath)</span> &#123;</span><br><span class="line">    <span class="type">Mat</span> <span class="variable">src</span> <span class="operator">=</span> Imgcodecs.imread(filePath);</span><br><span class="line">    <span class="comment">// 灰度化，转为灰度图</span></span><br><span class="line">    <span class="type">Mat</span> <span class="variable">gray</span> <span class="operator">=</span> ImageUtil.gray(src.clone());</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 计算图像的平均亮度</span></span><br><span class="line">    <span class="type">Scalar</span> <span class="variable">mean</span> <span class="operator">=</span> Core.mean(gray);</span><br><span class="line">    <span class="keyword">return</span> mean.val[<span class="number">0</span>];</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h3 id="图片亮度异常值"><a href="#图片亮度异常值" class="headerlink" title="图片亮度异常值"></a>图片亮度异常值</h3><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br></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"> * cast为计算出的偏差值，小于1表示比较正常，大于1表示存在亮度异常；当cast异常时，da大于0表示过亮，da小于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 class="doctag">@param</span>   filePath    图片路径</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@return</span>              [cast, da] [亮度值， 亮度异常值]</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="keyword">private</span> <span class="type">double</span>[] brightness2(String filePath) &#123;</span><br><span class="line">    <span class="type">Mat</span> <span class="variable">src</span> <span class="operator">=</span> Imgcodecs.imread(filePath);</span><br><span class="line">    <span class="comment">// 灰度化，转为灰度图</span></span><br><span class="line">    <span class="type">Mat</span> <span class="variable">gray</span> <span class="operator">=</span> ImageUtil.gray(src.clone());</span><br><span class="line"></span><br><span class="line">    <span class="type">double</span> <span class="variable">a</span> <span class="operator">=</span> <span class="number">0</span>;</span><br><span class="line">    <span class="type">int</span>[] hist = <span class="keyword">new</span> <span class="title class_">int</span>[<span class="number">256</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; gray.rows(); i++) &#123;</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; gray.cols(); j++) &#123;</span><br><span class="line">            a += gray.get(i, j)[<span class="number">0</span>] - <span class="number">128</span>;</span><br><span class="line">            <span class="type">int</span> <span class="variable">index</span> <span class="operator">=</span> (<span class="type">int</span>) gray.get(i, j)[<span class="number">0</span>];</span><br><span class="line">            hist[index]++;</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="type">double</span> <span class="variable">da</span> <span class="operator">=</span> a / (gray.rows() * gray.cols());</span><br><span class="line">    <span class="type">double</span> <span class="variable">ma</span> <span class="operator">=</span> <span class="number">0</span>;</span><br><span class="line">    <span class="keyword">for</span> (<span class="type">int</span> <span class="variable">i</span> <span class="operator">=</span> <span class="number">0</span>; i &lt; hist.length; i++) &#123;</span><br><span class="line">        ma += Math.abs(i - <span class="number">128</span> - da) * hist[i];</span><br><span class="line">    &#125;</span><br><span class="line">    ma = ma / (gray.rows() * gray.cols());</span><br><span class="line">    <span class="type">double</span> <span class="variable">cast</span> <span class="operator">=</span> Math.abs(da) / Math.abs(ma);</span><br><span class="line">    <span class="keyword">return</span> <span class="keyword">new</span> <span class="title class_">double</span>[] &#123;cast, da&#125;;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h2 id="检测图片的清晰度"><a href="#检测图片的清晰度" class="headerlink" title="检测图片的清晰度"></a>检测图片的清晰度</h2><p>网上检测图片清晰度的方法有三种，需要注意的是，这些计算的指标在同一张图片里，越高代表这张图片越清晰。所以可能会出现一种情况，就是有一张模糊的图片指标比有一张清晰的指标高。</p><h3 id="Tenengrad梯度"><a href="#Tenengrad梯度" class="headerlink" title="Tenengrad梯度"></a>Tenengrad梯度</h3><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * Tenengrad梯度方法计算清晰度</span></span><br><span class="line"><span class="comment"> * Tenengrad梯度方法利用Sobel算子分别计算水平和垂直方向的梯度，同一场景下梯度值越高，图像越清晰。</span></span><br><span class="line"><span class="comment"> *</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@param</span> image  图片矩阵</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> <span class="type">double</span> <span class="title function_">tenengrad</span><span class="params">(Mat image)</span> &#123;</span><br><span class="line">    <span class="comment">// 图片灰度化</span></span><br><span class="line">    <span class="type">Mat</span> <span class="variable">grayImage</span> <span class="operator">=</span> image.clone();</span><br><span class="line">    Imgproc.cvtColor(image, grayImage, Imgproc.COLOR_BGR2GRAY);</span><br><span class="line"></span><br><span class="line">    <span class="comment">// Sobel算子</span></span><br><span class="line">    <span class="type">Mat</span> <span class="variable">sobelImage</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">Mat</span>();</span><br><span class="line">    Imgproc.Sobel(grayImage, sobelImage, CvType.CV_16U, <span class="number">1</span>, <span class="number">1</span>);</span><br><span class="line"></span><br><span class="line">    <span class="type">Scalar</span> <span class="variable">mean</span> <span class="operator">=</span> Core.mean(sobelImage);</span><br><span class="line">    <span class="type">double</span> <span class="variable">meanValue</span> <span class="operator">=</span> mean.val[<span class="number">0</span>];</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 释放内存</span></span><br><span class="line">    sobelImage.release();</span><br><span class="line">    grayImage.release();</span><br><span class="line"></span><br><span class="line">    <span class="keyword">return</span> meanValue;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h3 id="Laplacian方法计算清晰度"><a href="#Laplacian方法计算清晰度" class="headerlink" title="Laplacian方法计算清晰度"></a>Laplacian方法计算清晰度</h3><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * Laplacian方法计算清晰度</span></span><br><span class="line"><span class="comment"> *</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@param</span> image  图片矩阵</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> <span class="type">double</span> <span class="title function_">laplacian</span><span class="params">(Mat image)</span> &#123;</span><br><span class="line">    <span class="comment">// 图片灰度化</span></span><br><span class="line">    <span class="type">Mat</span> <span class="variable">grayImage</span> <span class="operator">=</span> image.clone();</span><br><span class="line">    Imgproc.cvtColor(image, grayImage, Imgproc.COLOR_BGR2GRAY);</span><br><span class="line"></span><br><span class="line">    <span class="comment">// Laplacian算子</span></span><br><span class="line">    <span class="type">Mat</span> <span class="variable">laplacian</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">Mat</span>();</span><br><span class="line">    Imgproc.Laplacian(grayImage, laplacian, CvType.CV_16U);</span><br><span class="line"></span><br><span class="line">    <span class="type">Scalar</span> <span class="variable">mean</span> <span class="operator">=</span> Core.mean(laplacian);</span><br><span class="line">    <span class="type">double</span> <span class="variable">meanValue</span> <span class="operator">=</span> mean.val[<span class="number">0</span>];</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 释放内存</span></span><br><span class="line">    laplacian.release();</span><br><span class="line">    grayImage.release();</span><br><span class="line"></span><br><span class="line">    <span class="keyword">return</span> meanValue;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h3 id="灰度方差获取图片清晰度"><a href="#灰度方差获取图片清晰度" class="headerlink" title="灰度方差获取图片清晰度"></a>灰度方差获取图片清晰度</h3><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * 通过灰度方差获取图片清晰度</span></span><br><span class="line"><span class="comment"> * 对焦清晰的图像相比对焦模糊的图像，它的数据之间的灰度差异应该更大，即它的方差应该较大，可以通过图像灰度数据的方差来衡量图像的清晰度，方差越大，表示清晰度越好。</span></span><br><span class="line"><span class="comment"> *</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@param</span> image  图片矩阵</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> <span class="type">double</span> <span class="title function_">variance</span><span class="params">(Mat image)</span> &#123;</span><br><span class="line">    <span class="comment">// 图片灰度化</span></span><br><span class="line">    <span class="type">Mat</span> <span class="variable">grayImage</span> <span class="operator">=</span> image.clone();</span><br><span class="line">    Imgproc.cvtColor(image, grayImage, Imgproc.COLOR_BGR2GRAY);</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 计算灰度图像的标准差</span></span><br><span class="line">    <span class="type">MatOfDouble</span> <span class="variable">mean</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">MatOfDouble</span>();</span><br><span class="line">    <span class="type">MatOfDouble</span> <span class="variable">stdDev</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">MatOfDouble</span>();</span><br><span class="line">    Core.meanStdDev(grayImage, mean, stdDev);</span><br><span class="line"></span><br><span class="line">    <span class="type">double</span> <span class="variable">meanValue</span> <span class="operator">=</span> stdDev.get(<span class="number">0</span>, <span class="number">0</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">    mean.release();</span><br><span class="line">    stdDev.release();</span><br><span class="line">    grayImage.release();</span><br><span class="line"></span><br><span class="line">    <span class="keyword">return</span> meanValue;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h2 id="图片亮度处理"><a href="#图片亮度处理" class="headerlink" title="图片亮度处理"></a>图片亮度处理</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><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * 图片亮度调整</span></span><br><span class="line"><span class="comment"> *</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@param</span> src   输入路径</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@param</span> dst   输出路径</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_">adjustBrightness</span><span class="params">(String src, String dst)</span> &#123;</span><br><span class="line">    <span class="comment">// 读取图片</span></span><br><span class="line">    <span class="meta">@Cleanup(value = &quot;release&quot;)</span></span><br><span class="line">    <span class="type">Mat</span> <span class="variable">mat</span> <span class="operator">=</span> Imgcodecs.imread(src);</span><br><span class="line">    <span class="comment">// 灰度化，转为灰度图</span></span><br><span class="line">    <span class="meta">@Cleanup(value = &quot;release&quot;)</span></span><br><span class="line">    <span class="type">Mat</span> <span class="variable">gray</span> <span class="operator">=</span> OpencvUtil.gray(mat.clone());</span><br><span class="line">    <span class="comment">// 获取图片平均亮度值</span></span><br><span class="line">    <span class="type">Scalar</span> <span class="variable">mean</span> <span class="operator">=</span> Core.mean(gray);</span><br><span class="line">    <span class="type">double</span> <span class="variable">brightness</span> <span class="operator">=</span> mean.val[<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="meta">@Cleanup(value = &quot;release&quot;)</span></span><br><span class="line">    <span class="type">Mat</span> <span class="variable">adjustedImage</span> <span class="operator">=</span> mat.clone();</span><br><span class="line">    <span class="keyword">if</span> (brightness &lt; <span class="number">100</span> || brightness &gt; <span class="number">250</span>) &#123;</span><br><span class="line">        <span class="comment">// 计算亮度调整值，可以根据具体情况选择参数调整</span></span><br><span class="line">        <span class="type">double</span> <span class="variable">alpha</span> <span class="operator">=</span> <span class="number">175</span> / brightness;</span><br><span class="line">        <span class="comment">// 执行亮度调整</span></span><br><span class="line">        gray.convertTo(adjustedImage, -<span class="number">1</span>, alpha, <span class="number">0</span>);</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="comment">// 保存调整后的图像</span></span><br><span class="line">    Imgcodecs.imwrite(dst, adjustedImage);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h2 id="图片纠偏"><a href="#图片纠偏" class="headerlink" title="图片纠偏"></a>图片纠偏</h2><p>图片纠偏是一个比较难的问题，主要的难点在于如何计算出图片的倾斜角度，最常见的方法就是霍夫变换检测直线了，以下是具体步骤。</p><ol><li>图片边缘检测</li><li>霍夫变换，获取检测的直线</li><li>计算每条直线的倾斜角度</li><li>获取所有直线倾斜角度的平均值或者是众数来作为图片的倾斜角度。（关于平均值和众数，总会在某些情况下十分异常，我认为平均数比较可靠，但需要严格筛选需要内容的直线才行。也可以根据某些算法排除掉异常值。）</li><li>图片灰度化。</li><li>高斯模糊，二值化，膨胀，腐蚀等操作处理图片，去除噪点，以免出现不需要的直线。</li><li>图片边缘检测，通过调整参数，把一些无用的边框去除。</li><li>霍夫变换检测直线，通过调整参数把一些无用的直线去除。</li><li>收集并计算每条直线的倾斜角度。</li><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><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><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 class="comment">/**</span></span><br><span 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> src   图片路径</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@param</span> dst   纠偏图片保存路径</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_">deskew</span><span class="params">(String src, String dst)</span> &#123;</span><br><span class="line">    <span class="meta">@Cleanup(value = &quot;release&quot;)</span></span><br><span class="line">    <span class="type">Mat</span> <span class="variable">mat</span> <span class="operator">=</span> Imgcodecs.imread(src);</span><br><span class="line">    <span class="comment">// 计算图片倾斜角</span></span><br><span class="line">    <span class="type">double</span> <span class="variable">angle</span> <span class="operator">=</span> getDeskewAngle(mat);</span><br><span class="line">    <span class="comment">// 图片旋转</span></span><br><span class="line">    ImageUtil.rotateImage(src, dst, angle);</span><br><span 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> src       图片</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> Integer <span class="title function_">getDeskewAngle</span><span class="params">(Mat src)</span> &#123;</span><br><span class="line">    <span class="comment">// 图片灰度化</span></span><br><span class="line">    <span class="type">Mat</span> <span class="variable">gray</span> <span class="operator">=</span> src.clone();</span><br><span class="line">    Imgproc.cvtColor(src, gray, Imgproc.COLOR_BGR2GRAY);</span><br><span class="line">    </span><br><span class="line">    <span class="type">Mat</span> <span class="variable">kernel</span> <span class="operator">=</span> Imgproc.getStructuringElement(Imgproc.MORPH_RECT, <span class="keyword">new</span> <span class="title class_">Size</span>(<span class="number">5</span>, <span class="number">5</span>));</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 图片膨胀</span></span><br><span class="line">    <span class="type">Mat</span> <span class="variable">erode</span> <span class="operator">=</span> gray.clone();</span><br><span class="line">    Imgproc.erode(gray, erode, kernel);</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 图片腐蚀</span></span><br><span class="line">    <span class="type">Mat</span> <span class="variable">dilate</span> <span class="operator">=</span> erode.clone();</span><br><span class="line">    Imgproc.dilate(erode, dilate, kernel);</span><br><span class="line"><span class="keyword">public</span> <span class="keyword">static</span> Integer <span class="title function_">getDeskewAngle</span><span class="params">(Mat src)</span> &#123;</span><br><span class="line">    <span class="comment">// 图片灰度化</span></span><br><span class="line">    <span class="meta">@Cleanup(value = &quot;release&quot;)</span></span><br><span class="line">    <span class="type">Mat</span> <span class="variable">gray</span> <span class="operator">=</span> src.clone();</span><br><span class="line">    Imgproc.cvtColor(src, gray, Imgproc.COLOR_BGR2GRAY);</span><br><span class="line">    <span class="comment">// 边缘检测</span></span><br><span class="line">    <span class="type">Mat</span> <span class="variable">canny</span> <span class="operator">=</span> dilate.clone();</span><br><span class="line">    Imgproc.Canny(dilate, canny, <span class="number">50</span>, <span class="number">150</span>);</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 霍夫变换得到线条</span></span><br><span class="line">    <span class="meta">@Cleanup(value = &quot;release&quot;)</span></span><br><span class="line">    <span class="type">Mat</span> <span class="variable">kernel</span> <span class="operator">=</span> Imgproc.getStructuringElement(Imgproc.MORPH_RECT, <span class="keyword">new</span> <span class="title class_">Size</span>(<span class="number">5</span>, <span class="number">5</span>));</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 图片膨胀</span></span><br><span class="line">    <span class="meta">@Cleanup(value = &quot;release&quot;)</span></span><br><span class="line">    <span class="type">Mat</span> <span class="variable">erode</span> <span class="operator">=</span> gray.clone();</span><br><span class="line">    Imgproc.erode(gray, erode, kernel);</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 图片腐蚀</span></span><br><span class="line">    <span class="meta">@Cleanup(value = &quot;release&quot;)</span></span><br><span class="line">    <span class="type">Mat</span> <span class="variable">dilate</span> <span class="operator">=</span> erode.clone();</span><br><span class="line">    Imgproc.dilate(erode, dilate, kernel);</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 边缘检测</span></span><br><span class="line">    <span class="meta">@Cleanup(value = &quot;release&quot;)</span></span><br><span class="line">    <span class="type">Mat</span> <span class="variable">canny</span> <span class="operator">=</span> dilate.clone();</span><br><span class="line">    Imgproc.Canny(dilate, canny, <span class="number">50</span>, <span class="number">150</span>);</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 霍夫变换得到线条</span></span><br><span class="line">    <span class="meta">@Cleanup(value = &quot;release&quot;)</span></span><br><span class="line">    <span class="type">Mat</span> <span class="variable">lines</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">Mat</span>();</span><br><span class="line">    <span class="comment">//累加器阈值参数，小于设置值不返回</span></span><br><span class="line">    <span class="type">int</span> <span class="variable">threshold</span> <span class="operator">=</span> <span class="number">90</span>;</span><br><span class="line">    <span class="comment">//最低线段长度，低于设置值则不返回</span></span><br><span class="line">    <span class="type">double</span> <span class="variable">minLineLength</span> <span class="operator">=</span> <span class="number">100</span>;</span><br><span class="line">    <span class="comment">//间距小于该值的线当成同一条线</span></span><br><span class="line">    <span class="type">double</span> <span class="variable">maxLineGap</span> <span class="operator">=</span> <span class="number">10</span>;</span><br><span class="line">    <span class="comment">// 霍夫变换，通过步长为1，角度为PI/180来搜索可能的直线</span></span><br><span class="line">    Imgproc.HoughLinesP(canny, lines, <span class="number">1</span>, Math.PI / <span class="number">180</span>, threshold, minLineLength, maxLineGap);</span><br><span class="line">    <span class="comment">// 计算倾斜角度</span></span><br><span class="line">    List&lt;Integer&gt; angelList = <span class="keyword">new</span> <span class="title class_">ArrayList</span>&lt;&gt;();</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; lines.rows(); i++) &#123;</span><br><span class="line">        <span class="type">double</span>[] line = lines.get(i, <span class="number">0</span>);</span><br><span class="line">        <span class="type">int</span> <span class="variable">k</span> <span class="operator">=</span> calculateAngle(line[<span class="number">0</span>], line[<span class="number">1</span>], line[<span class="number">2</span>], line[<span class="number">3</span>]);</span><br><span class="line">        angelList.add(k);</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">if</span> (angelList.isEmpty()) &#123;</span><br><span class="line">        <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">    &#125;</span><br><span class="line">    gray.release();</span><br><span class="line">    kernel.release();</span><br><span class="line">    erode.release();</span><br><span class="line">    dilate.release();</span><br><span class="line">    canny.release();</span><br><span class="line">    lines.release();</span><br><span class="line">    <span class="comment">// 可能还得需要考虑方差来决定选择平均数还是众数</span></span><br><span class="line">    <span class="keyword">return</span> most(angelList);</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">@param</span> angelList 数组</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> <span class="keyword">static</span> <span class="type">int</span> <span class="title function_">most</span><span class="params">(List&lt;Integer&gt; angelList)</span> &#123;</span><br><span class="line">    <span class="keyword">if</span> (angelList.isEmpty()) &#123;</span><br><span class="line">        <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="type">int</span> <span class="variable">res</span> <span class="operator">=</span> <span class="number">0</span>;</span><br><span class="line">    <span class="type">int</span> <span class="variable">max</span> <span class="operator">=</span> Integer.MIN_VALUE;</span><br><span class="line">    Map&lt;Integer, Integer&gt; map = <span class="keyword">new</span> <span class="title class_">HashMap</span>&lt;&gt;();</span><br><span class="line">    <span class="keyword">for</span> (<span class="type">int</span> i : angelList) &#123;</span><br><span class="line">        map.put(i, map.getOrDefault(i, <span class="number">0</span>) + <span class="number">1</span>);</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">for</span> (Integer i : map.keySet()) &#123;</span><br><span class="line">        <span class="type">int</span> <span class="variable">count</span> <span class="operator">=</span> map.get(i);</span><br><span class="line">        <span class="keyword">if</span> (count &gt; max) &#123;</span><br><span class="line">            max = count;</span><br><span class="line">            res = i;</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">return</span> res;</span><br><span 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> x1    点1的横坐标</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@param</span> x2    点2的横坐标</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@param</span> y1    点1的纵坐标</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@param</span> y2    点2的纵坐标</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> <span class="keyword">static</span> <span class="type">int</span> <span class="title function_">calculateAngle</span><span class="params">(<span class="type">double</span> x1, <span class="type">double</span> y1, <span class="type">double</span> x2, <span class="type">double</span> y2)</span> &#123;</span><br><span class="line">    <span class="type">double</span> <span class="variable">dx</span> <span class="operator">=</span> x2 - x1;</span><br><span class="line">    <span class="type">double</span> <span class="variable">dy</span> <span class="operator">=</span> y2 - y1;</span><br><span class="line">    <span class="keyword">if</span> (Math.abs(dx) &lt; <span class="number">1e-4</span>) &#123;</span><br><span class="line">        <span class="keyword">return</span> <span class="number">90</span>;</span><br><span class="line">    &#125; <span class="keyword">else</span> <span class="keyword">if</span> (Math.abs(dy) &lt; <span class="number">1e-4</span>) &#123;</span><br><span class="line">        <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">    &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">        <span class="type">double</span> <span class="variable">radians</span> <span class="operator">=</span> Math.atan2(dy, dx);</span><br><span class="line">        <span class="type">double</span> <span class="variable">degrees</span> <span class="operator">=</span> Math.toDegrees(radians);</span><br><span class="line">        <span class="keyword">return</span> Convert.toInt(Math.round(degrees));</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>有很多场景，比如说发票，白底黑字文件这种，在OCR识别的过程中，会受到红色印章的影响，所以需要先去除红色印章，在进行OCR识别，这样可以提高OCR的识别率。</p><h3 id="红色印章检测"><a href="#红色印章检测" class="headerlink" title="红色印章检测"></a>红色印章检测</h3><p>我碰到比较多的场景是白底黑字的图片，所以我这里就使用了检测红色像素，痛过红色像素的数量，来检测是否该图片有红色印章。</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br></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> filePath      图片路径</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@return</span>              true表示可能存在红色印章</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> Boolean <span class="title function_">recognizeRed</span><span class="params">(String filePath)</span> &#123;</span><br><span class="line">    <span class="meta">@Cleanup(value = &quot;release&quot;)</span></span><br><span class="line">    <span class="type">Mat</span> <span class="variable">mat</span> <span class="operator">=</span> Imgcodecs.imread(filePath);</span><br><span class="line">    <span class="comment">// 转为HSV空间</span></span><br><span class="line">    <span class="meta">@Cleanup(value = &quot;release&quot;)</span></span><br><span class="line">    <span class="type">Mat</span> <span class="variable">hsv</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">Mat</span>();</span><br><span class="line">    Imgproc.cvtColor(mat, hsv, Imgproc.COLOR_BGR2HSV);</span><br><span class="line">    <span class="type">int</span> <span class="variable">nums</span> <span class="operator">=</span> <span class="number">0</span>;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">for</span> (<span class="type">int</span> <span class="variable">i</span> <span class="operator">=</span> <span class="number">0</span>; i &lt; hsv.rows(); i++) &#123;</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; hsv.cols(); j++) &#123;</span><br><span class="line">            <span class="type">double</span>[] clone = hsv.get(i, j).clone();</span><br><span class="line">            <span class="type">double</span> <span class="variable">h</span> <span class="operator">=</span> clone[<span class="number">0</span>];</span><br><span class="line">            <span class="type">double</span> <span class="variable">s</span> <span class="operator">=</span> clone[<span class="number">1</span>];</span><br><span class="line">            <span class="type">double</span> <span class="variable">v</span> <span class="operator">=</span> clone[<span class="number">2</span>];</span><br><span class="line">            <span class="comment">// 红色的hsv范围判断</span></span><br><span class="line">            <span class="keyword">if</span> ((h &gt; <span class="number">0</span> &amp;&amp; h &lt; <span class="number">10</span>) || (h &gt; <span class="number">156</span> &amp;&amp; h &lt; <span class="number">180</span>)) &#123;</span><br><span class="line">                <span class="keyword">if</span> (s &gt; <span class="number">43</span> &amp;&amp; s &lt; <span class="number">255</span>) &#123;</span><br><span class="line">                    <span class="keyword">if</span> (v &lt; <span class="number">255</span> &amp;&amp; v &gt; <span class="number">46</span>) &#123;</span><br><span class="line">                        nums++;</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><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">return</span> nums &gt; <span class="number">8000</span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>有些图片看着是没有红色像素的，但是其实会存在有，所以先使用一个只有红色印章的图片进行检测，然后选择一个比较合理的值进行判断就可以。</p><p>这种方法不适合除了印章外还有很多红色的图片，那种情况可以考虑使用霍夫圆检测，那种可能会耗费很多时间，所以我没有使用。</p><h3 id="去除红色印章-1"><a href="#去除红色印章-1" class="headerlink" title="去除红色印章"></a>去除红色印章</h3><p>我这里去除红色印章的方法，主要的原理就是使用图片二值化，让印章的变成白色即可，但是会出现一个问题，就是红色比黑色更难二值化，所以需要先把图片的红色通道分离出来，红色通道的红色印章的颜色会变淡很多，从而二值化能去除掉红色印章。</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br></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> filePath  图片路径</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@param</span> dst       去除后图片保存地址</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_">removeRed</span><span class="params">(String filePath, String dst)</span> &#123;</span><br><span class="line">    <span class="meta">@Cleanup(value = &quot;release&quot;)</span></span><br><span class="line">    <span class="type">Mat</span> <span class="variable">mat</span> <span class="operator">=</span> Imgcodecs.imread(filePath);</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 分离红色通道</span></span><br><span class="line">    List&lt;Mat&gt; matList = <span class="keyword">new</span> <span class="title class_">ArrayList</span>&lt;&gt;();</span><br><span class="line">    Core.split(mat, matList);</span><br><span class="line">    <span class="meta">@Cleanup(value = &quot;release&quot;)</span></span><br><span class="line">    <span class="type">Mat</span> <span class="variable">red</span> <span class="operator">=</span> matList.get(<span class="number">2</span>);</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 红色通道二值化</span></span><br><span class="line">    <span class="meta">@Cleanup(value = &quot;release&quot;)</span></span><br><span class="line">    <span class="type">Mat</span> <span class="variable">redThresh</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">Mat</span>();</span><br><span class="line">    Imgproc.threshold(red, redThresh, <span class="number">120</span>, <span class="number">255</span>, Imgproc.THRESH_BINARY);</span><br><span class="line"></span><br><span class="line">    Imgcodecs.imwrite(dst, redThresh);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>这种方法的结果图片，因为去除了蓝绿通道，所以会变得灰色，不过因为主要的目的是给OCR识别，所以无所谓，如果想不改变图片的话，还是挺麻烦的，可能还得识别出椭圆印章，然后精确的去除。</p><h2 id="切边矫正"><a href="#切边矫正" class="headerlink" title="切边矫正"></a>切边矫正</h2><p>图片的切边矫正还是使用深度学习模型比较准确，使用opencv局限性还是挺大的，简单使用还是可以的。以下的方法借鉴了网上流传比较广的一个关于截取发票的边缘的方法。</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><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></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_">remove</span><span class="params">(String src,String dst)</span> &#123;</span><br><span class="line">    <span class="type">Mat</span> <span class="variable">img</span> <span class="operator">=</span> Imgcodecs.imread(src);</span><br><span class="line">    <span class="keyword">if</span>(img.empty())&#123;</span><br><span class="line">        <span class="keyword">return</span>;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="type">Mat</span> <span class="variable">greyImg</span> <span class="operator">=</span> img.clone();</span><br><span class="line">    <span class="comment">//1.彩色转灰色</span></span><br><span class="line">    Imgproc.cvtColor(img, greyImg, Imgproc.COLOR_BGR2GRAY);</span><br><span class="line">    OpencvUtil.saveImage(greyImg, <span class="string">&quot;C:\\Users\\11419\\Desktop\\test\\1.jpg&quot;</span>);</span><br><span class="line"></span><br><span class="line">    <span class="type">Mat</span> <span class="variable">gaussianBlurImg</span> <span class="operator">=</span> greyImg.clone();</span><br><span class="line">    <span class="comment">// 2.高斯滤波，降噪</span></span><br><span class="line">    Imgproc.GaussianBlur(greyImg, gaussianBlurImg, <span class="keyword">new</span> <span class="title class_">Size</span>(<span class="number">3</span>,<span class="number">3</span>),<span class="number">0</span>);</span><br><span class="line">    OpencvUtil.saveImage(greyImg, <span class="string">&quot;C:\\Users\\11419\\Desktop\\test\\2.jpg&quot;</span>);</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 3.Canny边缘检测</span></span><br><span class="line">    <span class="type">Mat</span> <span class="variable">cannyImg</span> <span class="operator">=</span> gaussianBlurImg.clone();</span><br><span class="line">    Imgproc.Canny(gaussianBlurImg, cannyImg, <span class="number">50</span>, <span class="number">200</span>);</span><br><span class="line">    OpencvUtil.saveImage(cannyImg, <span class="string">&quot;C:\\Users\\11419\\Desktop\\test\\3.jpg&quot;</span>);</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 4.膨胀，连接边缘</span></span><br><span class="line">    <span class="type">Mat</span> <span class="variable">dilateImg</span> <span class="operator">=</span> cannyImg.clone();</span><br><span class="line">    Imgproc.dilate(cannyImg, dilateImg, <span class="keyword">new</span> <span class="title class_">Mat</span>(), <span class="keyword">new</span> <span class="title class_">Point</span>(-<span class="number">1</span>, -<span class="number">1</span>), <span class="number">3</span>, <span class="number">1</span>, <span class="keyword">new</span> <span class="title class_">Scalar</span>(<span class="number">1</span>));</span><br><span class="line">    OpencvUtil.saveImage(dilateImg, <span class="string">&quot;C:\\Users\\11419\\Desktop\\test\\4.jpg&quot;</span>);</span><br><span class="line"></span><br><span class="line">    <span class="comment">//5.对边缘检测的结果图再进行轮廓提取</span></span><br><span class="line">    List&lt;MatOfPoint&gt; contours = <span class="keyword">new</span> <span class="title class_">ArrayList</span>&lt;&gt;();</span><br><span class="line">    List&lt;MatOfPoint&gt; drawContours = <span class="keyword">new</span> <span class="title class_">ArrayList</span>&lt;&gt;();</span><br><span class="line">    <span class="type">Mat</span> <span class="variable">hierarchy</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">Mat</span>();</span><br><span class="line">    Imgproc.findContours(dilateImg, contours, hierarchy, Imgproc.RETR_EXTERNAL, Imgproc.CHAIN_APPROX_SIMPLE);</span><br><span class="line">    <span class="type">Mat</span> <span class="variable">linePic</span> <span class="operator">=</span> Mat.zeros(dilateImg.rows(), dilateImg.cols(), CvType.CV_8UC3);</span><br><span class="line">    <span class="comment">//6.找出轮廓对应凸包的四边形拟合</span></span><br><span class="line">    List&lt;MatOfPoint&gt; squares = <span class="keyword">new</span> <span class="title class_">ArrayList</span>&lt;&gt;();</span><br><span class="line">    List&lt;MatOfPoint&gt; hulls = <span class="keyword">new</span> <span class="title class_">ArrayList</span>&lt;&gt;();</span><br><span class="line">    <span class="type">MatOfInt</span> <span class="variable">hull</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">MatOfInt</span>();</span><br><span class="line">    <span class="type">MatOfPoint2f</span> <span class="variable">approx</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">MatOfPoint2f</span>();</span><br><span class="line">    approx.convertTo(approx, CvType.CV_32F);</span><br><span class="line"></span><br><span class="line">    <span class="keyword">for</span> (MatOfPoint contour : contours) &#123;</span><br><span class="line">        <span class="comment">// 边框的凸包</span></span><br><span class="line">        Imgproc.convexHull(contour, hull);</span><br><span class="line">        <span class="comment">// 用凸包计算出新的轮廓点</span></span><br><span class="line">        Point[] contourPoints = contour.toArray();</span><br><span class="line">        <span class="type">int</span>[] indices = hull.toArray();</span><br><span class="line">        List&lt;Point&gt; newPoints = <span class="keyword">new</span> <span class="title class_">ArrayList</span>&lt;&gt;();</span><br><span class="line">        <span class="keyword">for</span> (<span class="type">int</span> index : indices) &#123;</span><br><span class="line">            newPoints.add(contourPoints[index]);</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="type">MatOfPoint2f</span> <span class="variable">contourHull</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">MatOfPoint2f</span>();</span><br><span class="line">        contourHull.fromList(newPoints);</span><br><span class="line">        <span class="comment">// 多边形拟合凸包边框(此时的拟合的精度较低)</span></span><br><span class="line">        Imgproc.approxPolyDP(contourHull, approx, Imgproc.arcLength(contourHull, <span class="literal">true</span>) * <span class="number">0.02</span>, <span class="literal">true</span>);</span><br><span class="line">        <span class="type">MatOfPoint</span> <span class="variable">mat</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">MatOfPoint</span>();</span><br><span class="line">        mat.fromArray(approx.toArray());</span><br><span class="line">        drawContours.add(mat);</span><br><span class="line">        <span class="comment">// 筛选出面积大于某一阈值的，且四边形的各个角度都接近直角的凸四边形</span></span><br><span class="line">        <span class="type">MatOfPoint</span> <span class="variable">approxf</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">MatOfPoint</span>();</span><br><span class="line">        approx.convertTo(approxf, CvType.CV_32S);</span><br><span class="line">        <span class="keyword">if</span> (approx.rows() == <span class="number">4</span> &amp;&amp; Math.abs(Imgproc.contourArea(approx)) &gt; <span class="number">40000</span> &amp;&amp;</span><br><span class="line">                Imgproc.isContourConvex(approxf)) &#123;</span><br><span class="line">            <span class="type">double</span> <span class="variable">maxCosine</span> <span class="operator">=</span> <span class="number">0</span>;</span><br><span class="line">            <span class="keyword">for</span> (<span class="type">int</span> <span class="variable">j</span> <span class="operator">=</span> <span class="number">2</span>; j &lt; <span class="number">5</span>; j++) &#123;</span><br><span class="line">                <span class="type">double</span> <span class="variable">cosine</span> <span class="operator">=</span> Math.abs(getAngle(approxf.toArray()[j % <span class="number">4</span>], approxf.toArray()[j - <span class="number">2</span>], approxf.toArray()[j - <span class="number">1</span>]));</span><br><span class="line">                maxCosine = Math.max(maxCosine, cosine);</span><br><span class="line">            &#125;</span><br><span class="line">            <span class="comment">// 角度大概72度</span></span><br><span class="line">            <span class="keyword">if</span> (maxCosine &lt; <span class="number">0.3</span>) &#123;</span><br><span class="line">                <span class="type">MatOfPoint</span> <span class="variable">tmp</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">MatOfPoint</span>();</span><br><span class="line">                contourHull.convertTo(tmp, CvType.CV_32S);</span><br><span class="line">                squares.add(approxf);</span><br><span class="line">                hulls.add(tmp);</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><span class="line">    <span class="type">Random</span> <span class="variable">r</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">Random</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; drawContours.size(); i++) &#123;</span><br><span class="line">        Imgproc.drawContours(linePic, drawContours, i, <span class="keyword">new</span> <span class="title class_">Scalar</span>(r.nextInt(<span class="number">255</span>),r.nextInt(<span class="number">255</span>), r.nextInt(<span class="number">255</span>)));</span><br><span class="line">    &#125;</span><br><span class="line">    OpencvUtil.saveImage(linePic, <span class="string">&quot;C:\\Users\\11419\\Desktop\\test\\5.jpg&quot;</span>);</span><br><span class="line">    <span class="comment">//7.找出最大的矩形</span></span><br><span class="line">    <span class="type">int</span> <span class="variable">index</span> <span class="operator">=</span> findLargestSquare(squares);</span><br><span class="line">    MatOfPoint largest_square;</span><br><span class="line">    <span class="keyword">if</span>(!squares.isEmpty())&#123;</span><br><span class="line">        largest_square = squares.get(index);</span><br><span class="line">    &#125;<span class="keyword">else</span>&#123;</span><br><span class="line">        System.out.println(<span class="string">&quot;图片无法识别&quot;</span>);</span><br><span class="line">        <span class="keyword">return</span>;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="type">Mat</span> <span class="variable">polyPic</span> <span class="operator">=</span> Mat.zeros(img.size(), CvType.CV_8UC3);</span><br><span class="line">    Imgproc.drawContours(polyPic, squares, index, <span class="keyword">new</span> <span class="title class_">Scalar</span>(<span class="number">0</span>, <span class="number">0</span>,<span class="number">255</span>), <span class="number">2</span>);</span><br><span class="line">    OpencvUtil.saveImage(polyPic, <span class="string">&quot;C:\\Users\\11419\\Desktop\\test\\6.jpg&quot;</span>);</span><br><span class="line">    <span class="comment">//存储矩形的四个凸点</span></span><br><span class="line">    hull = <span class="keyword">new</span> <span class="title class_">MatOfInt</span>();</span><br><span class="line">    Imgproc.convexHull(largest_square, hull, <span class="literal">false</span>);</span><br><span class="line">    List&lt;Integer&gt; hullList =  hull.toList();</span><br><span class="line">    List&lt;Point&gt; polyContoursList = largest_square.toList();</span><br><span class="line">    List&lt;Point&gt; hullPointList = <span class="keyword">new</span> <span class="title class_">ArrayList</span>&lt;&gt;();</span><br><span class="line">    <span class="keyword">for</span> (Integer integer : hullList) &#123;</span><br><span class="line">        Imgproc.circle(polyPic, polyContoursList.get(integer), <span class="number">10</span>, <span class="keyword">new</span> <span class="title class_">Scalar</span>(r.nextInt(<span class="number">255</span>), r.nextInt(<span class="number">255</span>), r.nextInt(<span class="number">255</span>), <span class="number">3</span>));</span><br><span class="line">        hullPointList.add(polyContoursList.get(integer));</span><br><span class="line">    &#125;</span><br><span class="line">    Core.addWeighted(polyPic, <span class="number">1</span>, img, <span class="number">1</span>, <span class="number">0</span>, img);</span><br><span class="line">    OpencvUtil.saveImage(img, <span class="string">&quot;C:\\Users\\11419\\Desktop\\test\\7.jpg&quot;</span>);</span><br><span class="line">    List&lt;Point&gt; lastHullPointList = <span class="keyword">new</span> <span class="title class_">ArrayList</span>&lt;&gt;(hullPointList);</span><br><span class="line">    <span class="comment">//dstPoints储存的是变换后各点的坐标，依次为左上，右上，右下， 左下</span></span><br><span class="line">    <span class="comment">//srcPoints储存的是上面得到的四个角的坐标</span></span><br><span class="line">    Point[] dstPoints = &#123;<span class="keyword">new</span> <span class="title class_">Point</span>(<span class="number">0</span>,<span class="number">0</span>), <span class="keyword">new</span> <span class="title class_">Point</span>(img.cols(),<span class="number">0</span>), <span class="keyword">new</span> <span class="title class_">Point</span>(img.cols(),img.rows()), <span class="keyword">new</span> <span class="title class_">Point</span>(<span class="number">0</span>,img.rows())&#125;;</span><br><span class="line">    Point[] srcPoints = <span class="keyword">new</span> <span class="title class_">Point</span>[<span class="number">4</span>];</span><br><span class="line">    <span class="type">boolean</span> <span class="variable">sorted</span> <span class="operator">=</span> <span class="literal">false</span>;</span><br><span class="line">    <span class="type">int</span> <span class="variable">n</span> <span class="operator">=</span> <span class="number">4</span>;</span><br><span class="line">    <span class="comment">//对四个点进行排序 分出左上 右上 右下 左下</span></span><br><span class="line">    <span class="keyword">while</span> (!sorted &amp;&amp; n &gt; <span class="number">0</span>)&#123;</span><br><span class="line">        <span class="keyword">for</span> (<span class="type">int</span> <span class="variable">i</span> <span class="operator">=</span> <span class="number">1</span>; i &lt; n; i++)&#123;</span><br><span class="line">            sorted = <span class="literal">true</span>;</span><br><span class="line">            <span class="keyword">if</span> (lastHullPointList.get(i - <span class="number">1</span>).x &gt; lastHullPointList.get(i).x)&#123;</span><br><span class="line">                <span class="type">Point</span> <span class="variable">temp1</span> <span class="operator">=</span> lastHullPointList.get(i);</span><br><span class="line">                <span class="type">Point</span> <span class="variable">temp2</span> <span class="operator">=</span> lastHullPointList.get(i-<span class="number">1</span>);</span><br><span class="line">                lastHullPointList.set(i, temp2);</span><br><span class="line">                lastHullPointList.set(i - <span class="number">1</span>, temp1);</span><br><span class="line">                sorted = <span class="literal">false</span>;</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;</span><br><span class="line">        n--;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="comment">//即先对四个点的x坐标进行冒泡排序分出左右，再根据两对坐标的y值比较分出上下</span></span><br><span class="line">    <span class="keyword">if</span> (lastHullPointList.get(<span class="number">0</span>).y &lt; lastHullPointList.get(<span class="number">1</span>).y)&#123;</span><br><span class="line">        srcPoints[<span class="number">0</span>] = lastHullPointList.get(<span class="number">0</span>);</span><br><span class="line">        srcPoints[<span class="number">3</span>] = lastHullPointList.get(<span class="number">1</span>);</span><br><span class="line">    &#125;<span class="keyword">else</span>&#123;</span><br><span class="line">        srcPoints[<span class="number">0</span>] = lastHullPointList.get(<span class="number">1</span>);</span><br><span class="line">        srcPoints[<span class="number">3</span>] = lastHullPointList.get(<span class="number">0</span>);</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">if</span> (lastHullPointList.get(<span class="number">2</span>).y &lt; lastHullPointList.get(<span class="number">3</span>).y)&#123;</span><br><span class="line">        srcPoints[<span class="number">1</span>] = lastHullPointList.get(<span class="number">2</span>);</span><br><span class="line">        srcPoints[<span class="number">2</span>] = lastHullPointList.get(<span class="number">3</span>);</span><br><span class="line">    &#125;<span class="keyword">else</span>&#123;</span><br><span class="line">        srcPoints[<span class="number">1</span>] = lastHullPointList.get(<span class="number">3</span>);</span><br><span class="line">        srcPoints[<span class="number">2</span>] = lastHullPointList.get(<span class="number">2</span>);</span><br><span class="line">    &#125;</span><br><span class="line">    List&lt;Point&gt; listSrcs = java.util.Arrays.asList(srcPoints[<span class="number">0</span>], srcPoints[<span class="number">1</span>], srcPoints[<span class="number">2</span>], srcPoints[<span class="number">3</span>]);</span><br><span class="line">    <span class="type">Mat</span> <span class="variable">srcPointsMat</span> <span class="operator">=</span> Converters.vector_Point_to_Mat(listSrcs, CvType.CV_32F);</span><br><span class="line"></span><br><span class="line">    List&lt;Point&gt; dstSrcs = java.util.Arrays.asList(dstPoints[<span class="number">0</span>], dstPoints[<span class="number">1</span>], dstPoints[<span class="number">2</span>], dstPoints[<span class="number">3</span>]);</span><br><span class="line">    <span class="type">Mat</span> <span class="variable">dstPointsMat</span> <span class="operator">=</span> Converters.vector_Point_to_Mat(dstSrcs, CvType.CV_32F);</span><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">Mat</span> <span class="variable">transMat</span> <span class="operator">=</span> Imgproc.getPerspectiveTransform(srcPointsMat, dstPointsMat);    <span class="comment">//得到变换矩阵</span></span><br><span class="line">    <span class="type">Mat</span> <span class="variable">outPic</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">Mat</span>();</span><br><span class="line">    Imgproc.warpPerspective(img, outPic, transMat, img.size());</span><br><span class="line">    OpencvUtil.saveImage(outPic, dst);</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">// 根据三个点计算中间那个点的夹角   pt1 pt0 pt2</span></span><br><span class="line"><span class="keyword">private</span> <span class="keyword">static</span> <span class="type">double</span> <span class="title function_">getAngle</span><span class="params">(Point pt1, Point pt2, Point pt0)</span> &#123;</span><br><span class="line">    <span class="type">double</span> <span class="variable">dx1</span> <span class="operator">=</span> pt1.x - pt0.x;</span><br><span class="line">    <span class="type">double</span> <span class="variable">dy1</span> <span class="operator">=</span> pt1.y - pt0.y;</span><br><span class="line">    <span class="type">double</span> <span class="variable">dx2</span> <span class="operator">=</span> pt2.x - pt0.x;</span><br><span class="line">    <span class="type">double</span> <span class="variable">dy2</span> <span class="operator">=</span> pt2.y - pt0.y;</span><br><span class="line">    <span class="keyword">return</span> (dx1*dx2 + dy1*dy2)/Math.sqrt((dx1*dx1 + dy1*dy1)*(dx2*dx2 + dy2*dy2) + <span class="number">1e-10</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">private</span> <span class="keyword">static</span> <span class="type">int</span> <span class="title function_">findLargestSquare</span><span class="params">(List&lt;MatOfPoint&gt; squares)</span> &#123;</span><br><span class="line">    <span class="keyword">if</span> (squares.isEmpty()) &#123;</span><br><span class="line">        <span class="keyword">return</span> -<span class="number">1</span>;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="type">int</span> <span class="variable">maxWidth</span> <span class="operator">=</span> <span class="number">0</span>;</span><br><span class="line">    <span class="type">int</span> <span class="variable">maxHeight</span> <span class="operator">=</span> <span class="number">0</span>;</span><br><span class="line">    <span class="type">int</span> <span class="variable">maxSquareIdx</span> <span class="operator">=</span> <span class="number">0</span>;</span><br><span class="line">    <span class="type">int</span> <span class="variable">currentIndex</span> <span class="operator">=</span> <span class="number">0</span>;</span><br><span class="line">    <span class="keyword">for</span> (MatOfPoint square : squares) &#123;</span><br><span class="line">        <span class="type">Rect</span> <span class="variable">rectangle</span> <span class="operator">=</span> Imgproc.boundingRect(square);</span><br><span class="line">        <span class="keyword">if</span> (rectangle.width &gt;= maxWidth &amp;&amp; rectangle.height &gt;= maxWidth) &#123;</span><br><span class="line">            maxWidth = rectangle.width;</span><br><span class="line">            maxHeight = rectangle.height;</span><br><span class="line">            maxSquareIdx = currentIndex;</span><br><span class="line">        &#125;</span><br><span class="line">        currentIndex++;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">return</span> maxSquareIdx;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>]]></content:encoded>
            
            
            <category domain="https://yww52.com/categories/%E6%88%91%E7%9A%84%E6%8A%80%E6%9C%AF%E5%AD%A6%E4%B9%A0%E7%89%A9%E8%AF%AD%E6%9E%9C%E7%84%B6%E6%9C%89%E9%97%AE%E9%A2%98/">我的技术学习物语果然有问题</category>
            
            <category domain="https://yww52.com/categories/%E6%88%91%E7%9A%84%E6%8A%80%E6%9C%AF%E5%AD%A6%E4%B9%A0%E7%89%A9%E8%AF%AD%E6%9E%9C%E7%84%B6%E6%9C%89%E9%97%AE%E9%A2%98/%E9%A1%B9%E7%9B%AE%E5%BC%80%E5%8F%91/">项目开发</category>
            
            
            
            <comments>https://yww52.com/posts/91f4854c/#disqus_thread</comments>
            
        </item>
        
        <item>
            <title>服务器软件</title>
            <link>https://yww52.com/posts/58d09dd7/</link>
            <guid>https://yww52.com/posts/58d09dd7/</guid>
            <pubDate>Mon, 10 Apr 2023 16:00:00 GMT</pubDate>
            
            <description>一些服务器软件的安装</description>
            
            
            
            <content:encoded><![CDATA[<img src="https://img.yww52.com/2023/4/2023-4-11top_img.jpg" alt="服务器软件" style="max-width:100%;height:auto;margin-bottom:15px;"
  /><h1 id="Java"><a href="#Java" class="headerlink" title="Java"></a>Java</h1><h2 id="卸载系统自带的JDK"><a href="#卸载系统自带的JDK" class="headerlink" title="卸载系统自带的JDK"></a>卸载系统自带的JDK</h2><h3 id="查看系统安装的JDK"><a href="#查看系统安装的JDK" class="headerlink" title="查看系统安装的JDK"></a>查看系统安装的JDK</h3><p>使用<code>rpm -qa | grep jdk</code>命令查看当前系统安装的JDK。</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line">[root@192 ~]# rpm -qa | grep jdk</span><br><span class="line">java-1.8.0-openjdk-headless-1.8.0.362.b08-1.el7_9.x86_64</span><br><span class="line">java-1.7.0-openjdk-1.7.0.261-2.6.22.2.el7_8.x86_64</span><br><span class="line">java-1.7.0-openjdk-headless-1.7.0.261-2.6.22.2.el7_8.x86_64</span><br><span class="line">copy-jdk-configs-3.3-11.el7_9.noarch</span><br><span class="line">java-1.8.0-openjdk-1.8.0.362.b08-1.el7_9.x86_64</span><br></pre></td></tr></table></figure><h3 id="卸载安装的JDK"><a href="#卸载安装的JDK" class="headerlink" title="卸载安装的JDK"></a>卸载安装的JDK</h3><p>使用<code>rpm -e --nodeps</code>的命令去卸载上述查询到的JDK。</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></pre></td><td class="code"><pre><span class="line">[root@192 ~]# rpm -e --nodeps java-1.8.0-openjdk-headless-1.8.0.362.b08-1.el7_9.x86_64</span><br><span class="line">[root@192 ~]# rpm -e --nodeps java-1.7.0-openjdk-1.7.0.261-2.6.22.2.el7_8.x86_64</span><br><span class="line">[root@192 ~]# rpm -e --nodeps java-1.7.0-openjdk-headless-1.7.0.261-2.6.22.2.el7_8.x86_64</span><br><span class="line">[root@192 ~]# rpm -e --nodeps copy-jdk-configs-3.3-11.el7_9.noarch</span><br><span class="line">[root@192 ~]# rpm -e --nodeps java-1.8.0-openjdk-1.8.0.362.b08-1.el7_9.x86_64</span><br><span class="line">[root@192 ~]# rpm -qa | grep jdk</span><br><span class="line">[root@192 ~]# java -version</span><br><span class="line">bash: java: 未找到命令...</span><br></pre></td></tr></table></figure><h2 id="使用yum方式安装Java"><a href="#使用yum方式安装Java" class="headerlink" title="使用yum方式安装Java"></a>使用yum方式安装Java</h2><h3 id="查看可以安装的版本"><a href="#查看可以安装的版本" class="headerlink" title="查看可以安装的版本"></a>查看可以安装的版本</h3><p>使用命令<code>yum -y list java*</code>查询可以安装的Java版本。</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line">[root@192 ~]# yum -y list java*</span><br><span class="line">已加载插件：fastestmirror, langpacks</span><br><span class="line">Loading mirror speeds from cached hostfile</span><br><span class="line"> * base: mirrors.aliyun.com</span><br><span class="line"> * extras: mirrors.aliyun.com</span><br><span class="line"> * updates: mirrors.aliyun.com</span><br><span class="line">已安装的软件包</span><br><span class="line">javapackages-tools.noarch  3.4.1-11.el7  @anaconda可安装的软件包</span><br><span class="line">.......</span><br></pre></td></tr></table></figure><h3 id="安装JDK"><a href="#安装JDK" class="headerlink" title="安装JDK"></a>安装JDK</h3><p>这里选择安装1.8的版本。</p><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">yum -y install java-1.8.0-openjdk*</span><br></pre></td></tr></table></figure><h3 id="查看JDK安装的目录"><a href="#查看JDK安装的目录" class="headerlink" title="查看JDK安装的目录"></a>查看JDK安装的目录</h3><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">[root@192 /]# <span class="built_in">cd</span> /usr/lib/jvm/java-1.8.0-openjdk</span><br><span class="line">[root@192 java-1.8.0-openjdk]# <span class="built_in">ls</span> </span><br><span class="line">ASSEMBLY_EXCEPTION  bin  demo  include  jre  lib  LICENSE  sample  src.zip  tapset  THIRD_PARTY_README</span><br></pre></td></tr></table></figure><h3 id="卸载yum下载的JDK"><a href="#卸载yum下载的JDK" class="headerlink" title="卸载yum下载的JDK"></a>卸载yum下载的JDK</h3><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">yum -y remove java-1.8.0-openjdk*</span><br></pre></td></tr></table></figure><h2 id="手动安装JDK"><a href="#手动安装JDK" class="headerlink" title="手动安装JDK"></a>手动安装JDK</h2><h3 id="下载安装包"><a href="#下载安装包" class="headerlink" title="下载安装包"></a>下载安装包</h3><p>安装包的下载可以去oracle的官网去下载，这里选择下载<code>Java8</code>的版本。</p><p><a href="https://www.oracle.com/java/technologies/downloads/#java8">下载地址</a></p><p>根据自己的系统，选择对应的版本即可，这里选择<code>x64 Compressed Archive</code>的安装包。</p><p>将<code>jdk-8u361-linux-x64.tar.gz</code>安装包上传到服务器。</p><h3 id="解压安装包"><a href="#解压安装包" class="headerlink" title="解压安装包"></a>解压安装包</h3><p>这里将Java放在了<code>/usr/local/java</code>的文件夹中，把安装包解压到该文件夹即可。</p><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">tar -zxvf jdk-8u361-linux-x64.tar.gz -C ./</span><br></pre></td></tr></table></figure><h3 id="配置环境变量"><a href="#配置环境变量" class="headerlink" title="配置环境变量"></a>配置环境变量</h3><p>在<code>/etc/profile</code>文件尾部，加入以下的JDK的环境配置。</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">JAVA_HOME=/usr/local/java/jdk1.8.0_361</span><br><span class="line">CLASSPATH=$JAVA_HOME/lib/</span><br><span class="line">PATH=$PATH:$JAVA_HOME/bin</span><br><span class="line">export PATH JAVA_HOME CLASSPATH</span><br></pre></td></tr></table></figure><p>执行以下命令，使环境变量生效。</p><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"><span class="built_in">source</span> /etc/profile</span><br></pre></td></tr></table></figure><p>这条命令，在重启之后会失效，需要再次执行，不想再次执行，需要在<code>/etc/bashrc</code>尾部加入该条命令。</p><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"><span class="built_in">source</span> /etc/profile</span><br></pre></td></tr></table></figure><h3 id="卸载JDK"><a href="#卸载JDK" class="headerlink" title="卸载JDK"></a>卸载JDK</h3><p>想卸载的话，就将上述的压缩包的文件和环境变量删掉即可。</p><h1 id="Redis"><a href="#Redis" class="headerlink" title="Redis"></a>Redis</h1><h2 id="源码编译"><a href="#源码编译" class="headerlink" title="源码编译"></a>源码编译</h2><h3 id="编译安装"><a href="#编译安装" class="headerlink" title="编译安装"></a>编译安装</h3><ol><li><p>下载Redis</p><p>Redis的安装包直接去官网下载即可。</p><p><a href="https://download.redis.io/releases/?_gl=1*1oe52s2*_gcl_au*MTAyNjg4NTk2Ny4xNzQ0MTY3NDU0">下载地址</a></p></li><li><p>解压安装包</p><p>这里放到<code>/usr/local/redis</code>下，并执行下面命令进行解压。</p><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">tar -zxvf redis-7.0.10.tar.gz </span><br></pre></td></tr></table></figure></li><li><p>编译安装</p><p>进入redis源码文件夹，执行以下命令，编译redis并进行安装。</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></pre></td><td class="code"><pre><span class="line"><span class="comment"># 编译安装</span></span><br><span class="line">make</span><br><span class="line">make install</span><br><span class="line"><span class="comment"># 官方建议使用test测试</span></span><br><span class="line">make <span class="built_in">test</span></span><br></pre></td></tr></table></figure></li><li><p>查看是否安装成功，在系统命令中出现redis的命令表示安装成功了。</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></pre></td><td class="code"><pre><span class="line">[root@192 redis-7.0.10]# <span class="built_in">cd</span> /usr/local/bin/</span><br><span class="line">[root@192 bin]# <span class="built_in">ls</span> </span><br><span class="line">redis-benchmark  redis-check-aof  redis-check-rdb  redis-cli  redis-sentinel  redis-server</span><br></pre></td></tr></table></figure></li><li><p>将Redis安装到系统服务</p><p>使用Redis的脚本工具，将Redis安装到系统服务上。</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line">[root@192 redis-7.0.10]# cd utils/</span><br><span class="line">[root@192 utils]# ./install_server.sh</span><br><span class="line">Welcome to the redis service installer</span><br><span class="line">This script will help you easily set up a running redis server</span><br><span class="line"></span><br><span class="line">This systems seems to use systemd.</span><br><span class="line">Please take a look at the provided example service unit files in this directory, and adapt and install them. Sorry!</span><br></pre></td></tr></table></figure><p>发现有问题，得换种方法。</p><p>将<code>utils</code>目录下的<code>systemd-redis_server.service</code>文件拷贝到系统配置目录下，并改名为<code>redis.server</code></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></pre></td><td class="code"><pre><span class="line"><span class="built_in">cp</span> systemd-redis_server.service /usr/lib/systemd/system</span><br><span class="line"><span class="built_in">cd</span> /usr/lib/systemd/system</span><br><span class="line"><span class="built_in">mv</span> systemd-redis_server.service  redis.service</span><br></pre></td></tr></table></figure><p>编辑<code>redis.server</code>.</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># 原来为ExecStart=/usr/local/bin/redis-server --supervised systemd --daemonize no</span></span><br><span class="line">[Service]</span><br><span class="line">ExecStart=/usr/local/bin/redis-server /usr/local/redis/redis-7.0.10/redis.conf --supervised systemd</span><br><span class="line"></span><br><span class="line"><span class="comment"># 不修改启动可能会超时</span></span><br><span class="line"><span class="comment">#Type=notify</span></span><br><span class="line">PrivateTmp=<span class="built_in">yes</span></span><br><span class="line">TimeoutStartSec=15</span><br><span class="line">TimeoutStopSec=15</span><br></pre></td></tr></table></figure></li><li><p>编辑redis的配置文件</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></pre></td><td class="code"><pre><span class="line"><span class="comment">################################# GENERAL #####################################</span></span><br><span class="line"><span class="comment"># 配置日志文件位置</span></span><br><span class="line">logfile /var/log/redis/redis.log</span><br><span class="line"></span><br><span class="line"><span class="comment">################################ SNAPSHOTTING  ################################</span></span><br><span class="line"><span class="comment"># 数据目录</span></span><br><span class="line"><span class="built_in">dir</span> /var/redis/6379</span><br></pre></td></tr></table></figure></li><li><p>基础命令</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># 重新加载服务文件</span></span><br><span class="line">systemctl daemon-reload</span><br><span class="line"><span class="comment"># 设置自启</span></span><br><span class="line">systemctl <span class="built_in">enable</span> redis</span><br><span class="line"><span class="comment"># 启动</span></span><br><span class="line">systemctl start redis</span><br><span class="line"><span class="comment"># 停止</span></span><br><span class="line">systemctl stop redis</span><br><span class="line"><span class="comment"># 重启</span></span><br><span class="line">systemctl restart redis</span><br><span class="line"><span class="comment"># 查看状态</span></span><br><span class="line">systemctl status redis</span><br></pre></td></tr></table></figure></li></ol><h3 id="配置文件修改"><a href="#配置文件修改" class="headerlink" title="配置文件修改"></a>配置文件修改</h3><p>redis有些配置需要更改，不然使用起来会出现问题。配置文件就在源码文件夹中的<code>redis.conf</code></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><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><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"># 需设置为 0.0.0.0 以允许所有 IP 访问，默认只能本地127.0.0.1本地IP访问</span><br><span class="line">bind 0.0.0.0</span><br><span class="line"># 关闭protected-mode模式，此时外部网络可以直接访问</span><br><span class="line">protected-mode no</span><br><span class="line"># 设置密码，不设置就需要自己注意</span><br><span class="line">requirepass 123456</span><br><span class="line"># 是否以守护进程模式运行（后台运行），默认为no</span><br><span class="line">daemonize yes</span><br><span class="line"># 日志文件存放</span><br><span class="line">logfile &quot;/data/redis/log/redis.log&quot;</span><br><span class="line"># 持久化存放文件的路径</span><br><span class="line">dir /data/redis/data</span><br><span class="line"># 启用 AOF 持久化</span><br><span class="line">appendonly yes</span><br><span class="line"># 指定 AOF 文件名</span><br><span class="line">appendfilename &quot;redis_data.aof&quot;</span><br></pre></td></tr></table></figure><h2 id="docker"><a href="#docker" class="headerlink" title="docker"></a>docker</h2><p>redis容器有个比较坑的地方，就是默认是找不到配置文件的，得自己参考配置文件，然后增加配置，映射到容器内部，覆盖掉默认的配置。</p><ol><li><p>拉取容器</p><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">docker pull redis:7-alpine</span><br></pre></td></tr></table></figure></li><li><p>创建<code>redis.conf</code>配置文件</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><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><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><br><span class="line">dir /data/redis</span><br><span class="line">bind 0.0.0.0</span><br><span class="line">port 6379</span><br><span class="line"></span><br><span class="line"># 日志文件存放</span><br><span class="line">logfile &quot;/data/log/redis.log&quot;</span><br><span class="line"></span><br><span class="line"># 启用AOF持久化</span><br><span class="line">appendonly yes</span><br><span class="line">appendfilename &quot;appendonly.aof&quot;</span><br><span class="line">appendfsync everysec</span><br><span class="line"></span><br><span class="line"># RDB持久化配置（可选）</span><br><span class="line">save 900 1</span><br><span class="line">save 300 10</span><br><span class="line">save 60 10000</span><br><span class="line"></span><br><span class="line"># 日志级别</span><br><span class="line">loglevel notice</span><br></pre></td></tr></table></figure></li><li><p>启动容器</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line">docker run -d \</span><br><span class="line">  --name redis \</span><br><span class="line">  -p 6379:6379 \</span><br><span class="line">  -v /data/redis/data:/data/redis \</span><br><span class="line">  -v /data/redis/log:/data/log \</span><br><span class="line">  -v ./redis.conf:/usr/local/etc/redis/redis.conf \</span><br><span class="line">  --restart unless-stopped \</span><br><span class="line">  redis:7-alpine \</span><br><span class="line">  redis-server /usr/local/etc/redis/redis.conf --appendonly <span class="built_in">yes</span></span><br></pre></td></tr></table></figure></li></ol><h2 id="docker-compose"><a href="#docker-compose" class="headerlink" title="docker-compose"></a>docker-compose</h2><figure class="highlight dockerfile"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><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">version: <span class="string">&#x27;3.8&#x27;</span></span><br><span class="line"></span><br><span class="line">services:</span><br><span class="line">  redis:</span><br><span class="line">    image: redis:<span class="number">7</span>-alpine</span><br><span class="line">    container_name: chitanda-redis</span><br><span class="line">    ports:</span><br><span class="line">      - <span class="string">&quot;6379:6379&quot;</span></span><br><span class="line">    volumes:</span><br><span class="line">      <span class="comment"># 持久化数据目录映射到宿主机</span></span><br><span class="line">      - /data/redis/data:/data/redis</span><br><span class="line">      <span class="comment"># 日志映射到宿主机</span></span><br><span class="line">      - /data/redis/log:/data/log</span><br><span class="line">      <span class="comment"># Redis配置文件映射到宿主机</span></span><br><span class="line">      - ./redis.conf:/usr/local/etc/redis/redis.conf</span><br><span class="line">    command: redis-server /usr/local/etc/redis/redis.conf --appendonly yes</span><br><span class="line">    restart: unless-stopped</span><br></pre></td></tr></table></figure><p>只部署一个服务的话，还是推荐用docker，有多个容器编排再用docker-compose</p><p>将配置文件和<code>docker-compose.yml</code>放在同一个目录，启动容器</p><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">docker-compose up -d </span><br></pre></td></tr></table></figure><h1 id="Nginx"><a href="#Nginx" class="headerlink" title="Nginx"></a>Nginx</h1><h2 id="APT包管理"><a href="#APT包管理" class="headerlink" title="APT包管理"></a>APT包管理</h2><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># 更新</span></span><br><span class="line">apt update</span><br><span class="line"><span class="comment"># 下载nginx</span></span><br><span class="line">apt install nginx</span><br><span class="line"><span class="comment"># 验证nginx</span></span><br><span class="line">nginx -v</span><br><span class="line"><span class="comment"># nginx配置/etc/nginx </span></span><br></pre></td></tr></table></figure><h2 id="源码编译-1"><a href="#源码编译-1" class="headerlink" title="源码编译"></a>源码编译</h2><h3 id="Nginx依赖下载"><a href="#Nginx依赖下载" class="headerlink" title="Nginx依赖下载"></a>Nginx依赖下载</h3><p>Nginx还需要下载一些依赖库。</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></pre></td><td class="code"><pre><span class="line"><span class="comment"># PCRE https://github.com/PCRE2Project/pcre2/releases，例如pcre2-10.45.tar.gz</span></span><br><span class="line"><span class="comment"># PCRE编译安装</span></span><br><span class="line">tar -zxvf pcre2-10.45.tar.gz</span><br><span class="line"><span class="built_in">cd</span>  pcre2-10.45</span><br><span class="line">./configure</span><br><span class="line">make </span><br><span class="line">make install</span><br></pre></td></tr></table></figure><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># Zlib https://www.zlib.net/，例如zlib-1.3.1.tar.gz</span></span><br><span class="line"><span class="comment"># Zlib编译安装</span></span><br><span class="line">tar -zxvf zlib-1.3.1.tar.gz</span><br><span class="line"><span class="built_in">cd</span>  zlib-1.3.1</span><br><span class="line">./configure</span><br><span class="line">make </span><br><span class="line">make install</span><br></pre></td></tr></table></figure><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># OpenSSL https://openssl-library.org/source/，例如openssl-3.5.1.tar.gz</span></span><br><span class="line"><span class="comment"># OpenSSL编译安装</span></span><br><span class="line">tar -zxvf openssl-3.5.1.tar.gz</span><br><span class="line"><span class="built_in">cd</span>  openssl-3.5.1</span><br><span class="line">./config --prefix=/usr/local/openssl --openssldir=/usr/local/openssl</span><br><span class="line">make </span><br><span class="line">make install</span><br></pre></td></tr></table></figure><blockquote><p>尽量添加sudo执行命令，避免权限出现的问题。</p></blockquote><h3 id="编译安装Nginx"><a href="#编译安装Nginx" class="headerlink" title="编译安装Nginx"></a>编译安装Nginx</h3><p>首先需要先去下载<code>nginx</code>的源码</p><p><a href="https://nginx.org/en/download.html">下载地址</a></p><p>比如这里的nginx-1.28.0.tar.gz。</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># 解压</span></span><br><span class="line">tar -zxvf nginx-1.28.0.tar.gz</span><br><span class="line"><span class="built_in">cd</span>  nginx-1.28.0</span><br><span class="line"><span class="comment"># 配置编译参数，请自行替换刚刚的依赖源码路径</span></span><br><span class="line">./configure \</span><br><span class="line">  --prefix=/etc/nginx \</span><br><span class="line">  --with-pcre=/data/nginx/pcre2-10.45 \</span><br><span class="line">  --with-zlib=/data/nginx/zlib-1.3.1 \</span><br><span class="line">  --with-openssl=/data/openssl-3.5.1 \</span><br><span class="line">  --with-http_ssl_module</span><br><span class="line"><span class="comment"># 编译安装</span></span><br><span class="line">make</span><br><span class="line">make install</span><br></pre></td></tr></table></figure><h3 id="配置-Systemd-服务"><a href="#配置-Systemd-服务" class="headerlink" title="配置 Systemd 服务"></a>配置 Systemd 服务</h3><p>创建服务文件<code>/etc/systemd/system/nginx.service</code></p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br></pre></td><td class="code"><pre><span class="line">[Unit]</span><br><span class="line">Description=The NGINX HTTP and reverse proxy server</span><br><span class="line">After=syslog.target network.target</span><br><span class="line"> </span><br><span class="line">[Service]</span><br><span class="line">Type=forking</span><br><span class="line">PIDFile=/etc/nginx/logs/nginx.pid</span><br><span class="line">ExecStartPre=/etc/nginx/sbin/nginx -t</span><br><span class="line">ExecStart=/etc/nginx/sbin/nginx</span><br><span class="line">ExecReload=/etc/nginx/sbin/nginx -s reload</span><br><span class="line">ExecStop=/bin/kill -s QUIT <span class="variable">$MAINPID</span></span><br><span class="line">PrivateTmp=<span class="literal">true</span></span><br><span class="line"> </span><br><span class="line">[Install]</span><br><span class="line">WantedBy=multi-user.target</span><br></pre></td></tr></table></figure><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="built_in">sudo</span> systemctl <span class="built_in">enable</span> nginx</span><br><span class="line"><span class="built_in">sudo</span> systemctl start nginx</span><br></pre></td></tr></table></figure><h2 id="docker-1"><a href="#docker-1" class="headerlink" title="docker"></a>docker</h2><p>拉取镜像</p><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">docker pull nginx:latest</span><br></pre></td></tr></table></figure><p>配置文件，自用参考</p><p>nginx.conf</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><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><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">user  nginx;</span><br><span class="line">worker_processes  auto;</span><br><span class="line"></span><br><span class="line">error_log  /var/log/nginx/error.log notice;</span><br><span class="line">pid        /var/run/nginx.pid;</span><br><span class="line"></span><br><span class="line"></span><br><span class="line">events &#123;</span><br><span class="line">    worker_connections  1024;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"></span><br><span class="line">http &#123;</span><br><span class="line">    include       /etc/nginx/mime.types;</span><br><span class="line">    default_type  application/octet-stream;</span><br><span class="line">    log_format  main  &#x27;$remote_addr - $remote_user [$time_local] &quot;$request&quot; &#x27;</span><br><span class="line">                      &#x27;$status $body_bytes_sent &quot;$http_referer&quot; &#x27;</span><br><span class="line">                      &#x27;&quot;$http_user_agent&quot; &quot;$http_x_forwarded_for&quot;&#x27;;</span><br><span class="line"></span><br><span class="line">    access_log  /var/log/nginx/access.log  main;</span><br><span class="line"></span><br><span class="line">    sendfile        on;</span><br><span class="line">    #tcp_nopush     on;</span><br><span class="line"></span><br><span class="line">    keepalive_timeout  65;</span><br><span class="line">    client_max_body_size 0;</span><br><span class="line">    #gzip  on;</span><br><span class="line"></span><br><span class="line">    include /etc/nginx/conf.d/*.conf;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>然后单独配置网页监听，/conf.d/test.conf</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><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><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">server &#123;</span><br><span class="line">    listen       10010;</span><br><span class="line">    server_name  localhost;</span><br><span class="line">    </span><br><span class="line">    location / &#123;</span><br><span class="line">      root /etc/nginx/html/dist;</span><br><span class="line">      index index.html index.htm;</span><br><span class="line">      try_files $uri $uri/ @router;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line"></span><br><span class="line">    location /api &#123;</span><br><span class="line">        proxy_pass http://192.168.1.1:9888/api;</span><br><span class="line">        proxy_set_header X-Forwarded-Proto $scheme;</span><br><span class="line">        proxy_set_header X-Forwarded-Port $server_port;</span><br><span class="line">        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;</span><br><span class="line">        proxy_set_header Upgrade $http_upgrade;</span><br><span class="line">        proxy_set_header Connection &quot;upgrade&quot;;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line"></span><br><span class="line">    error_page 404 /404.html;</span><br><span class="line">    location = /404.html &#123;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    error_page 500 502 503 504 /50x.html;</span><br><span class="line">    location = /50x.html &#123;</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>启动容器</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line">docker run -d \</span><br><span class="line">--name nginx \</span><br><span class="line">-p 10010:10010 \</span><br><span class="line">-v /data/nginx/nginx.conf:/etc/nginx/nginx.conf \</span><br><span class="line">-v /data/nginx/conf.d:/etc/nginx/conf.d \</span><br><span class="line">-v /data/nginx/logs:/var/log/nginx \</span><br><span class="line">-v /data/nginx/www:/etc/nginx/html \</span><br><span class="line">--restart unless-stopped \</span><br><span class="line">nginx</span><br></pre></td></tr></table></figure><h1 id="Mysql"><a href="#Mysql" class="headerlink" title="Mysql"></a>Mysql</h1><h2 id="官网DEB包安装"><a href="#官网DEB包安装" class="headerlink" title="官网DEB包安装"></a>官网DEB包安装</h2><p><a href="https://downloads.mysql.com/archives/community/">https://downloads.mysql.com/archives/community/</a></p><p>选择官网适配有的操作系统，这里就选择<code>Ubantu Linux</code>，版本自己选择。</p><p>要是没有对应的版本，就看看使用的操作系统是基于什么的，也可以尝试一下，实在不行就只能试试使用源码进行编译了。</p><p>我这里下载的就是第一个<code>Ubuntu Linux 24.10 (x86, 64-bit), DEB Bundle</code>，包含了所有的数据库依赖了。</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># 解压</span></span><br><span class="line">tar -xvf mysql-server_8.0.41-1ubuntu24.10_amd64.deb-bundle.tar</span><br><span class="line"><span class="comment"># 里面包含这些依赖包</span></span><br><span class="line">libmysqlclient-dev_8.0.41-1ubuntu24.10_amd64.deb</span><br><span class="line">libmysqlclient21_8.0.41-1ubuntu24.10_amd64.deb</span><br><span class="line">mysql-client_8.0.41-1ubuntu24.10_amd64.deb</span><br><span class="line">mysql-common_8.0.41-1ubuntu24.10_amd64.deb</span><br><span class="line">mysql-community-client-core_8.0.41-1ubuntu24.10_amd64.deb</span><br><span class="line">mysql-community-client-plugins_8.0.41-1ubuntu24.10_amd64.deb</span><br><span class="line">mysql-community-client_8.0.41-1ubuntu24.10_amd64.deb</span><br><span class="line">mysql-community-server-core_8.0.41-1ubuntu24.10_amd64.deb</span><br><span class="line">mysql-community-server-debug_8.0.41-1ubuntu24.10_amd64.deb</span><br><span class="line">mysql-community-server_8.0.41-1ubuntu24.10_amd64.deb</span><br><span class="line">mysql-community-test-debug_8.0.41-1ubuntu24.10_amd64.deb</span><br><span class="line">mysql-community-test_8.0.41-1ubuntu24.10_amd64.deb</span><br><span class="line">mysql-server_8.0.41-1ubuntu24.10_amd64.deb</span><br><span class="line">mysql-testsuite_8.0.41-1ubuntu24.10_amd64.deb</span><br></pre></td></tr></table></figure><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><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="built_in">sudo</span> dpkg -i mysql-common_8.0.41-1ubuntu24.10_amd64.deb</span><br><span class="line"><span class="comment"># 核心库</span></span><br><span class="line"><span class="built_in">sudo</span> dpkg -i libmysqlclient21_8.0.41-1ubuntu24.10_amd64.deb</span><br><span class="line"><span class="built_in">sudo</span> dpkg -i libmysqlclient-dev_8.0.41-1ubuntu24.10_amd64.deb</span><br><span class="line"><span class="comment"># 客户端组件</span></span><br><span class="line"><span class="built_in">sudo</span> dpkg -i mysql-community-client-core_8.0.41-1ubuntu24.10_amd64.deb</span><br><span class="line"><span class="built_in">sudo</span> dpkg -i mysql-community-client-plugins_8.0.41-1ubuntu24.10_amd64.deb</span><br><span class="line"><span class="built_in">sudo</span> dpkg -i mysql-community-client_8.0.41-1ubuntu24.10_amd64.deb</span><br><span class="line"><span class="built_in">sudo</span> dpkg -i mysql-client_8.0.41-1ubuntu24.10_amd64.deb</span><br><span class="line"><span class="comment"># 服务器组件</span></span><br><span class="line"><span class="built_in">sudo</span> dpkg -i mysql-community-server-core_8.0.41-1ubuntu24.10_amd64.deb</span><br><span class="line"><span class="built_in">sudo</span> dpkg -i mysql-community-server_8.0.41-1ubuntu24.10_amd64.deb</span><br><span class="line"><span class="built_in">sudo</span> dpkg -i mysql-server_8.0.41-1ubuntu24.10_amd64.deb</span><br><span class="line"><span class="comment"># 测试调试组件</span></span><br><span class="line"><span class="built_in">sudo</span> dpkg -i mysql-community-test_8.0.41-1ubuntu24.10_amd64.deb</span><br><span class="line"><span class="built_in">sudo</span> dpkg -i mysql-community-test-debug_8.0.41-1ubuntu24.10_amd64.deb</span><br><span class="line"><span class="built_in">sudo</span> dpkg -i mysql-testsuite_8.0.41-1ubuntu24.10_amd64.deb</span><br></pre></td></tr></table></figure><h2 id="docker-2"><a href="#docker-2" class="headerlink" title="docker"></a>docker</h2><p>创建好存储目录</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># 日志目录</span></span><br><span class="line"><span class="built_in">mkdir</span> -p /data/docker/mysql/log</span><br><span class="line"><span class="comment"># 数据目录</span></span><br><span class="line"><span class="built_in">mkdir</span> -p /data/docker/mysql/data</span><br><span class="line"><span class="comment"># 配置文件目录</span></span><br><span class="line"><span class="built_in">mkdir</span> -p /data/docker/mysql/conf</span><br></pre></td></tr></table></figure><p>创建配置文件<code>/data/docker/mysql/conf/custom.cnf</code>，具体配置可以自行修改</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><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">[client]</span><br><span class="line">default-character-set=utf8mb4</span><br><span class="line"></span><br><span class="line">[mysql]</span><br><span class="line">default-character-set=utf8mb4</span><br><span class="line"></span><br><span class="line">[mysqld]</span><br><span class="line">init_connect=&quot;SET collation_connection = utf8mb4_general_ci&quot;</span><br><span class="line">init_connect=&quot;SET NAMES utf8mb4&quot;</span><br><span class="line">character-set-server=utf8mb4</span><br><span class="line">collation-server=utf8mb4_general_ci</span><br><span class="line">skip-character-set-client-handshake</span><br><span class="line">skip-name-resolve</span><br></pre></td></tr></table></figure><p>创建并运行容器</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line">docker run </span><br><span class="line">--name mysql</span><br><span class="line">--restart=always</span><br><span class="line">-p 3306:3306 </span><br><span class="line">-v /data/docker/mysql/log:/var/log/mysql </span><br><span class="line">-v /data/docker/mysql/data:/var/lib/mysql </span><br><span class="line">-v /data/docker/mysql/conf:/etc/mysql/conf.d </span><br><span class="line">-e MYSQL_ROOT_PASSWORD=123456</span><br><span class="line">-d mysql:5.7.44</span><br></pre></td></tr></table></figure><p>验证容器</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></pre></td><td class="code"><pre><span class="line"><span class="comment"># 进入容器</span></span><br><span class="line">docker <span class="built_in">exec</span> -it mysql bash</span><br><span class="line"></span><br><span class="line"><span class="comment"># 进入容器后</span></span><br><span class="line">mysql -uroot -p123456</span><br></pre></td></tr></table></figure><blockquote><p>注意，MySQL容器初始化会在存储目录产生一些初始化文件，如果想重复初始化，得先把之前的配置删干净，不然在创建容器会初始化报错。</p></blockquote><h1 id="Docker"><a href="#Docker" class="headerlink" title="Docker"></a>Docker</h1><h2 id="卸载旧版本"><a href="#卸载旧版本" class="headerlink" title="卸载旧版本"></a>卸载旧版本</h2><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="built_in">sudo</span> yum remove docker \</span><br><span class="line">                  docker-client \</span><br><span class="line">                  docker-client-latest \</span><br><span class="line">                  docker-common \</span><br><span class="line">                  docker-latest \</span><br><span class="line">                  docker-latest-logrotate \</span><br><span class="line">                  docker-logrotate \</span><br><span class="line">                  docker-selinux \</span><br><span class="line">                  docker-engine-selinux \</span><br><span class="line">                  docker-engine</span><br></pre></td></tr></table></figure><h2 id="安装Docker"><a href="#安装Docker" class="headerlink" title="安装Docker"></a>安装Docker</h2><h3 id="使用yum安装"><a href="#使用yum安装" class="headerlink" title="使用yum安装"></a>使用yum安装</h3><p>安装所需要的依赖包。</p><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">$ <span class="built_in">sudo</span> yum install -y yum-utils</span><br></pre></td></tr></table></figure><p>鉴于国内网络问题，强烈建议使用国内源，官方源请在注释中查看。</p><p>执行下面的命令添加 <code>yum</code> 软件源：</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></pre></td><td class="code"><pre><span class="line">$ <span class="built_in">sudo</span> yum-config-manager \</span><br><span class="line">    --add-repo \</span><br><span class="line">    https://mirrors.aliyun.com/docker-ce/linux/centos/docker-ce.repo</span><br><span class="line"></span><br><span class="line">$ <span class="built_in">sudo</span> sed -i <span class="string">&#x27;s/download.docker.com/mirrors.aliyun.com\/docker-ce/g&#x27;</span> /etc/yum.repos.d/docker-ce.repo</span><br></pre></td></tr></table></figure><p>有了依赖包之后，就可以直接安装Docker了。</p><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">$ <span class="built_in">sudo</span> yum install docker-ce docker-ce-cli containerd.io</span><br></pre></td></tr></table></figure><h3 id="使用脚本自动安装"><a href="#使用脚本自动安装" class="headerlink" title="使用脚本自动安装"></a>使用脚本自动安装</h3><p>在测试或开发环境中 Docker 官方为了简化安装流程，提供了一套便捷的安装脚本，CentOS 系统上可以使用这套脚本安装，另外可以通过 <code>--mirror</code> 选项使用国内源进行安装：</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">$ curl -fsSL get.docker.com -o get-docker.sh</span><br><span class="line">$ <span class="built_in">sudo</span> sh get-docker.sh --mirror Aliyun</span><br></pre></td></tr></table></figure><p>执行这个命令后，脚本就会自动的将一切准备工作做好，并且把 Docker 的稳定(stable)版本安装在系统中。</p><h2 id="启动Docker"><a href="#启动Docker" class="headerlink" title="启动Docker"></a>启动Docker</h2><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="built_in">sudo</span> systemctl <span class="built_in">enable</span> docker</span><br><span class="line">$ <span class="built_in">sudo</span> systemctl start docker</span><br></pre></td></tr></table></figure><h2 id="建立docker用户组"><a href="#建立docker用户组" class="headerlink" title="建立docker用户组"></a>建立docker用户组</h2><p>建立<code>docker</code>组</p><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">$ <span class="built_in">sudo</span> groupadd docker</span><br></pre></td></tr></table></figure><p>将当前用户加入<code>docker</code>组中</p><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">$ <span class="built_in">sudo</span> usermod -aG docker <span class="variable">$USER</span></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><ol><li>阿里云加速器，可以去阿里云的关于镜像的管理控制台去获取。</li><li>网易云加速器，<code>https://hub-mirror.c.163.com</code></li><li>百度云加速器，<code>https://mirror.baidubce.com</code></li></ol><h3 id="配置docker加速器"><a href="#配置docker加速器" class="headerlink" title="配置docker加速器"></a>配置docker加速器</h3><p>查看之前是否配置过镜像地址。</p><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">$ systemctl <span class="built_in">cat</span> docker | grep <span class="string">&#x27;\-\-registry\-mirror&#x27;</span></span><br></pre></td></tr></table></figure><p>如果该命令有输出，那么请执行 <code>$ systemctl cat docker</code> 查看 <code>ExecStart=</code> 出现的位置，修改对应的文件内容去掉 <code>--registry-mirror</code> 参数及其值，并按接下来的步骤进行配置。</p><p>如果以上命令没有任何输出，那么就可以在 <code>/etc/docker/daemon.json</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;registry-mirrors&quot;</span><span class="punctuation">:</span> <span class="punctuation">[</span></span><br><span class="line">    <span class="string">&quot;https://hub-mirror.c.163.com&quot;</span><span class="punctuation">,</span></span><br><span class="line">    <span class="string">&quot;https://mirror.baidubce.com&quot;</span></span><br><span class="line">  <span class="punctuation">]</span></span><br><span class="line"><span class="punctuation">&#125;</span></span><br></pre></td></tr></table></figure><h3 id="重启docker服务"><a href="#重启docker服务" class="headerlink" title="重启docker服务"></a>重启docker服务</h3><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="built_in">sudo</span> systemctl daemon-reload</span><br><span class="line">$ <span class="built_in">sudo</span> systemctl restart docker</span><br></pre></td></tr></table></figure><h2 id="测试Docker是否安装成功"><a href="#测试Docker是否安装成功" class="headerlink" title="测试Docker是否安装成功"></a>测试Docker是否安装成功</h2><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br></pre></td><td class="code"><pre><span class="line">[root@localhost ~]# docker run --<span class="built_in">rm</span> hello-world</span><br><span class="line"></span><br><span class="line">Hello from Docker!</span><br><span class="line">This message shows that your installation appears to be working correctly.</span><br><span class="line"></span><br><span class="line">To generate this message, Docker took the following steps:</span><br><span class="line"> 1. The Docker client contacted the Docker daemon.</span><br><span class="line"> 2. The Docker daemon pulled the <span class="string">&quot;hello-world&quot;</span> image from the Docker Hub.</span><br><span class="line">    (amd64)</span><br><span class="line"> 3. The Docker daemon created a new container from that image <span class="built_in">which</span> runs the</span><br><span class="line">    executable that produces the output you are currently reading.</span><br><span class="line"> 4. The Docker daemon streamed that output to the Docker client, <span class="built_in">which</span> sent it</span><br><span class="line">    to your terminal.</span><br><span class="line"></span><br><span class="line">To try something more ambitious, you can run an Ubuntu container with:</span><br><span class="line"> $ docker run -it ubuntu bash</span><br><span class="line"></span><br><span class="line">Share images, automate workflows, and more with a free Docker ID:</span><br><span class="line"> https://hub.docker.com/</span><br><span class="line"></span><br><span class="line">For more examples and ideas, visit:</span><br><span class="line"> https://docs.docker.com/get-started/</span><br></pre></td></tr></table></figure><p>出现以上内容则表示安装成功。</p><h1 id="Docker离线安装"><a href="#Docker离线安装" class="headerlink" title="Docker离线安装"></a>Docker离线安装</h1><p>下载Docker离线安装包。</p><p><a href="https://download.docker.com/linux/static/stable/x86_64/">下载地址</a></p><p>系统配置文件<code>docker.service</code></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><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><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">[Unit]</span><br><span class="line">Description=Docker Application Container Engine</span><br><span class="line">Documentation=https://docs.docker.com</span><br><span class="line">After=network - online.target firewalld.service</span><br><span class="line">Wants=network - online.target</span><br><span class="line">[Service]</span><br><span class="line">Type=notify</span><br><span class="line"># the default is not to use systemd for cgroups because the delegate issues still</span><br><span class="line"># exists and systemd currently does not support the cgroup feature set required</span><br><span class="line"># for containers run by docker </span><br><span class="line">ExecStart=/usr/bin/dockerd  --ipv6=false</span><br><span class="line">ExecReload=/bin/kill -s HUP $MAINPID</span><br><span class="line"># Having non - zero Limit*s causes performance problems due to accounting overhead</span><br><span class="line"># in the kernel. We recommend using cgroups to do container - local accounting.</span><br><span class="line">LimitNOFILE=infinity</span><br><span class="line">LimitNPROC=infinity</span><br><span class="line">LimitCORE=infinity</span><br><span class="line"># Uncomment TasksMax if your systemd version supports it.</span><br><span class="line"># Only systemd 226 and above support this version.</span><br><span class="line">#TasksMax=infinity</span><br><span class="line">TimeoutStartSec=0</span><br><span class="line"># set delegate yes so that systemd does not reset the cgroups of docker containers</span><br><span class="line">Delegate=yes</span><br><span class="line"># kill only the docker process, not all processes in the cgroup</span><br><span class="line">KillMode=process</span><br><span class="line"># restart the docker process if it exits prematurely</span><br><span class="line">Restart=on - failure</span><br><span class="line">StartLimitBurst=3</span><br><span class="line">StartLimitInterval=60s</span><br><span class="line">[Install]</span><br><span class="line">WantedBy=multi - user.target</span><br></pre></td></tr></table></figure><p>安装脚本。</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#!/bin/sh</span></span><br><span class="line"> </span><br><span class="line"><span class="built_in">echo</span> <span class="string">&#x27;解压tar包&#x27;</span></span><br><span class="line">tar -xvf <span class="variable">$1</span></span><br><span class="line"><span class="built_in">echo</span> <span class="string">&#x27;将docker目录下所有文件复制到/usr/bin目录&#x27;</span></span><br><span class="line"><span class="built_in">cp</span> docker/* /usr/bin</span><br><span class="line"><span class="built_in">echo</span> <span class="string">&#x27;将docker.service 复制到/etc/systemd/system/目录&#x27;</span></span><br><span class="line"><span class="built_in">cp</span> <span class="variable">$2</span> /etc/systemd/system/</span><br><span class="line"><span class="built_in">echo</span> <span class="string">&#x27;添加文件可执行权限&#x27;</span></span><br><span class="line"><span class="built_in">chmod</span> +x /etc/systemd/system/docker.service</span><br><span class="line"><span class="built_in">echo</span> <span class="string">&#x27;重新加载配置文件&#x27;</span></span><br><span class="line">systemctl daemon-reload</span><br><span class="line"><span class="built_in">echo</span> <span class="string">&#x27;启动docker&#x27;</span></span><br><span class="line">systemctl start docker</span><br><span class="line"><span class="built_in">echo</span> <span class="string">&#x27;设置开机自启&#x27;</span></span><br><span class="line">systemctl <span class="built_in">enable</span> docker.service</span><br><span class="line"><span class="built_in">echo</span> <span class="string">&#x27;docker安装成功&#x27;</span></span><br><span class="line">docker -v</span><br></pre></td></tr></table></figure><p>卸载脚本。</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#!/bin/sh</span></span><br><span class="line"> </span><br><span class="line"><span class="built_in">echo</span> <span class="string">&#x27;停止docker&#x27;</span></span><br><span class="line">systemctl stop docker</span><br><span class="line"><span class="built_in">echo</span> <span class="string">&#x27;删除docker.service&#x27;</span></span><br><span class="line"><span class="built_in">rm</span> -f /etc/systemd/system/docker.service</span><br><span class="line"><span class="built_in">echo</span> <span class="string">&#x27;删除docker文件&#x27;</span></span><br><span class="line"><span class="built_in">rm</span> -rf /usr/bin/docker*</span><br><span class="line"><span class="built_in">echo</span> <span class="string">&#x27;重新加载配置文件&#x27;</span></span><br><span class="line">systemctl daemon-reload</span><br><span class="line"><span class="built_in">echo</span> <span class="string">&#x27;卸载成功&#x27;</span></span><br></pre></td></tr></table></figure><p>通过安装包，执行安装脚本。</p><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">sh install.sh docker-23.0.3.tgz docker.service</span><br></pre></td></tr></table></figure><h1 id="docker-compose-1"><a href="#docker-compose-1" class="headerlink" title="docker-compose"></a>docker-compose</h1><p>docker-compose本质就是docker命令，所以安装了docker后，只需要把这个命令放到服务器环境中就能调用了</p><ol><li><p>根据架构下载对应二进制命令文件，下载地址<a href="https://github.com/docker/compose/releases">https://github.com/docker/compose/releases</a></p></li><li><p>比如x86架构，就下载<code>docker-compose-darwin-x86_64</code>，把二进制文件名字改成<code>docker-compose</code> ,然后将命令文件上传到服务器的<code>/usr/local/bin</code>路径中</p></li><li><p>给命令添加可执行权限</p><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"><span class="built_in">sudo</span> <span class="built_in">chmod</span> +x /usr/local/bin/docker-compose</span><br></pre></td></tr></table></figure></li><li><p>测试命令</p><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">docker-compose --version</span><br></pre></td></tr></table></figure></li></ol><h1 id="RAGFlow"><a href="#RAGFlow" class="headerlink" title="RAGFlow"></a>RAGFlow</h1><p>这里只记录<code>docker-compose</code>的安装方式，RafFlow涉及的服务有点多，使用docker-compose方便很多。</p><p>官方的仓库<a href="https://github.com/infiniflow/ragflow.git">https://github.com/infiniflow/ragflow.git</a></p><p>下载这个仓库，重点是<code>docker</code>目录</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></pre></td><td class="code"><pre><span class="line">git <span class="built_in">clone</span> https://github.com/infiniflow/ragflow.git</span><br><span class="line"></span><br><span class="line"><span class="built_in">cd</span> ./ragflow/docker</span><br></pre></td></tr></table></figure><p>修改配置文件<code>.env</code>，可以根据官方文档自行修改，这里只修改RAGFlow的版本。</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></pre></td><td class="code"><pre><span class="line"><span class="comment"># 默认镜像是slim的</span></span><br><span class="line"><span class="comment"># RAGFLOW_IMAGE=infiniflow/ragflow:v0.20.5-slim</span></span><br><span class="line"><span class="comment"># 修改成这个镜像</span></span><br><span class="line">RAGFLOW_IMAGE=infiniflow/ragflow:v0.20.5</span><br></pre></td></tr></table></figure><p>在<code>docker目录中</code>启动容器</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></pre></td><td class="code"><pre><span class="line"><span class="comment"># 启动</span></span><br><span class="line">docker-compose up -d</span><br><span class="line"><span class="comment"># 停止</span></span><br><span class="line">docker-compose stop</span><br><span class="line"></span><br><span class="line"><span class="comment"># 如果要删除，尽量把数据卷也删掉</span></span><br><span class="line">docker-compose down -v</span><br></pre></td></tr></table></figure><p>可能会遇到的问题</p><ol><li><p>Mysql容器出现问题，起不来</p><p>这个容器很容易出问题，所以得检查数据卷是否已经存在，是否有初始化文件重复问题，请删干净</p><p>权限问题，在docker的Mysql配置文件中，即<code>docker/docker-compose-base.yml</code>，添加配置</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></pre></td><td class="code"><pre><span class="line">mysql:</span><br><span class="line">privileged: true</span><br><span class="line">user: root</span><br></pre></td></tr></table></figure></li><li><p>有其他配套服务起不来</p><p>也尝试在配置中添加<code>privileged: ture</code>配置</p></li><li><p>ragflow-server报错</p><p>OPENBLAS blas_thread_init: pthread_create failed for thread x of 64: Operation not permitted</p><p>有可能是python的问题，详情参考<a href="https://stackoverflow.com/questions/52026652/openblas-blas-thread-init-pthread-create-resource-temporarily-unavailable这个修改方案">https://stackoverflow.com/questions/52026652/openblas-blas-thread-init-pthread-create-resource-temporarily-unavailable这个修改方案</a></p><p>修改了之后，启动可能还会报一个奇怪的错误，也可以尝试在配置文件<code>docker-compose.yml</code>中添加<code>privileged: ture</code>配置</p></li></ol><h1 id="声明"><a href="#声明" class="headerlink" title="声明"></a>声明</h1><p>以上基本都是网上搜集的教程，基本都是复制粘贴，有些已经找不到出处，有出处的我尽量标明在以下链接。</p><p><a href="https://yeasy.gitbook.io/docker_practice/">Docker</a></p><p><a href="https://chexl.blog.csdn.net/article/details/127932711?spm=1001.2101.3001.6650.1&amp;utm_medium=distribute.pc_relevant.none-task-blog-2~default~CTRLIST~Rate-1-127932711-blog-122311325.235%5Ev28%5Epc_relevant_default&amp;depth_1-utm_source=distribute.pc_relevant.none-task-blog-2~default~CTRLIST~Rate-1-127932711-blog-122311325.235%5Ev28%5Epc_relevant_default&amp;utm_relevant_index=2">Docker离线安装</a></p><p><a href="https://blog.csdn.net/m0_61035257/article/details/125705400">Java安装</a></p><p><a href="https://blog.csdn.net/BodyandSoul/article/details/121297563">Redis离线安装</a></p><p><a href="https://blog.csdn.net/gogospecter/article/details/149069779">离线安装nginx</a></p>]]></content:encoded>
            
            
            <category domain="https://yww52.com/categories/%E6%88%91%E7%9A%84%E6%8A%80%E6%9C%AF%E5%AD%A6%E4%B9%A0%E7%89%A9%E8%AF%AD%E6%9E%9C%E7%84%B6%E6%9C%89%E9%97%AE%E9%A2%98/">我的技术学习物语果然有问题</category>
            
            <category domain="https://yww52.com/categories/%E6%88%91%E7%9A%84%E6%8A%80%E6%9C%AF%E5%AD%A6%E4%B9%A0%E7%89%A9%E8%AF%AD%E6%9E%9C%E7%84%B6%E6%9C%89%E9%97%AE%E9%A2%98/%E5%BC%80%E5%8F%91%E6%8A%80%E5%B7%A7/">开发技巧</category>
            
            
            
            <comments>https://yww52.com/posts/58d09dd7/#disqus_thread</comments>
            
        </item>
        
        <item>
            <title>MybatisPlus常用方法</title>
            <link>https://yww52.com/posts/4e5ad84e/</link>
            <guid>https://yww52.com/posts/4e5ad84e/</guid>
            <pubDate>Wed, 01 Feb 2023 16:00:00 GMT</pubDate>
            
            <description>一些MybatisPlus常见的使用方法。</description>
            
            
            
            <content:encoded><![CDATA[<img src="https://img.yww52.com/2023/2/2023-2-2top_img.jpg" alt="MybatisPlus常用方法" style="max-width:100%;height:auto;margin-bottom:15px;"
  /><h1 id="简介"><a href="#简介" class="headerlink" title="简介"></a>简介</h1><p>记录一下常用的一些使用方法。</p><h1 id="测试环境"><a href="#测试环境" class="headerlink" title="测试环境"></a>测试环境</h1><h2 id="表SQL"><a href="#表SQL" class="headerlink" title="表SQL"></a>表SQL</h2><figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br></pre></td><td class="code"><pre><span class="line"><span class="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">CHAR</span>(<span class="number">19</span>)        <span class="keyword">NOT NULL</span>                        COMMENT <span class="string">&#x27;数据ID&#x27;</span>,</span><br><span class="line">  `username`      <span class="type">VARCHAR</span>(<span class="number">200</span>)    <span class="keyword">NOT NULL</span>                        COMMENT <span class="string">&#x27;用户名&#x27;</span>,</span><br><span class="line">  `password`      <span class="type">VARCHAR</span>(<span class="number">200</span>)    <span class="keyword">NOT NULL</span>                        COMMENT <span class="string">&#x27;密码&#x27;</span>,</span><br><span class="line">  `nickname`      <span class="type">VARCHAR</span>(<span class="number">200</span>)    <span class="keyword">NULL</span>    <span class="keyword">DEFAULT</span> <span class="string">&#x27;用户昵称&#x27;</span>       COMMENT <span class="string">&#x27;用户昵称&#x27;</span>,</span><br><span class="line">  `avatar`        <span class="type">VARCHAR</span>(<span class="number">200</span>)    <span class="keyword">NULL</span>    <span class="keyword">DEFAULT</span> <span class="string">&#x27;&#x27;</span>              COMMENT <span class="string">&#x27;用户头像地址&#x27;</span>,</span><br><span class="line">  `email`         <span class="type">VARCHAR</span>(<span class="number">200</span>)    <span class="keyword">NULL</span>    <span class="keyword">DEFAULT</span> <span class="string">&#x27;123456@qq.com&#x27;</span> COMMENT <span class="string">&#x27;用户邮箱地址&#x27;</span>,</span><br><span class="line">  `status`        BIT <span class="keyword">DEFAULT</span> <span class="number">1</span>   <span class="keyword">NOT NULL</span>                        COMMENT <span class="string">&#x27;账号状态,0禁用|1正常&#x27;</span>,</span><br><span class="line">    `sex`        BIT <span class="keyword">DEFAULT</span> <span class="number">1</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">200</span>)    <span class="keyword">NULL</span>    <span class="keyword">DEFAULT</span> <span class="string">&#x27;18888888888&#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">    `create_by`     <span class="type">VARCHAR</span>(<span class="number">200</span>)    <span class="keyword">NOT NULL</span>                        COMMENT <span class="string">&#x27;创建人&#x27;</span>,</span><br><span class="line">    `update_time`   DATETIME        <span class="keyword">NOT NULL</span>                        COMMENT <span class="string">&#x27;更新时间&#x27;</span>,</span><br><span class="line">    `update_by`     <span class="type">VARCHAR</span>(<span class="number">200</span>)    <span class="keyword">NOT NULL</span>                        COMMENT <span class="string">&#x27;更新人&#x27;</span>,</span><br><span class="line">    <span class="keyword">PRIMARY KEY</span> (`id`),</span><br><span class="line">    <span class="keyword">CONSTRAINT</span>  username <span class="keyword">unique</span> (username)</span><br><span class="line">  ) ENGINE <span class="operator">=</span> InnoDB <span class="keyword">DEFAULT</span> CHARSET <span class="operator">=</span> utf8mb4 COMMENT <span class="operator">=</span> <span class="string">&#x27;用户信息实体类&#x27;</span>;</span><br></pre></td></tr></table></figure><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><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><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></pre></td><td class="code"><pre><span class="line"><span class="keyword">package</span> com.yww.demo.entity;</span><br><span class="line"></span><br><span class="line"><span class="keyword">import</span> com.baomidou.mybatisplus.annotation.*;</span><br><span class="line"><span class="keyword">import</span> io.swagger.v3.oas.annotations.media.Schema;</span><br><span class="line"><span class="keyword">import</span> lombok.AllArgsConstructor;</span><br><span class="line"><span class="keyword">import</span> lombok.Builder;</span><br><span class="line"><span class="keyword">import</span> lombok.Data;</span><br><span class="line"><span class="keyword">import</span> lombok.NoArgsConstructor;</span><br><span class="line"></span><br><span class="line"><span class="keyword">import</span> java.io.Serializable;</span><br><span class="line"><span class="keyword">import</span> java.time.LocalDateTime;</span><br><span class="line"></span><br><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * &lt;p&gt;</span></span><br><span class="line"><span class="comment"> *      (user)用户信息实体类</span></span><br><span class="line"><span class="comment"> * &lt;/p&gt;</span></span><br><span class="line"><span class="comment"> *</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@Author</span> yww</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@Date</span> 2023-2-20</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">@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">@TableName(&quot;user&quot;)</span></span><br><span class="line"><span class="meta">@Schema(name = &quot;User&quot;, description = &quot;用户信息实体类&quot;)</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="meta">@Schema(description = &quot;数据ID&quot;)</span></span><br><span class="line">    <span class="meta">@TableId(value = &quot;id&quot;, type = IdType.ASSIGN_ID)</span></span><br><span class="line">    <span class="keyword">private</span> String id;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Schema(description = &quot;用户名&quot;)</span></span><br><span class="line">    <span class="meta">@TableField(&quot;username&quot;)</span></span><br><span class="line">    <span class="keyword">private</span> String username;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Schema(description = &quot;密码&quot;)</span></span><br><span class="line">    <span class="meta">@TableField(&quot;password&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">@Schema(description = &quot;用户昵称&quot;)</span></span><br><span class="line">    <span class="meta">@TableField(&quot;nickname&quot;)</span></span><br><span class="line">    <span class="keyword">private</span> String nickname;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Schema(description = &quot;用户头像地址&quot;)</span></span><br><span class="line">    <span class="meta">@TableField(&quot;avatar&quot;)</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="meta">@Schema(description = &quot;用户邮箱地址&quot;)</span></span><br><span class="line">    <span class="meta">@TableField(&quot;email&quot;)</span></span><br><span class="line">    <span class="keyword">private</span> String email;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Schema(description = &quot;性别,0是女|1是男&quot;)</span></span><br><span class="line">    <span class="meta">@TableField(&quot;sex&quot;)</span></span><br><span class="line">    <span class="keyword">private</span> Boolean sex;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Schema(description = &quot;电话号码&quot;)</span></span><br><span class="line">    <span class="meta">@TableField(&quot;phone&quot;)</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="meta">@Schema(description = &quot;账号状态,0禁用|1正常&quot;)</span></span><br><span class="line">    <span class="meta">@TableField(&quot;status&quot;)</span></span><br><span class="line">    <span class="keyword">private</span> Boolean status;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Schema(description = &quot;创建时间&quot;)</span></span><br><span class="line">    <span class="meta">@TableField(value = &quot;create_time&quot;, 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">@Schema(description = &quot;创建人&quot;)</span></span><br><span class="line">    <span class="meta">@TableField(value = &quot;create_by&quot;, fill = FieldFill.INSERT)</span></span><br><span class="line">    <span class="keyword">private</span> String createBy;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Schema(description = &quot;更新时间&quot;)</span></span><br><span class="line">    <span class="meta">@TableField(value = &quot;update_time&quot;, 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">@Schema(description = &quot;更新人&quot;)</span></span><br><span class="line">    <span class="meta">@TableField(value = &quot;update_by&quot;, fill = FieldFill.INSERT_UPDATE)</span></span><br><span class="line">    <span class="keyword">private</span> String updateBy;</span><br><span class="line"></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h2 id="执行SQL分析打印"><a href="#执行SQL分析打印" class="headerlink" title="执行SQL分析打印"></a>执行SQL分析打印</h2><p>使用<code>p6spy</code>来打印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">dependency</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">groupId</span>&gt;</span>p6spy<span class="tag">&lt;/<span class="name">groupId</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">artifactId</span>&gt;</span>p6spy<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;p6spy.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></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="comment"># 数据库配置(p6spy)</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.p6spy.engine.spy.P6SpyDriver</span></span><br><span class="line">  <span class="attr">url:</span> <span class="string">jdbc:p6spy:mysql://localhost:3306/demo?userUnicode=true&amp;useSSL=false&amp;characterEncoding=utf-8&amp;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">password</span></span><br></pre></td></tr></table></figure><h2 id="假数据的生成"><a href="#假数据的生成" class="headerlink" title="假数据的生成"></a>假数据的生成</h2><p>这里使用了<code>datafaker</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>net.datafaker<span class="tag">&lt;/<span class="name">groupId</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">artifactId</span>&gt;</span>datafaker<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.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">dependency</span>&gt;</span></span><br></pre></td></tr></table></figure><p>随机生成600条数据。</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><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> <span class="keyword">void</span> <span class="title function_">random</span><span class="params">(Integer number)</span> &#123;</span><br><span class="line">    <span class="type">Faker</span> <span class="variable">cnFaker</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">Faker</span>(<span class="keyword">new</span> <span class="title class_">Locale</span>(<span class="string">&quot;zh-CN&quot;</span>));</span><br><span class="line">    <span class="type">Faker</span> <span class="variable">enFaker</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">Faker</span>(<span class="keyword">new</span> <span class="title class_">Locale</span>(<span class="string">&quot;en&quot;</span>));</span><br><span class="line">    <span class="type">Random</span> <span class="variable">random</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">Random</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; number; i++) &#123;</span><br><span class="line">        <span class="type">User</span> <span class="variable">user</span> <span class="operator">=</span> User.builder()</span><br><span class="line">                .username(enFaker.name().username() + i)</span><br><span class="line">                .password(enFaker.passport().valid())</span><br><span class="line">                .nickname(cnFaker.name().name())</span><br><span class="line">                .avatar(enFaker.avatar().image())</span><br><span class="line">                .email(StrUtil.cleanBlank(enFaker.name().fullName().toLowerCase()) + <span class="string">&quot;@qq.com&quot;</span>)</span><br><span class="line">                .sex(random.nextBoolean())</span><br><span class="line">                .phone(StrUtil.cleanBlank(cnFaker.phoneNumber().phoneNumber()))</span><br><span class="line">                .status(<span class="literal">true</span>)</span><br><span class="line">                .build();</span><br><span class="line">        <span class="built_in">this</span>.baseMapper.insert(user);</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>环境使用<code>MybatisPlus</code>代码生成器的结构。</p><blockquote><ol><li>service继承IService</li><li>serviceImpl继承ServiceImpl，实现service</li><li>mapper继承BaseMapper</li></ol></blockquote><h1 id="补充说明"><a href="#补充说明" class="headerlink" title="补充说明"></a>补充说明</h1><h2 id="关于服务实现类的API"><a href="#关于服务实现类的API" class="headerlink" title="关于服务实现类的API"></a>关于服务实现类的API</h2><p>根据上述的代码结构，在服务的实现类中，会有两套API。</p><ol><li>Service CRUD接口</li><li>Mapper CRUD接口</li></ol><p>前者算是对Mapper接口的封装，所以前者会有Mapper接口里基本所有方法，也会添加很多封装后的方法。后者是基础的Mapper使用。</p><p>所以以下大多数使用都是基于前者，当有用到Mapper里的方法才会使用后者。</p><h2 id="关于条件构造器"><a href="#关于条件构造器" class="headerlink" title="关于条件构造器"></a>关于条件构造器</h2><p>这里以查询的条件构造器为例子。</p><h3 id="QueryWrapper"><a href="#QueryWrapper" class="headerlink" title="QueryWrapper"></a>QueryWrapper</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">QueryWrapper&lt;User&gt; queryWrapper = <span class="keyword">new</span> <span class="title class_">QueryWrapper</span>&lt;&gt;();</span><br><span class="line">queryWrapper.eq(<span class="string">&quot;id&quot;</span>, <span class="number">1</span>);</span><br><span class="line"><span class="built_in">this</span>.getOne(queryWrapper);</span><br></pre></td></tr></table></figure><h3 id="LambdaQueryWrapper"><a href="#LambdaQueryWrapper" class="headerlink" title="LambdaQueryWrapper"></a>LambdaQueryWrapper</h3><p>这个条件构造器使用了<code>Lambda</code>语法，使用起来更方便，而且使用这个方法不用构造条件，不用填写表的字段，因为字段这种不能修改的对开发来说这是十分不友好的，<code>LambdaQueryWrapper</code>可以直接使用实体类属性来构造条件。</p><ol><li><p>使用<code>QueryWrapper</code>的<code>lambda</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">QueryWrapper&lt;User&gt; queryWrapper = <span class="keyword">new</span> <span class="title class_">QueryWrapper</span>&lt;&gt;();</span><br><span class="line">queryWrapper.eq(<span class="string">&quot;id&quot;</span>, <span class="number">1</span>);</span><br><span class="line">LambdaQueryWrapper&lt;User&gt; lambda = queryWrapper.lambda();</span><br><span class="line"><span class="built_in">this</span>.getOne(lambda);</span><br></pre></td></tr></table></figure></li><li><p>使用<code>Wrappers.lambdaQuery()</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">LambdaQueryWrapper&lt;User&gt; lambda = Wrappers.lambdaQuery(User.builder().id(<span class="string">&quot;1&quot;</span>).build());</span><br><span class="line"><span class="built_in">this</span>.getOne(lambda);</span><br></pre></td></tr></table></figure></li></ol><p>以下的条件构造器，都是基于第二种方法生成。</p><h2 id="防全表更新与删除插件"><a href="#防全表更新与删除插件" class="headerlink" title="防全表更新与删除插件"></a>防全表更新与删除插件</h2><p>这个插件我感觉还是很有必要的，要是开发出现一点错误，这个插件能多一份保障。当出现全表更新或者是全表删除的SQL时，<code>MybatisPlus</code>能帮我们拦截，并抛出异常。</p><p><a href="https://baomidou.com/pages/c571bc/#blockattackinnerinterceptor">官方插件说明</a></p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * 插件配置</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="meta">@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">    <span class="comment">// 防全表更新与删除插件</span></span><br><span class="line">    interceptor.addInnerInterceptor(<span class="keyword">new</span> <span class="title class_">BlockAttackInnerInterceptor</span>());</span><br><span class="line">    <span class="keyword">return</span> interceptor;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h2 id="分页插件"><a href="#分页插件" class="headerlink" title="分页插件"></a>分页插件</h2><p>分页时需要用到的插件。</p><p><a href="https://baomidou.com/pages/97710a/#paginationinnerinterceptor">官方插件说明</a></p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment">    * 插件配置</span></span><br><span class="line"><span class="comment">    */</span></span><br><span class="line">   <span class="meta">@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">       <span class="comment">// 分页插件，指定数据库为MYSQL</span></span><br><span class="line">       interceptor.addInnerInterceptor(<span class="keyword">new</span> <span class="title class_">PaginationInnerInterceptor</span>(DbType.MYSQL));</span><br><span class="line">       <span class="keyword">return</span> interceptor;</span><br><span class="line">   &#125;</span><br></pre></td></tr></table></figure><blockquote><p>多个插件使用的情况，请将分页插件放到 <code>插件执行链</code> 最后面。</p></blockquote><h1 id="添加"><a href="#添加" class="headerlink" title="添加"></a>添加</h1><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></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="type">boolean</span> <span class="title function_">insert</span><span class="params">(User user)</span> &#123;</span><br><span class="line">    <span class="type">User</span> <span class="variable">insertUser</span> <span class="operator">=</span> User.builder()</span><br><span class="line">            .username(user.getUsername())</span><br><span class="line">            .password(user.getPassword())</span><br><span class="line">            .nickname(user.getNickname())</span><br><span class="line">            .avatar(user.getAvatar())</span><br><span class="line">            .email(user.getEmail())</span><br><span class="line">            .sex(user.getSex())</span><br><span class="line">            .phone(user.getPhone())</span><br><span class="line">            .status(user.getStatus())</span><br><span class="line">            .build();</span><br><span class="line">    <span class="comment">// 可以进行一些操作校验数据，比如说username是唯一的等等</span></span><br><span class="line">    <span class="keyword">return</span> <span class="built_in">this</span>.save(insertUser);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><blockquote><ol><li>添加的数据尽量自己先提取在添加，避免添加了不该添加的数据，比如说ID，不建议传入ID，更新用户这些字段，尽量避免这些脏数据。</li><li>考虑添加数据的条件，比如<code>username</code>是唯一的，某些数据是不为空的，或者某些数据是有什么格式的，都可以先将提取的元素进行判断后在添加。</li><li>只通过前端校验参数是不合理的。</li></ol></blockquote><h2 id="批量添加"><a href="#批量添加" class="headerlink" title="批量添加"></a>批量添加</h2><p>这里就不从请求上输入用户列表了，通过<code>faker</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="comment">/**</span></span><br><span class="line"><span class="comment"> * 随机生成number条用户数据</span></span><br><span class="line"><span class="comment"> *</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@param</span> number    指定数量</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> List&lt;User&gt; <span class="title function_">getUsers</span><span class="params">(<span class="type">int</span> number)</span> &#123;</span><br><span class="line">    <span class="type">Faker</span> <span class="variable">cnFaker</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">Faker</span>(<span class="keyword">new</span> <span class="title class_">Locale</span>(<span class="string">&quot;zh-CN&quot;</span>));</span><br><span class="line">    <span class="type">Faker</span> <span class="variable">enFaker</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">Faker</span>(<span class="keyword">new</span> <span class="title class_">Locale</span>(<span class="string">&quot;en&quot;</span>));</span><br><span class="line">    <span class="type">Random</span> <span class="variable">random</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">Random</span>();</span><br><span class="line">    List&lt;User&gt; userList = <span class="keyword">new</span> <span class="title class_">ArrayList</span>&lt;&gt;(number);</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; number; i++) &#123;</span><br><span class="line">        <span class="type">User</span> <span class="variable">user</span> <span class="operator">=</span> User.builder()</span><br><span class="line">                .username(enFaker.name().username() + i)</span><br><span class="line">                .password(enFaker.passport().valid())</span><br><span class="line">                .nickname(cnFaker.name().name())</span><br><span class="line">                .avatar(enFaker.avatar().image())</span><br><span class="line">                .email(StrUtil.cleanBlank(enFaker.name().fullName().toLowerCase()) + <span class="string">&quot;@qq.com&quot;</span>)</span><br><span class="line">                .sex(random.nextBoolean())</span><br><span class="line">                .phone(StrUtil.cleanBlank(cnFaker.phoneNumber().phoneNumber()))</span><br><span class="line">                .status(<span class="literal">true</span>)</span><br><span class="line">                .build();</span><br><span class="line">        userList.add(user);</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">return</span> userList;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h3 id="for循环添加"><a href="#for循环添加" class="headerlink" title="for循环添加"></a>for循环添加</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></pre></td><td class="code"><pre><span class="line">   <span class="meta">@Override</span></span><br><span class="line"><span class="meta">@Transactional(rollbackFor = Exception.class)</span></span><br><span class="line">   <span class="keyword">public</span> <span class="type">boolean</span> <span class="title function_">insertBatch1</span><span class="params">(List&lt;User&gt; userList)</span> &#123;</span><br><span class="line">       userList = getUsers(<span class="number">10</span>);</span><br><span class="line">       <span class="type">boolean</span> <span class="variable">res</span> <span class="operator">=</span> <span class="literal">true</span>;</span><br><span class="line">       <span class="keyword">for</span> (User user : userList) &#123;</span><br><span class="line">           res = res &amp;&amp; <span class="built_in">this</span>.insert(user);</span><br><span class="line">       &#125;</span><br><span class="line">       <span class="keyword">return</span> res;</span><br><span class="line">   &#125;</span><br></pre></td></tr></table></figure><p>通过<code>p6spy</code>的打印，可以看到执行的其中一条插入语句。</p><figure class="highlight plaintext"><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">Consume Time：1 ms 2023-02-22 15:30:30</span><br><span class="line">Execute SQL：INSERT INTO user ( id, username, password, nickname, avatar, email, sex, phone, status, create_time, create_by, update_time, update_by ) VALUES ( &#x27;1628296156785729537&#x27;, &#x27;lonnie.walsh0&#x27;, &#x27;X75170066&#x27;, &#x27;弓远航&#x27;, &#x27;https://robohash.org/nnawetiq.png&#x27;, &#x27;mr.brandonthiel@qq.com&#x27;, true, &#x27;15024489626&#x27;, true, &#x27;2023-02-22T15:30:30.725&#x27;, &#x27;yww&#x27;, &#x27;2023-02-22T15:30:30.725&#x27;, &#x27;yww&#x27; )</span><br></pre></td></tr></table></figure><h3 id="saveBatch的伪批量插入"><a href="#saveBatch的伪批量插入" class="headerlink" title="saveBatch的伪批量插入"></a>saveBatch的伪批量插入</h3><p>使用saveBatch最好先通过<code>@EnableTransactionManagement</code>注解开启<code>mybatis-plus</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">@Override</span></span><br><span class="line"><span class="keyword">public</span> <span class="type">boolean</span> <span class="title function_">insertBatch2</span><span class="params">(List&lt;User&gt; userList)</span> &#123;</span><br><span class="line">    userList = getUsers(<span class="number">10</span>);</span><br><span class="line">    <span class="keyword">return</span> <span class="built_in">this</span>.saveBatch(userList);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>通过<code>p6spy</code>的打印，可以看到执行的其中一条插入语句。</p><figure class="highlight plaintext"><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">Consume Time：0 ms 2023-02-22 15:32:05</span><br><span class="line">Execute SQL：INSERT INTO user ( id, username, password, nickname, avatar, email, sex, phone, status, create_time, create_by, update_time, update_by ) VALUES ( &#x27;1628296554896482305&#x27;, &#x27;aline.aufderhar0&#x27;, &#x27;296752366&#x27;, &#x27;商鑫鹏&#x27;, &#x27;https://robohash.org/zmvciuuf.png&#x27;, &#x27;randalwolfiii@qq.com&#x27;, false, &#x27;15067200564&#x27;, true, &#x27;2023-02-22T15:32:05.643&#x27;, &#x27;yww&#x27;, &#x27;2023-02-22T15:32:05.643&#x27;, &#x27;yww&#x27; )</span><br></pre></td></tr></table></figure><blockquote><p>这里可以发现saveBatch的插入语句和for循环的插入语句是一样的，都是有10条INSERT语句，而不是真正的批量插入，只不过saveBatch是10条SQL语句一起提交，而for循环是10条语句10次提交，这里的效率差距还是很明显的，但由于这样也不算是批量插入，所以saveBatch被称为伪批量插入。</p></blockquote><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">@Override</span></span><br><span class="line"><span class="keyword">public</span> <span class="type">boolean</span> <span class="title function_">insertBatch2</span><span class="params">(List&lt;User&gt; userList)</span> &#123;</span><br><span class="line">    userList = UserUtil.getUsers(<span class="number">10</span>);</span><br><span class="line">    <span class="comment">// 按每次500条数据进行插入</span></span><br><span class="line">    <span class="keyword">return</span> saveBatch(userList, <span class="number">500</span>);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h3 id="批量插入"><a href="#批量插入" class="headerlink" title="批量插入"></a>批量插入</h3><p>这种方案效率是最高的，但是相应的需要去配置一下。</p><ol><li><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><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"> * &lt;p&gt;</span></span><br><span class="line"><span class="comment"> *      批量插入SQL注入器</span></span><br><span class="line"><span class="comment"> * &lt;/p&gt;</span></span><br><span class="line"><span class="comment"> *</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@author</span> yww</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@since</span> 2023/2/22 15:53</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_">InsertBatchSqlInjector</span> <span class="keyword">extends</span> <span class="title class_">DefaultSqlInjector</span> &#123;</span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="keyword">public</span> List&lt;AbstractMethod&gt; <span class="title function_">getMethodList</span><span class="params">(Class&lt;?&gt; mapperClass, TableInfo tableInfo)</span> &#123;</span><br><span class="line">        <span class="comment">// 获取MybatisPlus的自带方法</span></span><br><span class="line">        List&lt;AbstractMethod&gt; methodList = <span class="built_in">super</span>.getMethodList(mapperClass, tableInfo);</span><br><span class="line">        <span class="comment">// 添加自定义批量插入方法，名称为insertBatchSomeColumn</span></span><br><span class="line">        methodList.add(<span class="keyword">new</span> <span class="title class_">InsertBatchSomeColumn</span>());</span><br><span class="line">        <span class="keyword">return</span> methodList;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></li><li><p>将SQL注入器添加到<code>mybatis-plus</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="meta">@Configuration</span></span><br><span class="line"><span class="meta">@EnableTransactionManagement</span></span><br><span class="line"><span class="meta">@MapperScan(&quot;com.yww.demo.mapper&quot;)</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="comment">/**</span></span><br><span class="line"><span class="comment">     * 自定义批量插入 SQL 注入器</span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    <span class="meta">@Bean</span></span><br><span class="line">    <span class="keyword">public</span> InsertBatchSqlInjector <span class="title function_">insertBatchSqlInjector</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_">InsertBatchSqlInjector</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><li><p>在Mapper类中添加方法</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><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</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">interface</span> <span class="title class_">UserMapper</span> <span class="keyword">extends</span> <span class="title class_">BaseMapper</span>&lt;User&gt; &#123;</span><br><span class="line"></span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     * 批量插入 仅适用于mysql</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@param</span> batchList     实体列表</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">@SuppressWarnings(&quot;MybatisMapperMethodInspection&quot;)</span></span><br><span class="line">    <span class="type">int</span> <span class="title function_">insertBatchSomeColumn</span><span class="params">(<span class="meta">@Param(&quot;list&quot;)</span> List&lt;User&gt; batchList)</span>;</span><br><span class="line"></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></li></ol><blockquote><p>注意，insertBatchSomeColumn为内部指定的方法名字，不用在xml中写具体的SQL语句。IDE的警告可以忽略，上述添加的SQL注入器会帮我们注入具体的SQL语句，当然不用这么麻烦的配置，自己去xml中拼接SQL也是可以的。</p></blockquote><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="comment">/**</span></span><br><span class="line"><span class="comment"> * 批量新增数据,自选字段 insert</span></span><br><span class="line"><span class="comment"> * &lt;p&gt; 不同的数据库支持度不一样!!!  只在 mysql 下测试过!!!  只在 mysql 下测试过!!!  只在 mysql 下测试过!!! &lt;/p&gt;</span></span><br><span class="line"><span class="comment"> * &lt;p&gt; 除了主键是 &lt;strong&gt; 数据库自增的未测试 &lt;/strong&gt; 外理论上都可以使用!!! &lt;/p&gt;</span></span><br><span class="line"><span class="comment"> * &lt;p&gt; 如果你使用自增有报错或主键值无法回写到entity,就不要跑来问为什么了,因为我也不知道!!! &lt;/p&gt;</span></span><br><span class="line"><span class="comment"> * &lt;p&gt;</span></span><br><span class="line"><span class="comment"> * 自己的通用 mapper 如下使用:</span></span><br><span class="line"><span class="comment"> * &lt;pre&gt;</span></span><br><span class="line"><span class="comment"> * int insertBatchSomeColumn(List&lt;T&gt; entityList);</span></span><br><span class="line"><span class="comment"> * &lt;/pre&gt;</span></span><br><span class="line"><span class="comment"> * &lt;/p&gt;</span></span><br><span class="line"><span class="comment"> *</span></span><br><span class="line"><span class="comment"> * &lt;li&gt; 注意: 这是自选字段 insert !!,如果个别字段在 entity 里为 null 但是数据库中有配置默认值, insert 后数据库字段是为 null 而不是默认值 &lt;/li&gt;</span></span><br><span class="line"><span class="comment"> *</span></span><br><span class="line"><span class="comment"> * &lt;p&gt;</span></span><br><span class="line"><span class="comment"> * 常用的 &#123;<span class="doctag">@link</span> Predicate&#125;:</span></span><br><span class="line"><span class="comment"> * &lt;/p&gt;</span></span><br><span class="line"><span class="comment"> *</span></span><br><span class="line"><span class="comment"> * &lt;li&gt; 例1: t -&gt; !t.isLogicDelete() , 表示不要逻辑删除字段 &lt;/li&gt;</span></span><br><span class="line"><span class="comment"> * &lt;li&gt; 例2: t -&gt; !t.getProperty().equals(&quot;version&quot;) , 表示不要字段名为 version 的字段 &lt;/li&gt;</span></span><br><span class="line"><span class="comment"> * &lt;li&gt; 例3: t -&gt; t.getFieldFill() != FieldFill.UPDATE) , 表示不要填充策略为 UPDATE 的字段 &lt;/li&gt;</span></span><br><span class="line"><span class="comment"> *</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@author</span> miemie</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@since</span> 2018-11-29</span></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></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="type">int</span> <span class="title function_">insertBatch3</span><span class="params">(List&lt;User&gt; userList)</span> &#123;</span><br><span class="line">    userList = getUsers(<span class="number">10</span>);</span><br><span class="line">    <span class="keyword">return</span> <span class="built_in">this</span>.baseMapper.insertBatchSomeColumn(userList);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>SQL打印为</p><figure class="highlight plaintext"><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">Consume Time：3 ms 2023-02-22 16:04:37</span><br><span class="line">Execute SQL：INSERT INTO user (id,username,password,nickname,avatar,email,sex,phone,status,create_time,create_by,update_time,update_by) VALUES (&#x27;1628304743473872898&#x27;,&#x27;tod.jacobs0&#x27;,&#x27;Y19482857&#x27;,&#x27;房晟睿&#x27;,&#x27;https://robohash.org/fmcgfafy.png&#x27;,&#x27;marileemacejkovic@qq.com&#x27;,false,&#x27;13069884517&#x27;,true,&#x27;2023-02-22T16:04:37.951&#x27;,&#x27;yww&#x27;,&#x27;2023-02-22T16:04:37.951&#x27;,&#x27;yww&#x27;) , (&#x27;1628304743473872899&#x27;,&#x27;leo.wilderman1&#x27;,&#x27;X45343338&#x27;,&#x27;庾瑞霖&#x27;,&#x27;https://robohash.org/calkekge.png&#x27;,&#x27;jefferyarmstrong@qq.com&#x27;,true,&#x27;18603800047&#x27;,true,&#x27;2023-02-22T16:04:37.952&#x27;,&#x27;yww&#x27;,&#x27;2023-02-22T16:04:37.952&#x27;,&#x27;yww&#x27;) , (&#x27;1628304743473872900&#x27;,&#x27;dorris.kutch2&#x27;,&#x27;Y41776964&#x27;,&#x27;胥擎宇&#x27;,&#x27;https://robohash.org/cbrcnhor.png&#x27;,&#x27;darwindickinson@qq.com&#x27;,false,&#x27;15855129426&#x27;,true,&#x27;2023-02-22T16:04:37.952&#x27;,&#x27;yww&#x27;,&#x27;2023-02-22T16:04:37.952&#x27;,&#x27;yww&#x27;) , (&#x27;1628304743473872901&#x27;,&#x27;julio.dubuque3&#x27;,&#x27;529376562&#x27;,&#x27;辛鸿煊&#x27;,&#x27;https://robohash.org/atjuqhwo.png&#x27;,&#x27;annemariejerde@qq.com&#x27;,true,&#x27;13764269293&#x27;,true,&#x27;2023-02-22T16:04:37.952&#x27;,&#x27;yww&#x27;,&#x27;2023-02-22T16:04:37.952&#x27;,&#x27;yww&#x27;) , (&#x27;1628304743473872902&#x27;,&#x27;alvera.gerlach4&#x27;,&#x27;Z05050838&#x27;,&#x27;骆泽洋&#x27;,&#x27;https://robohash.org/kttvknqi.png&#x27;,&#x27;vincenzoortiz@qq.com&#x27;,false,&#x27;18764984449&#x27;,true,&#x27;2023-02-22T16:04:37.952&#x27;,&#x27;yww&#x27;,&#x27;2023-02-22T16:04:37.952&#x27;,&#x27;yww&#x27;) , (&#x27;1628304743473872903&#x27;,&#x27;robert.walter5&#x27;,&#x27;Z47884351&#x27;,&#x27;茅志强&#x27;,&#x27;https://robohash.org/gigofxue.png&#x27;,&#x27;missjosphinemarquardt@qq.com&#x27;,false,&#x27;16550365693&#x27;,true,&#x27;2023-02-22T16:04:37.952&#x27;,&#x27;yww&#x27;,&#x27;2023-02-22T16:04:37.952&#x27;,&#x27;yww&#x27;) , (&#x27;1628304743473872904&#x27;,&#x27;curtis.considine6&#x27;,&#x27;X36270777&#x27;,&#x27;满明哲&#x27;,&#x27;https://robohash.org/zkakbsnh.png&#x27;,&#x27;mitzidaugherty@qq.com&#x27;,false,&#x27;18605996014&#x27;,true,&#x27;2023-02-22T16:04:37.952&#x27;,&#x27;yww&#x27;,&#x27;2023-02-22T16:04:37.952&#x27;,&#x27;yww&#x27;) , (&#x27;1628304743473872905&#x27;,&#x27;stewart.mayert7&#x27;,&#x27;Z27480881&#x27;,&#x27;舒文轩&#x27;,&#x27;https://robohash.org/wnmekekg.png&#x27;,&#x27;pierreconsidine@qq.com&#x27;,false,&#x27;13988711400&#x27;,true,&#x27;2023-02-22T16:04:37.952&#x27;,&#x27;yww&#x27;,&#x27;2023-02-22T16:04:37.952&#x27;,&#x27;yww&#x27;) , (&#x27;1628304743473872906&#x27;,&#x27;winnie.mills8&#x27;,&#x27;Z66334442&#x27;,&#x27;刘擎苍&#x27;,&#x27;https://robohash.org/snbyikas.png&#x27;,&#x27;taneshapagac@qq.com&#x27;,true,&#x27;19603769365&#x27;,true,&#x27;2023-02-22T16:04:37.952&#x27;,&#x27;yww&#x27;,&#x27;2023-02-22T16:04:37.952&#x27;,&#x27;yww&#x27;) , (&#x27;1628304743473872907&#x27;,&#x27;lanita.walker9&#x27;,&#x27;Y43021930&#x27;,&#x27;边鸿煊&#x27;,&#x27;https://robohash.org/wgypkmjj.png&#x27;,&#x27;dorseyrath@qq.com&#x27;,true,&#x27;17297354287&#x27;,true,&#x27;2023-02-22T16:04:37.952&#x27;,&#x27;yww&#x27;,&#x27;2023-02-22T16:04:37.952&#x27;,&#x27;yww&#x27;)</span><br></pre></td></tr></table></figure><blockquote><p>可以看到这个SQL的格式为</p><p>INSERT INTO user (..) VALUES (…),(…),(…),(…)</p><p>这种格式才算是批量插入，而且效率会高上很多。</p></blockquote><p>同理，一次性不建议插入太多条数据，所以还是需要分片。<code>insertBatchSomeColumn</code>这个mapper方法我找了一下源码，好像没见到自带的分片方法，所以需要手动分片然后进行插入。操作如下。</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><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">@Override</span></span><br><span class="line"><span class="meta">@Transactional(rollbackFor = Exception.class)</span></span><br><span class="line">   <span class="keyword">public</span> <span class="type">int</span> <span class="title function_">insertBatch3</span><span class="params">(List&lt;User&gt; userList)</span> &#123;</span><br><span class="line">       userList = UserUtil.getUsers(<span class="number">10</span>);</span><br><span class="line">       <span class="comment">// 按每次500进行插入</span></span><br><span class="line">       List&lt;List&lt;User&gt;&gt; list = ListUtil.partition(userList, <span class="number">500</span>);</span><br><span class="line">       <span class="type">int</span> <span class="variable">res</span> <span class="operator">=</span> <span class="number">0</span>;</span><br><span class="line">       <span class="keyword">for</span> (List&lt;User&gt; i : list) &#123;</span><br><span class="line">           res += <span class="built_in">this</span>.baseMapper.insertBatchSomeColumn(i);</span><br><span class="line">       &#125;</span><br><span class="line">       <span class="keyword">return</span> res;</span><br><span class="line">   &#125;</span><br></pre></td></tr></table></figure><h3 id="总结"><a href="#总结" class="headerlink" title="总结"></a>总结</h3><p>使用第三种发放是效率最高的，所以建议使用第三种，当然嫌麻烦配置，直接用第二种也可以。毕竟数据量不是特别大，差距可以忽略。</p><blockquote><p>尽量不要使用for循环的操作进行数据批量操作，消耗的时间是很多的。</p><p>所以之后的批量操作，就不记录使用for循环的方案了。</p></blockquote><h1 id="删除"><a href="#删除" class="headerlink" title="删除"></a>删除</h1><h2 id="根据ID删除单条数据"><a href="#根据ID删除单条数据" class="headerlink" title="根据ID删除单条数据"></a>根据ID删除单条数据</h2><p>直接调用API即可。</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></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="type">boolean</span> <span class="title function_">deleteById</span><span class="params">(String userId)</span> &#123;</span><br><span class="line">    <span class="comment">// 有必要的话可以先查出数据，进行处理后在删除</span></span><br><span class="line">    <span class="comment">// entity = select(userId);   this.removeById(entity)</span></span><br><span class="line">    <span class="built_in">this</span>.removeById(userId);</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><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></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="type">boolean</span> <span class="title function_">deleteByCondition</span><span class="params">(User user)</span> &#123;</span><br><span class="line">    <span class="keyword">return</span> <span class="built_in">this</span>.remove(Wrappers.lambdaQuery(user));</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><blockquote><p>这个方法，这里只是用来记录一下，我感觉这个方法十分的危险，实际开发建议不要这样写，当user为空，或者是里面的字段全是空值，就会导致语句变成</p><p>DELETE FROM user</p><p>这样的SQL会导致全表数据删除，所以尽量使用ID进行删除。</p><p>实在需要用到这种方法，请先判定传入的实体对象条件不为空，而且里面的属性不能全部为空，避免出现全表删除的情况。</p><p>或者可以参考补充说明，添加防全表更新与删除插件的插件。</p></blockquote><h2 id="根据ID列表批量删除"><a href="#根据ID列表批量删除" class="headerlink" title="根据ID列表批量删除"></a>根据ID列表批量删除</h2><p>这里有两个方法，对应的SQL语句不太一样。</p><h3 id="removeByIds方法"><a href="#removeByIds方法" class="headerlink" title="removeByIds方法"></a>removeByIds方法</h3><p>直接调用API即可。</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></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="type">boolean</span> <span class="title function_">deleteByIds</span><span class="params">(List&lt;String&gt; userIds)</span> &#123;</span><br><span class="line">    <span class="keyword">return</span> <span class="built_in">this</span>.removeByIds(userIds);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>对应的SQL语句如下。</p><figure class="highlight plaintext"><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">Consume Time：0 ms 2023-02-23 17:07:41</span><br><span class="line">Execute SQL：DELETE FROM user WHERE id IN ( &#x27;1&#x27; , &#x27;2&#x27; , &#x27;3&#x27; , &#x27;4&#x27; )</span><br></pre></td></tr></table></figure><p>同样，尽量对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="meta">@Override</span></span><br><span class="line"><span class="meta">@Transactional(rollbackFor = Exception.class)</span></span><br><span class="line">   <span class="keyword">public</span> <span class="type">boolean</span> <span class="title function_">deleteByIds</span><span class="params">(List&lt;String&gt; userIds)</span> &#123;</span><br><span class="line">       <span class="comment">// 按每次500条数据进行操作</span></span><br><span class="line">       List&lt;List&lt;String&gt;&gt; list = ListUtil.partition(userIds, <span class="number">1000</span>);</span><br><span class="line">       <span class="type">boolean</span> <span class="variable">res</span> <span class="operator">=</span> <span class="literal">true</span>;</span><br><span class="line">       <span class="keyword">for</span> (List&lt;String&gt; i : list) &#123;</span><br><span class="line">           res = res &amp;&amp; <span class="built_in">this</span>.removeByIds(i);</span><br><span class="line">       &#125;</span><br><span class="line">       <span class="keyword">return</span> <span class="built_in">this</span>.removeByIds(userIds);</span><br><span class="line">   &#125;</span><br></pre></td></tr></table></figure><h3 id="removeBatchByIds方法"><a href="#removeBatchByIds方法" class="headerlink" title="removeBatchByIds方法"></a>removeBatchByIds方法</h3><p>直接调用API即可。</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></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="type">boolean</span> <span class="title function_">deleteBatchByIds</span><span class="params">(List&lt;String&gt; userIds)</span> &#123;</span><br><span class="line">    <span class="comment">// 每次最多进行1000条数据删除</span></span><br><span class="line">    <span class="keyword">return</span> <span class="built_in">this</span>.removeBatchByIds(userIds, <span class="number">1000</span>);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>对应的SQL语句如下。</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><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line">Consume Time：1 ms 2023-02-23 17:11:09</span><br><span class="line">Execute SQL：DELETE FROM user WHERE id=&#x27;1&#x27;</span><br><span class="line"></span><br><span class="line">Consume Time：0 ms 2023-02-23 17:11:09</span><br><span class="line">Execute SQL：DELETE FROM user WHERE id=&#x27;2&#x27;</span><br><span class="line"></span><br><span class="line">Consume Time：0 ms 2023-02-23 17:11:09</span><br><span class="line">Execute SQL：DELETE FROM user WHERE id=&#x27;3&#x27;</span><br><span class="line"></span><br><span class="line">Consume Time：0 ms 2023-02-23 17:11:09</span><br><span class="line">Execute SQL：DELETE FROM user WHERE id=&#x27;4&#x27;</span><br></pre></td></tr></table></figure><p>这个有点类似与<code>saveBatch</code>的操作，多次删除一次提交。</p><h3 id="总结-1"><a href="#总结-1" class="headerlink" title="总结"></a>总结</h3><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></pre></td><td class="code"><pre><span class="line">com.yww.demo.util.TestController         : 此处测试的两种方法，测试删除的数据数量为10600</span><br><span class="line">com.yww.demo.util.TestController         : deleteByIds测试时间为1349毫秒</span><br><span class="line">com.yww.demo.util.TestController         : deleteBatchByIds测试时间为12956毫秒</span><br></pre></td></tr></table></figure><blockquote><p>这两个差距还算很明显的了。测试中每次删除1000条数据，要是按更大的长度进行分片，我觉得这两种的方法差距会更加的大，所以尽量使用<code>removeByIds</code>这种方法。</p></blockquote><h2 id="根据其他条件批量删除"><a href="#根据其他条件批量删除" class="headerlink" title="根据其他条件批量删除"></a>根据其他条件批量删除</h2><p>这里以用户名举例，即传入用户名列表，批量进行删除。</p><p>由于<code>MybatisPlus</code>没有对应的API，所以需要手动去编写Mapper。</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><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">@Override</span></span><br><span class="line"><span class="meta">@Transactional(rollbackFor = Exception.class)</span></span><br><span class="line"><span class="keyword">public</span> <span class="type">int</span> <span class="title function_">deleteBatchByUsernames</span><span class="params">(List&lt;String&gt; usernames)</span> &#123;</span><br><span class="line">    <span class="comment">// 按每次1000条数据进行操作</span></span><br><span class="line">    List&lt;List&lt;String&gt;&gt; list = ListUtil.partition(usernames, <span class="number">1000</span>);</span><br><span class="line">    <span class="type">int</span> <span class="variable">count</span> <span class="operator">=</span> <span class="number">0</span>;</span><br><span class="line">    <span class="keyword">for</span> (List&lt;String&gt; i : list) &#123;</span><br><span class="line">        count = count + baseMapper.deleteBatchByUsernames(i);</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">return</span> count;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p><code>deleteBatchByUsernames</code>方法的mapper可以参考批量删除ID的方法进行编写，这里就模仿<code>removeByIds</code>对应的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></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;deleteBatchByUsernames&quot;</span> <span class="attr">parameterType</span>=<span class="string">&quot;java.util.List&quot;</span>&gt;</span></span><br><span class="line">    DELETE FROM user WHERE username in</span><br><span class="line">    <span class="tag">&lt;<span class="name">foreach</span> <span class="attr">collection</span>=<span class="string">&quot;usernames&quot;</span> <span class="attr">index</span>=<span class="string">&quot;index&quot;</span> <span class="attr">item</span>=<span class="string">&quot;username&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;username&#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><h1 id="更新"><a href="#更新" class="headerlink" title="更新"></a>更新</h1><h2 id="根据ID更新数据"><a href="#根据ID更新数据" class="headerlink" title="根据ID更新数据"></a>根据ID更新数据</h2><p>直接调用API即可。</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">@Operation(summary = &quot;根据ID更新数据&quot;)</span></span><br><span class="line"><span class="meta">@PutMapping(&quot;/updateById&quot;)</span></span><br><span class="line"><span class="keyword">public</span> Result&lt;?&gt; updateById(<span class="meta">@RequestBody</span> User user) &#123;</span><br><span class="line">    <span class="keyword">if</span> (service.updateById(user)) &#123;</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 class="keyword">else</span> &#123;</span><br><span class="line">        <span class="keyword">return</span> Result.failure(<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><h2 id="根据ID批量更新数据"><a href="#根据ID批量更新数据" class="headerlink" title="根据ID批量更新数据"></a>根据ID批量更新数据</h2><p>直接调用API即可。</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">@Operation(summary = &quot;根据ID批量更新数据&quot;)</span></span><br><span class="line"><span class="meta">@PutMapping(&quot;/updateByIds&quot;)</span></span><br><span class="line"><span class="keyword">public</span> Result&lt;?&gt; updateByIds(<span class="meta">@RequestBody</span> List&lt;User&gt; users) &#123;</span><br><span class="line">    <span class="keyword">if</span> (service.updateBatchById(users, <span class="number">1000</span>)) &#123;</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 class="keyword">else</span> &#123;</span><br><span class="line">        <span class="keyword">return</span> Result.failure(<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><h2 id="使用UpdateWrapper更新"><a href="#使用UpdateWrapper更新" class="headerlink" title="使用UpdateWrapper更新"></a>使用UpdateWrapper更新</h2><p>使用<code>UpdateWrapper</code>的条件去定位数据，更新条件需要设置sqlset。</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></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="type">boolean</span> <span class="title function_">update1</span><span class="params">(User user)</span> &#123;</span><br><span class="line">    <span class="comment">// 将名字设置为WHERE条件，将状态设置为SET语句</span></span><br><span class="line">    <span class="keyword">return</span> <span class="built_in">this</span>.update(</span><br><span class="line">        Wrappers.lambdaUpdate(User.class).eq(User::getNickname, user.getNickname())</span><br><span class="line">                .set(User::getStatus, user.getStatus())</span><br><span class="line">    );</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h2 id="使用Wrapper更新"><a href="#使用Wrapper更新" class="headerlink" title="使用Wrapper更新"></a>使用Wrapper更新</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></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="type">boolean</span> <span class="title function_">update2</span><span class="params">(User user)</span> &#123;</span><br><span class="line">    <span class="keyword">return</span> <span class="built_in">this</span>.update(user, </span><br><span class="line">            Wrappers.lambdaQuery(User.class).eq(User::getNickname, user.getNickname())</span><br><span class="line">    );</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h2 id="saveOrUpdate"><a href="#saveOrUpdate" class="headerlink" title="saveOrUpdate"></a>saveOrUpdate</h2><p>这个方法是<code>MybatisPlus</code>里的一个方法，顾名思义，保存或者是更新，先去数据库根据ID查询是否存在该条数据，没有就插入数据，有的话就更新数据。大致用法和update或者是save差不多。</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><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">// TableId 注解存在更新记录，否插入一条记录</span></span><br><span class="line"><span class="type">boolean</span> <span class="title function_">saveOrUpdate</span><span class="params">(T entity)</span>;</span><br><span class="line"><span class="comment">// 根据updateWrapper尝试更新，否继续执行saveOrUpdate(T)方法</span></span><br><span class="line"><span class="type">boolean</span> <span class="title function_">saveOrUpdate</span><span class="params">(T entity, Wrapper&lt;T&gt; updateWrapper)</span>;</span><br><span class="line"><span class="comment">// 批量修改插入</span></span><br><span class="line"><span class="type">boolean</span> <span class="title function_">saveOrUpdateBatch</span><span class="params">(Collection&lt;T&gt; entityList)</span>;</span><br><span class="line"><span class="comment">// 批量修改插入</span></span><br><span class="line"><span class="type">boolean</span> <span class="title function_">saveOrUpdateBatch</span><span class="params">(Collection&lt;T&gt; entityList, <span class="type">int</span> batchSize)</span>;</span><br></pre></td></tr></table></figure><blockquote><p>这方法确实也挺好用的，不过是更新和插入的混合方法，有点违和就是了，所以这里就不怎么探讨了。</p></blockquote><h1 id="查询"><a href="#查询" class="headerlink" title="查询"></a>查询</h1><h2 id="根据ID查询数据"><a href="#根据ID查询数据" class="headerlink" title="根据ID查询数据"></a>根据ID查询数据</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></pre></td><td class="code"><pre><span class="line"><span class="meta">@Operation(summary = &quot;根据ID获取数据&quot;)</span></span><br><span class="line"><span class="meta">@GetMapping(&quot;/getById/&#123;userId&#125;&quot;)</span></span><br><span class="line"><span class="keyword">public</span> Result&lt;?&gt; getById(<span class="meta">@PathVariable</span> Integer userId) &#123;</span><br><span class="line">    <span class="keyword">return</span> Result.success(service.getById(userId));</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><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></pre></td><td class="code"><pre><span class="line"><span class="meta">@Override</span></span><br><span class="line"><span class="keyword">public</span> User <span class="title function_">getByUsername</span><span class="params">(String username)</span> &#123;</span><br><span class="line">    <span class="keyword">return</span> <span class="built_in">this</span>.getOne(</span><br><span class="line">            Wrappers.lambdaQuery(User.class).eq(User::getUsername, username)</span><br><span class="line">    );</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><blockquote><p>这个方法只会返回一条数据，如果查询出多条数据会抛出异常，若是希望查出多条数据不抛出异常，可以自行设置。</p><p><code>getOne(queryWrapper, false)</code></p></blockquote><h2 id="根据条件查询数据"><a href="#根据条件查询数据" class="headerlink" title="根据条件查询数据"></a>根据条件查询数据</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></pre></td><td class="code"><pre><span class="line"><span class="meta">@Override</span></span><br><span class="line"><span class="keyword">public</span> List&lt;User&gt; <span class="title function_">getByUser</span><span class="params">(User user)</span> &#123;</span><br><span class="line">    <span class="comment">// this.list() 查询所有数据</span></span><br><span class="line">    <span class="keyword">return</span> <span class="built_in">this</span>.list(</span><br><span class="line">            Wrappers.lambdaQuery(User.class).eq(User::getUsername, user.getUsername()).eq(User::getNickname, user.getNickname())</span><br><span class="line">    );</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h2 id="根据ID批量查询"><a href="#根据ID批量查询" class="headerlink" title="根据ID批量查询"></a>根据ID批量查询</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></pre></td><td class="code"><pre><span class="line"><span class="meta">@Operation(summary = &quot;根据ID批量查询&quot;)</span></span><br><span class="line"><span class="meta">@GetMapping(&quot;/getByIds&quot;)</span></span><br><span class="line"><span class="keyword">public</span> Result&lt;?&gt; getByUser(<span class="meta">@RequestBody</span> List&lt;String&gt; userIds) &#123;</span><br><span class="line">    <span class="keyword">return</span> Result.success(service.listByIds(userIds));</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h2 id="根据其他条件批量查询"><a href="#根据其他条件批量查询" class="headerlink" title="根据其他条件批量查询"></a>根据其他条件批量查询</h2><p>这里需要自己手写SQL实现才行了，SQL语句和根据其他条件批量删除的差不多，也是通过<code>IN</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="comment">/**</span></span><br><span 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> usernames 用户名列表</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;User&gt; <span class="title function_">listByUserNames</span><span class="params">(<span class="meta">@Param(&quot;usernames&quot;)</span> List&lt;String&gt; usernames)</span>;</span><br></pre></td></tr></table></figure><figure class="highlight xml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></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;listByUserNames&quot;</span> <span class="attr">resultType</span>=<span class="string">&quot;com.yww.demo.entity.User&quot;</span>&gt;</span></span><br><span class="line">    SELECT * FROM user WHERE username IN</span><br><span class="line">    <span class="tag">&lt;<span class="name">foreach</span> <span class="attr">collection</span>=<span class="string">&quot;usernames&quot;</span> <span class="attr">index</span>=<span class="string">&quot;index&quot;</span> <span class="attr">item</span>=<span class="string">&quot;username&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;username&#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></pre></td></tr></table></figure><h2 id="简单分页查询"><a href="#简单分页查询" class="headerlink" title="简单分页查询"></a>简单分页查询</h2><p><code>MybatisPlus</code>提供了一个分页插件，需要先去配置，配置参考上述的补充说明。</p><p><code>MybatisPlus</code>的分页方法需要<code>IPage</code>这分页模型，可以自己去实现这个分页模型，也可以使用官方提供的<code>Page</code>类（<code>Page</code>继承了<code>IPage</code>，实现了一个简单的分页模型）。<code>Page</code>的对象创建可以直接使用其<code>Page.of</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">@Operation(summary = &quot;简单分页查询&quot;)</span></span><br><span class="line"><span class="meta">@GetMapping(&quot;/page/&#123;current&#125;/&#123;size&#125;&quot;)</span></span><br><span class="line"><span class="keyword">public</span> Result&lt;?&gt; page(<span class="meta">@PathVariable</span> Integer current,</span><br><span class="line">                      <span class="meta">@PathVariable</span> Integer size) &#123;</span><br><span class="line">    Page&lt;User&gt; ipage = Page.of(current, size);</span><br><span class="line">    <span class="keyword">return</span> Result.success(service.page(ipage));</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><blockquote><p>注意，<code>MybatisPlus</code>的分页方法是会先查询所有数据的数量，然后才会进行<code>LIMIT</code>分页，也就是说会有两条SQL语句，从打印的SQL就可以看到。</p><p> Consume Time：19 ms<br> Execute SQL：SELECT COUNT(*) AS total FROM user</p><p> Consume Time：3 ms<br> Execute SQL：SELECT id,username,password,nickname,avatar,email,sex,phone,status,create_time,create_by,update_time,update_by FROM user LIMIT 10</p><p>如果用不到总记录数，不想执行<code>count</code>查询，可以通过<code>Page.of(current, size, false)</code>方法去关掉。</p></blockquote><h2 id="根据条件分页查询"><a href="#根据条件分页查询" class="headerlink" title="根据条件分页查询"></a>根据条件分页查询</h2><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></pre></td><td class="code"><pre><span class="line"><span class="meta">@Operation(summary = &quot;根据条件分页查询（查询性别）&quot;)</span></span><br><span class="line"><span class="meta">@GetMapping(&quot;/page/&#123;current&#125;/&#123;size&#125;/&#123;sex&#125;&quot;)</span></span><br><span class="line"><span class="keyword">public</span> Result&lt;?&gt; page(<span class="meta">@PathVariable</span> Integer current,</span><br><span class="line">                      <span class="meta">@PathVariable</span> Integer size,</span><br><span class="line">                      <span class="meta">@PathVariable</span> Boolean sex) &#123;</span><br><span class="line">    Page&lt;User&gt; ipage = Page.of(current, size);</span><br><span class="line">    LambdaQueryWrapper&lt;User&gt; wrapper = Wrappers.lambdaQuery(User.builder().sex(sex).build());</span><br><span class="line">    <span class="keyword">return</span> Result.success(service.page(ipage, wrapper));</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h1 id="篇末"><a href="#篇末" class="headerlink" title="篇末"></a>篇末</h1><p>这篇文章主要是介绍一些<code>MybatisPlus</code>的API吧，其实刚刚开始写的时候，是想记录一些常用的用法的，但是后面写着写着，发现其实都是在写官方的API如何使用（确实这个项目考虑的很周全）。后来又怕别人看得更明白些，又写了很多介绍类和使用的注意事项。emm，怎么说的，是写的有些乱了，本来就是给自己看到，写写感觉就偏题了hh，不过还可以吧，将就看一下。</p>]]></content:encoded>
            
            
            <category domain="https://yww52.com/categories/%E6%88%91%E7%9A%84%E6%8A%80%E6%9C%AF%E5%AD%A6%E4%B9%A0%E7%89%A9%E8%AF%AD%E6%9E%9C%E7%84%B6%E6%9C%89%E9%97%AE%E9%A2%98/">我的技术学习物语果然有问题</category>
            
            <category domain="https://yww52.com/categories/%E6%88%91%E7%9A%84%E6%8A%80%E6%9C%AF%E5%AD%A6%E4%B9%A0%E7%89%A9%E8%AF%AD%E6%9E%9C%E7%84%B6%E6%9C%89%E9%97%AE%E9%A2%98/Spring/">Spring</category>
            
            
            
            <comments>https://yww52.com/posts/4e5ad84e/#disqus_thread</comments>
            
        </item>
        
        <item>
            <title>2022年度总结</title>
            <link>https://yww52.com/posts/622a5765/</link>
            <guid>https://yww52.com/posts/622a5765/</guid>
            <pubDate>Sat, 31 Dec 2022 16:00:00 GMT</pubDate>
            
            <description>2022年度总结</description>
            
            
            
            <content:encoded><![CDATA[<img src="https://img.yww52.com/2023/1/2023-1-1/top_img.jpg" alt="2022年度总结" style="max-width:100%;height:auto;margin-bottom:15px;"
  /><h1 id="简介"><a href="#简介" class="headerlink" title="简介"></a>简介</h1><p>​    刚打完游戏，发现2022年已经过去了。正好我这夜猫子也睡不着，就赶着2023年年初，来写了这篇年度总结。</p><blockquote><p>为什么2021年没有写？其实2022年1月1日的时候，我也是刚和朋友打完游戏，也差不多这个点，我已经写了一篇2021年的年度总结，但是后面写完之后，不记得为什么就删掉了，没发出来，所以就没了，也不想重新写了。</p></blockquote><p>这次没有鸽，肯定写出来hh。</p><h1 id="生活"><a href="#生活" class="headerlink" title="生活"></a>生活</h1><p>​    2022年最重要的事应该是我大学毕业了吧。毕业的时候原本想写个大学四年的总结的，后面想想算了，在大学里也没有什么特别的事情，像很多人一样，平平凡凡的上了个大学，平平凡凡的大学毕业。</p><p>​    其实大三大四过得不是很好，成天感觉到很焦虑，我都感觉心理状态有些亚健康了。每天都是上课，考试，刷题，还有那毕设的破事，大学后面两年其实还是有些遗憾的。不过应该也是我太懒了，学习的心情也没有大一大二那两年这么积极了，每天看那两三小时题目，效率也不高，所以后面也没有什么好结果，有些摆烂了。</p><p>​    所以毕业后，就疯玩了一两个月后才找了个班上。</p><h1 id="coding"><a href="#coding" class="headerlink" title="coding"></a>coding</h1><p>2022年前期毫无开发的心情，所以也没开发什么有趣，好用的东西。上班之后，感觉到很无聊，所以有了很多想做的东西，也捡起了很多之前想弄有没有弄好的东西，继续努力写吧。</p><p>其实有些项目拖了很久没写好（拖延症后期的人是这样的），总是想弄得最好，用很多很新的技术，有些时候也增加了很多学习成本，不过毕竟不是很急的东西，都是兴趣爱好，所以就拖着慢慢写了。</p><p>就像是这个博客，用着hexo来搭建的，也不是说不好用，但是总是想自己写一个，所以就会考虑UI设计，学习前端项目，想要搭建一个自己的服务器，总之很多需要考虑的东西，还有自己很懒，所以就写的很慢了。（我这不算找借口吧hhh）</p><blockquote><p>虽然工作也是coding，但是我不是很想写到这里，我还记得高中的时候和朋友讨论的一个话题，当爱好变成工作的时候，爱好还能一直是爱好吗？这个问题其实我回答不出，等我以后讨厌了，可能我才能回答得出来。所以我不是很喜欢把工作和爱好联系起来，工作是工作，爱好是爱好，所以不在这里讨论工作了。</p></blockquote><p>今年学了很多技术，比如</p><ol><li>springcloud</li><li>typescript</li><li>vue3</li><li>….</li></ol><p>也加深了很多东西的理解，有些东西还是在用到的时候，才会发现这其实有很多讲究，没有想象中的那么简单。</p><p>为了更好的学习，在打折的时候，我还买了很多相关的书。</p><ol><li>MySQL必知必会</li><li>漫画算法2</li><li>编码</li><li>图解Java多线程设计模式</li><li>Redis深度历险：核心原理与应用实践</li><li>现代操作系统</li><li>图解TCP/IP</li><li>labuladong的算法小抄</li><li>图解HTTP</li><li>操作系统导论</li><li>逆流而上</li><li>技术之瞳</li><li>深入理解Java虚拟机</li><li>Java并发之美</li><li>Java编程思想</li><li>重学Java设计模式</li><li>数据结构与算法之美</li></ol><p>其实这些书我都有电子版，不过买纸质版，也算另一种督促我看书的方式，毕竟不是花钱的电子书，就会看的没有那么认真了hh。</p><blockquote><p>别问看了多少，问就是正在看着，没全部看完。</p></blockquote><h1 id="游戏"><a href="#游戏" class="headerlink" title="游戏"></a>游戏</h1><p>游戏方面的话，还是和之前一样，玩来玩去还是那几个，对很多游戏都不感兴趣，不过我觉得也够了。</p><ol><li>英雄联盟</li><li>APEX</li><li>星际争霸2</li><li>公主连结R</li></ol><p>移动端现在就只玩公主连结了，之前会坑了战双一段时间，但是后面没这么多精力又再次退坑了。我还是喜欢像公主连结这种养生的游戏，虽然公会战的几天还是挺累的，不过毕竟一个月一次，还算是能接受。</p><p>其实我不怎么玩手游了，确实缺了点意思，但是也不舍得公会里的大家，所以拖着拖着，公主连结也玩了两年多了。在群里看到很多人都退坑了，也还是挺失落的，不过这游戏确实也就这样了，祝各位有缘再见吧。</p><h1 id="关于博客"><a href="#关于博客" class="headerlink" title="关于博客"></a>关于博客</h1><p>大三大四后，博客确实写得少了，没有一开始这么积极了，我也看到了友联里的很多人，虽然都在维护博客，但是发表的文章也肉眼可见的变少，甚至有些都不发了。前些天在github上看到一个记录独立博客的项目，确实挺有感慨的，地址如下。</p><blockquote><p> <a href="https://github.com/timqian/chinese-independent-blogs">https://github.com/timqian/chinese-independent-blogs</a></p></blockquote><p>当初我折腾这个博客的时候花了很多心思，也买了很久<code>yww52.com</code>的域名，现在喜欢折腾博客的人确实变少了很多，也有点感概吧。</p><p>今年发现了一些人的博客，看到他们充满生活气息的文章，也让我产生写东西的感觉了。</p><p>今年写得很少，大多都是在总结用法，写得少的原因大概有几个。</p><ol><li>有些不知道写什么了。</li><li>写东西就会想写最好，所以花的时间也很多。</li><li>想放弃hexo这个博客框架了，自己写的博客也没写好。</li><li>确实事情比较多，耗精力。</li></ol><h1 id="APP年度总结"><a href="#APP年度总结" class="headerlink" title="APP年度总结"></a>APP年度总结</h1><p>每个APP都有年度总结，这里相当于记录一下吧。</p><h2 id="QQ音乐"><a href="#QQ音乐" class="headerlink" title="QQ音乐"></a>QQ音乐</h2><p>今年没有上一年听歌听得这么多了，我发现一旦听歌，精力就不行，我总会去注意歌词，做的事情也会效率很低，所以听的就没这么多了。又是真夜陪伴的一年hh。</p><div class="gallery-container" data-type="data" data-button="" data-limit="10" data-first="10">    <div class="gallery-items">[{"url":"https://img.yww52.com/2023/1/2023-1-1/1.jpg","alt":"","title":""},{"url":"https://img.yww52.com/2023/1/2023-1-1/2.jpg","alt":"","title":""},{"url":"https://img.yww52.com/2023/1/2023-1-1/3.jpg","alt":"","title":""},{"url":"https://img.yww52.com/2023/1/2023-1-1/4.jpg","alt":"","title":""},{"url":"https://img.yww52.com/2023/1/2023-1-1/5.jpg","alt":"","title":""},{"url":"https://img.yww52.com/2023/1/2023-1-1/6.jpg","alt":"","title":""},{"url":"https://img.yww52.com/2023/1/2023-1-1/7.jpg","alt":"","title":""}]</div>  </div><h2 id="bilibili"><a href="#bilibili" class="headerlink" title="bilibili"></a>bilibili</h2><div class="gallery-container" data-type="data" data-button="" data-limit="10" data-first="10">    <div class="gallery-items">[{"url":"https://img.yww52.com/2023/1/2023-1-1/8.jpg","alt":"","title":""},{"url":"https://img.yww52.com/2023/1/2023-1-1/9.jpg","alt":"","title":""},{"url":"https://img.yww52.com/2023/1/2023-1-1/10.jpg","alt":"","title":""},{"url":"https://img.yww52.com/2023/1/2023-1-1/11.jpg","alt":"","title":""},{"url":"https://img.yww52.com/2023/1/2023-1-1/12.jpg","alt":"","title":""},{"url":"https://img.yww52.com/2023/1/2023-1-1/13.jpg","alt":"","title":""},{"url":"https://img.yww52.com/2023/1/2023-1-1/14.jpg","alt":"","title":""},{"url":"https://img.yww52.com/2023/1/2023-1-1/15.jpg","alt":"","title":""},{"url":"https://img.yww52.com/2023/1/2023-1-1/16.jpg","alt":"","title":""},{"url":"https://img.yww52.com/2023/1/2023-1-1/17.jpg","alt":"","title":""}]</div>  </div><p>自从国家对动漫的政策变了之后，就很少在B站看番了，大多都是学习和游戏。</p><blockquote><p>看看那个虫族教程，我看了多少遍，谁说学习游戏不是学习，我还是很好学的hh，不得不说一句星际争霸真难玩。扯到星际争霸，就想到暴雪了，该死的暴雪，可惜了这么多好游戏。</p></blockquote><h1 id="2023"><a href="#2023" class="headerlink" title="2023"></a>2023</h1><p>2023年就希望自己多看点书，多学习一点东西，多做一点项目，多写点文章，多勤奋一点吧。也没有很大的目标，没有很大的期待，可能我就是一条咸鱼吧。</p>]]></content:encoded>
            
            
            <category domain="https://yww52.com/categories/%E4%B8%AA%E4%BA%BA%E6%80%BB%E7%BB%93/">个人总结</category>
            
            
            
            <comments>https://yww52.com/posts/622a5765/#disqus_thread</comments>
            
        </item>
        
        <item>
            <title>SpringBoot部署启动</title>
            <link>https://yww52.com/posts/e31003e0/</link>
            <guid>https://yww52.com/posts/e31003e0/</guid>
            <pubDate>Tue, 27 Sep 2022 16:00:00 GMT</pubDate>
            
            
            
            
            
            <description></description>
            
            
            
            
            
            <content:encoded><![CDATA[<img src="https://img.yww52.com/2022/09/2022-9-28top_img.jpg" alt="SpringBoot部署启动" style="max-width:100%;height:auto;margin-bottom:15px;"
  /><h1 id="序言"><a href="#序言" class="headerlink" title="序言"></a>序言</h1><p>在开发中，经常需要部署SpringBoot服务，所以来简单记录一下我比较常用的一些部署方式。</p><h1 id="简单粗暴的脚本"><a href="#简单粗暴的脚本" class="headerlink" title="简单粗暴的脚本"></a>简单粗暴的脚本</h1><p>这种方法其实就是直接使用<code>java -jar</code>命令进行启动Jar包，所以得需要系统安装好Java的环境，然后打包好服务</p><figure class="highlight sh"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><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">#!/bin/bash</span></span><br><span class="line"></span><br><span class="line">JAVAHOME=<span class="variable">$JAVA_HOME</span></span><br><span class="line"></span><br><span class="line"><span class="built_in">echo</span> <span class="string">&quot;--------start--------&quot;</span></span><br><span class="line"></span><br><span class="line">PID=$(ps -ef|grep app.jar  |grep -v grep|awk <span class="string">&#x27;&#123;print $2&#125;&#x27;</span>)</span><br><span class="line"></span><br><span class="line"><span class="keyword">if</span> [ ! <span class="variable">$PID</span> ]; <span class="keyword">then</span></span><br><span class="line">    <span class="built_in">nohup</span> java -jar -XX:MetaspaceSize=128m -XX:MaxMetaspaceSize=256m -Xms512m -Xmn768m -Xmx1024m -Xss256k -XX:SurvivorRatio=8 -XX:+UseConcMarkSweepGC app.jar &gt; app.log 2&gt;&amp;1 &amp;</span><br><span class="line">    <span class="built_in">echo</span> <span class="string">&quot;--------------start success-----------&quot;</span></span><br><span class="line">    <span class="built_in">exit</span></span><br><span class="line"><span class="keyword">else</span></span><br><span class="line">    <span class="built_in">kill</span> -9 <span class="variable">$&#123;PID&#125;</span></span><br><span class="line">    <span class="built_in">echo</span> <span class="string">&quot;--------------kill success-----------&quot;</span></span><br><span class="line">    <span class="built_in">echo</span> <span class="string">&quot;---------------now wait start-------&quot;</span></span><br><span class="line">    <span class="built_in">nohup</span> java -jar -XX:MetaspaceSize=128m -XX:MaxMetaspaceSize=256m -Xms512m -Xmn768m -Xmx1024m -Xss256k -XX:SurvivorRatio=8 -XX:+UseConcMarkSweepGC app.jar &gt; app.log 2&gt;&amp;1 &amp;</span><br><span class="line">    <span class="built_in">echo</span> <span class="string">&quot;--------------start success-----------&quot;</span></span><br><span class="line">    <span class="built_in">exit</span></span><br><span class="line"><span class="keyword">fi</span></span><br></pre></td></tr></table></figure><blockquote><p>注意这种方法，简化了一些重启的操作，会寻找是否有名字为app.jar的服务进程，所以服务器上如果部署有多个服务，那么jar包不要重名，或者修改成路径</p></blockquote><h1 id="参数版脚本文件"><a href="#参数版脚本文件" class="headerlink" title="参数版脚本文件"></a>参数版脚本文件</h1><p>这是我自己参考，然后修改的一个脚本文件，只要将该脚本文件和Jar包放入同一个目录下运行即可启动服务。</p><p>这种是本地启动的方式，所以需要自行配置服务器的Java环境。</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><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></pre></td><td class="code"><pre><span class="line"><span class="meta">#!/bin/bash</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># 文件夹路径</span></span><br><span class="line">FOLDER_PATH=<span class="string">&#x27;&#x27;</span></span><br><span class="line"><span class="comment"># 服务名称</span></span><br><span class="line">SERVER_NAME=<span class="string">&#x27;&#x27;</span></span><br><span class="line"><span class="comment"># jar包名称</span></span><br><span class="line">JAR_NAME=<span class="string">&#x27;&#x27;</span></span><br><span class="line"><span class="comment"># PID文件</span></span><br><span class="line">PID_FILE=<span class="string">&#x27;&#x27;</span></span><br><span class="line"><span class="comment"># java -jar的参数</span></span><br><span class="line">OPTIONS=<span class="string">&#x27;&#x27;</span></span><br><span class="line"><span class="comment"># log文件</span></span><br><span class="line">LOG_FILE=<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="function"><span class="title">usage</span></span>() &#123;</span><br><span class="line">  <span class="built_in">echo</span> <span class="string">&quot;Usage:  sh ****.sh [OPTION]... &quot;</span></span><br><span class="line">  <span class="built_in">echo</span> -e <span class="string">&quot;\033[43;37m 注意: 需要添加参数运行,否则无法成功运行 \033[0m&quot;</span></span><br><span class="line">  <span class="built_in">echo</span> -e <span class="string">&quot;\033[43;37m 注意: 需要将本sh文件和jar包放在同一个文件夹 \033[0m&quot;</span></span><br><span class="line">  <span class="built_in">echo</span> <span class="string">&quot;当前OPTION参数有以下几种&quot;</span></span><br><span class="line">  <span class="built_in">echo</span> <span class="string">&quot;  --info       查看当前服务信息&quot;</span></span><br><span class="line">  <span class="built_in">echo</span> <span class="string">&quot;  --start      启动服务&quot;</span></span><br><span class="line">  <span class="built_in">echo</span> <span class="string">&quot;  --stop       停止服务&quot;</span></span><br><span class="line">  <span class="built_in">echo</span> <span class="string">&quot;  --restart    重启服务&quot;</span></span><br><span class="line">  <span class="built_in">echo</span> <span class="string">&quot;  --status     查询服务运行状态&quot;</span></span><br><span class="line">  <span class="built_in">exit</span> 1</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment"># 初始化</span></span><br><span class="line"><span class="function"><span class="title">init</span></span>() &#123;</span><br><span class="line">  FOLDER_PATH=$( <span class="built_in">pwd</span> )</span><br><span class="line">  JAR_NAME=$( <span class="built_in">ls</span> -l |grep  .jar | awk <span class="string">&#x27;&#123;print$9&#125;&#x27;</span> )</span><br><span class="line">  SERVER_NAME=$( <span class="built_in">basename</span> <span class="variable">$&#123;JAR_NAME&#125;</span> .jar )</span><br><span class="line">  PID_FILE=<span class="variable">$&#123;SERVER_NAME&#125;</span>\.pid</span><br><span class="line">  LOG_FILE=<span class="variable">$&#123;SERVER_NAME&#125;</span>\.<span class="built_in">log</span></span><br><span class="line">  OPTIONS=<span class="string">&#x27;-Xms256m -Xmx512m&#x27;</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="function"><span class="title">info</span></span>() &#123;</span><br><span class="line">  <span class="built_in">echo</span> <span class="string">&quot;----------检测是否有Java环境----------&quot;</span></span><br><span class="line">  <span class="keyword">if</span> ! <span class="built_in">command</span> -v java &amp;&gt; /dev/null</span><br><span class="line">  <span class="keyword">then</span></span><br><span class="line">    <span class="built_in">echo</span> <span class="string">&quot;-bash: java: command not found&quot;</span></span><br><span class="line">    <span class="built_in">echo</span> <span class="string">&quot;请检查是安装了Java环境,是否配置了环境变量&quot;</span></span><br><span class="line">  <span class="keyword">else</span></span><br><span class="line">    <span class="built_in">echo</span> <span class="string">&quot;----------当前Java的环境为----------&quot;</span></span><br><span class="line">    <span class="built_in">echo</span> <span class="string">&quot;&quot;</span></span><br><span class="line">    <span class="built_in">echo</span> <span class="string">&quot;<span class="subst">$( java -version )</span>&quot;</span></span><br><span class="line">  <span class="keyword">fi</span></span><br><span class="line">  <span class="built_in">echo</span> <span class="string">&quot;----------当前文件夹路径----------&quot;</span></span><br><span class="line">  <span class="built_in">echo</span> <span class="string">&quot;当前文件夹路径为<span class="variable">$&#123;FOLDER_PATH&#125;</span>&quot;</span></span><br><span class="line">  <span class="built_in">echo</span> <span class="string">&quot;----------启动服务的信息----------&quot;</span></span><br><span class="line">  <span class="built_in">echo</span> <span class="string">&quot;当前服务名称为<span class="variable">$&#123;SERVER_NAME&#125;</span>&quot;</span></span><br><span class="line">  <span class="built_in">echo</span> <span class="string">&quot;需要启动的jar包名称为<span class="variable">$&#123;JAR_NAME&#125;</span>&quot;</span></span><br><span class="line">  <span class="built_in">echo</span> <span class="string">&quot;保存的PID文件名称为<span class="variable">$&#123;PID_FILE&#125;</span>&quot;</span></span><br><span class="line">  <span class="built_in">echo</span> <span class="string">&quot;保存的LOG文件名称为<span class="variable">$&#123;LOG_FILE&#125;</span>&quot;</span></span><br><span class="line">  <span class="built_in">echo</span> <span class="string">&quot;----------java -jar的参数----------&quot;</span></span><br><span class="line">  <span class="built_in">echo</span> <span class="string">&quot;当前参数为<span class="variable">$&#123;OPTIONS&#125;</span>,需要更改请执行 -setOptions参数进行修改&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="function"><span class="title">is_running</span></span>() &#123;</span><br><span class="line">  pid=`ps -ef|grep <span class="variable">$JAR_NAME</span> | grep -v grep | awk <span class="string">&#x27;&#123;print $2&#125;&#x27;</span> `</span><br><span class="line">  <span class="comment"># pid是否存在，存在返回1，不存在返回0</span></span><br><span class="line">  <span class="keyword">if</span> [ -n <span class="string">&quot;<span class="variable">$&#123;pid&#125;</span>&quot;</span> ]; <span class="keyword">then</span></span><br><span class="line">    <span class="built_in">return</span> 1</span><br><span class="line">  <span class="keyword">else</span></span><br><span class="line">    <span class="built_in">return</span> 0</span><br><span class="line">  <span class="keyword">fi</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="function"><span class="title">start</span></span>() &#123;</span><br><span class="line">  is_running</span><br><span class="line">  <span class="comment"># 如果服务没有运行，则启动服务，否则不启动</span></span><br><span class="line">  <span class="keyword">if</span> [ $? -eq <span class="string">&quot;0&quot;</span> ]; <span class="keyword">then</span></span><br><span class="line">    init</span><br><span class="line">    <span class="built_in">echo</span> <span class="string">&quot;服务启动中,请稍后&quot;</span></span><br><span class="line">    <span class="built_in">nohup</span> java <span class="variable">$&#123;OPTIONS&#125;</span> -jar <span class="variable">$&#123;JAR_NAME&#125;</span> &gt;<span class="variable">$&#123;LOG_FILE&#125;</span> 2&gt;&amp;1 &amp;</span><br><span class="line">    <span class="built_in">echo</span> $! &gt; <span class="variable">$&#123;PID_FILE&#125;</span></span><br><span class="line">    <span class="built_in">echo</span> <span class="string">&quot;服务启动成功,当前的PID为<span class="subst">$( cat $&#123;PID_FILE&#125; )</span>&quot;</span></span><br><span class="line">  <span class="keyword">else</span></span><br><span class="line">    <span class="built_in">echo</span> <span class="string">&quot;目标服务正在运行，请勿再次启动&quot;</span></span><br><span class="line">  <span class="keyword">fi</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="function"><span class="title">stop</span></span>() &#123;</span><br><span class="line">  is_running</span><br><span class="line">  <span class="keyword">if</span> [ $? -eq <span class="string">&quot;0&quot;</span> ]; <span class="keyword">then</span></span><br><span class="line">    <span class="built_in">echo</span> <span class="string">&quot;没有检测到服务启动,请启动之后在尝试&quot;</span></span><br><span class="line">    <span class="built_in">exit</span> 2</span><br><span class="line">  <span class="keyword">fi</span></span><br><span class="line">  pid=$(<span class="built_in">cat</span> <span class="variable">$&#123;PID_FILE&#125;</span>)</span><br><span class="line">  <span class="built_in">kill</span> <span class="variable">$&#123;pid&#125;</span></span><br><span class="line">  <span class="built_in">rm</span> -rf <span class="variable">$&#123;PID_FILE&#125;</span></span><br><span class="line">  <span class="built_in">sleep</span> 1</span><br><span class="line">  is_running</span><br><span class="line">  <span class="keyword">if</span> [ $? -eq <span class="string">&quot;1&quot;</span> ]; <span class="keyword">then</span></span><br><span class="line">    <span class="built_in">kill</span> -9 <span class="variable">$&#123;pid&#125;</span></span><br><span class="line">  <span class="keyword">fi</span></span><br><span class="line">  <span class="built_in">echo</span> <span class="string">&quot;成功结束<span class="variable">$&#123;SERVER_NAME&#125;</span>的进程&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="function"><span class="title">status</span></span>() &#123;</span><br><span class="line">  is_running</span><br><span class="line">  <span class="keyword">if</span> [ $? -eq <span class="string">&quot;1&quot;</span> ]; <span class="keyword">then</span></span><br><span class="line">    <span class="built_in">echo</span> <span class="string">&quot;<span class="variable">$&#123;SERVER_NAME&#125;</span>服务正在运行中,当前服务的PID为<span class="variable">$&#123;pid&#125;</span>&quot;</span></span><br><span class="line">  <span class="keyword">else</span> </span><br><span class="line">    <span class="built_in">echo</span> <span class="string">&quot;<span class="variable">$&#123;SERVER_NAME&#125;</span>服务没有在运行&quot;</span></span><br><span class="line">  <span class="keyword">fi</span></span><br><span class="line">  <span class="built_in">exit</span> 0</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment"># 重启服务</span></span><br><span class="line"><span class="function"><span class="title">restart</span></span>() &#123;</span><br><span class="line">  is_running</span><br><span class="line">  <span class="keyword">if</span> [ $? -eq <span class="string">&quot;1&quot;</span> ]; <span class="keyword">then</span></span><br><span class="line">    stop</span><br><span class="line">    start</span><br><span class="line">  <span class="keyword">else</span></span><br><span class="line">    <span class="built_in">echo</span> <span class="string">&quot;当前服务并没有运行,请运行之后在尝试&quot;</span></span><br><span class="line">  <span class="keyword">fi</span></span><br><span class="line">  <span class="built_in">exit</span> 0</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line">init</span><br><span class="line"></span><br><span class="line"><span class="keyword">case</span> <span class="string">&quot;<span class="variable">$1</span>&quot;</span> <span class="keyword">in</span></span><br><span class="line">  <span class="string">&quot;--info&quot;</span>)</span><br><span class="line">    info</span><br><span class="line">    ;;</span><br><span class="line">  <span class="string">&quot;--start&quot;</span>)</span><br><span class="line">    start</span><br><span class="line">    ;;</span><br><span class="line">  <span class="string">&quot;--stop&quot;</span>)</span><br><span class="line">    stop</span><br><span class="line">    ;;</span><br><span class="line">  <span class="string">&quot;--status&quot;</span>)</span><br><span class="line">    status</span><br><span class="line">    ;;</span><br><span class="line">  <span class="string">&quot;--restart&quot;</span>)</span><br><span class="line">    restart</span><br><span class="line">    ;;</span><br><span class="line">  *)</span><br><span class="line">    usage</span><br><span class="line">    ;;</span><br><span class="line"><span class="keyword">esac</span></span><br><span class="line"></span><br><span class="line"><span class="built_in">exit</span> 0</span><br></pre></td></tr></table></figure><p>以下是使用方法</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># 获取当前服务信息</span></span><br><span class="line">sh start.sh --info</span><br><span class="line"><span class="comment"># 启动服务</span></span><br><span class="line">sh start.sh --start</span><br><span class="line"><span class="comment"># 停止服务</span></span><br><span class="line">sh start.sh --stop</span><br><span class="line"><span class="comment"># 查询服务状态</span></span><br><span class="line">sh start.sh --status</span><br><span class="line"><span class="comment"># 重启服务</span></span><br><span class="line">sh start.sh --restart</span><br><span class="line"><span class="comment"># 提示使用信息</span></span><br><span class="line">sh start</span><br></pre></td></tr></table></figure><blockquote><p>其实这个脚本文件多多少少还是有一点问题的，提示信息也不是很美观，但是自己使用还是没什么问题的。</p></blockquote><h1 id="自动maven打包的Dockerfile"><a href="#自动maven打包的Dockerfile" class="headerlink" title="自动maven打包的Dockerfile"></a>自动maven打包的Dockerfile</h1><p>这种方法是通过Docker部署的方式，将源码使用maven打包成jar包，然后在容器内运行jar包。</p><p>步骤：</p><ol><li>拉取Springboot源代码</li><li>如果需要自定义maven配置，请添加maven配置文件在于源码目录，或自己添加</li><li>修改服务打包生成的JAR包文件名</li><li>制作镜像</li></ol><p>使用Docker的话，得自行选择需要的JDK镜像和Maven版本镜像，然后修改服务打包出来后的服务名即可。</p><figure class="highlight dockerfile"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><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"># 多阶段构建：第一阶段用于编译 Spring Boot 项目</span></span><br><span class="line"><span class="keyword">FROM</span> maven:<span class="number">3.8</span>.<span class="number">1</span>-openjdk-<span class="number">17</span> AS builder</span><br><span class="line"><span class="keyword">LABEL</span><span class="language-bash"> stage=builder</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># 设置工作目录</span></span><br><span class="line"><span class="keyword">WORKDIR</span><span class="language-bash"> /app</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># 拷贝项目文件</span></span><br><span class="line"><span class="keyword">COPY</span><span class="language-bash"> pom.xml .</span></span><br><span class="line"><span class="keyword">COPY</span><span class="language-bash"> src ./src</span></span><br><span class="line"><span class="comment"># 如果需要自定义maven镜像仓库配置，请添加maven配置文件</span></span><br><span class="line"><span class="keyword">COPY</span><span class="language-bash"> settings.xml /root/.m2/settings.xml</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># 编译并打包（跳过测试）</span></span><br><span class="line"><span class="keyword">RUN</span><span class="language-bash"> mvn clean package -DskipTests</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># 第二阶段：运行阶段（基于轻量级 JRE 镜像）</span></span><br><span class="line"><span class="keyword">FROM</span> eclipse-temurin:<span class="number">17</span>-jre-alpine</span><br><span class="line"><span class="keyword">LABEL</span><span class="language-bash"> maintainer=<span class="string">&quot;yww@yww52.com&quot;</span></span></span><br><span class="line"></span><br><span class="line"><span class="comment"># 设置时区</span></span><br><span class="line"><span class="keyword">ENV</span> TZ=Asia/Shanghai</span><br><span class="line"><span class="keyword">RUN</span><span class="language-bash"> apk add --no-cache tzdata &amp;&amp; \</span></span><br><span class="line"><span class="language-bash">    <span class="built_in">ln</span> -sf /usr/share/zoneinfo/<span class="variable">$TZ</span> /etc/localtime &amp;&amp; \</span></span><br><span class="line"><span class="language-bash">    <span class="built_in">echo</span> <span class="variable">$TZ</span> &gt; /etc/timezone &amp;&amp; \</span></span><br><span class="line"><span class="language-bash">    <span class="built_in">rm</span> -rf /var/cache/apk/*</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># 定义 JAR 名称变量（需自定义）</span></span><br><span class="line"><span class="keyword">ARG</span> JAR_NAME=app.jar</span><br><span class="line"></span><br><span class="line"><span class="comment"># 定义 JVM 参数和应用目录</span></span><br><span class="line"><span class="keyword">ARG</span> JAR_NAME=app.jar</span><br><span class="line"><span class="keyword">ENV</span> JAVA_OPTS=<span class="string">&quot;-Xms512m -Xmx1536m -Xmn614m -Xss256k -XX:SurvivorRatio=6 -XX:+UseG1GC -XX:MetaspaceSize=256m -XX:MaxMetaspaceSize=512m&quot;</span></span><br><span class="line"><span class="keyword">ENV</span> APP_HOME=/app</span><br><span class="line"></span><br><span class="line"><span class="comment"># 创建非 root 用户并设置权限</span></span><br><span class="line"><span class="keyword">RUN</span><span class="language-bash"> adduser -D appuser &amp;&amp; \</span></span><br><span class="line"><span class="language-bash">    <span class="built_in">mkdir</span> -p <span class="variable">$APP_HOME</span> &amp;&amp; \</span></span><br><span class="line"><span class="language-bash">    <span class="built_in">chown</span> -R appuser:appuser <span class="variable">$APP_HOME</span></span></span><br><span class="line"><span class="keyword">USER</span> appuser</span><br><span class="line"></span><br><span class="line"><span class="comment"># 设置工作目录</span></span><br><span class="line"><span class="keyword">WORKDIR</span><span class="language-bash"> <span class="variable">$APP_HOME</span></span></span><br><span class="line"></span><br><span class="line"><span class="comment"># 从构建阶段复制生成的 JAR 文件</span></span><br><span class="line"><span class="keyword">COPY</span><span class="language-bash"> --from=builder /app/target/*.jar <span class="variable">$&#123;JAR_NAME&#125;</span></span></span><br><span class="line"></span><br><span class="line"><span class="comment"># 暴露应用端口</span></span><br><span class="line"><span class="keyword">EXPOSE</span> <span class="number">9872</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># 启动命令（使用 exec 确保 Java 成为 PID 1）</span></span><br><span class="line"><span class="keyword">ENTRYPOINT</span><span class="language-bash"> [<span class="string">&quot;sh&quot;</span>, <span class="string">&quot;-c&quot;</span>, <span class="string">&quot;exec java <span class="variable">$JAVA_OPTS</span> -jar <span class="variable">$&#123;JAR_NAME&#125;</span>&quot;</span>]</span></span><br></pre></td></tr></table></figure><blockquote><p>这个制作镜像的方式会有个问题，就是每次制作镜像都需要去跑maven下载依赖，然后打包，时间花费的很多，而且有时候有些maven依赖在仓库上获取不到，所以这种方式适合一些自建有maven仓库的项目，比如自建有Nexus，早就下载好依赖上传到仓库的项目。<br>不然的话还是在本地打包后在制作镜像吧，这样也快速，就是会很麻烦，不够自动化。</p></blockquote><h1 id="打包后的Dockerfile"><a href="#打包后的Dockerfile" class="headerlink" title="打包后的Dockerfile"></a>打包后的Dockerfile</h1><p>步骤</p><ol><li>打包好的JAR包</li><li>准备Dockerfile</li><li>修改服务打包生成的JAR包文件名</li><li>制作镜像</li></ol><figure class="highlight dockerfile"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><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">FROM</span> eclipse-temurin:<span class="number">17</span>-jre-alpine</span><br><span class="line"><span class="keyword">LABEL</span><span class="language-bash"> maintainer=<span class="string">&quot;yww@yww52.com&quot;</span></span></span><br><span class="line"></span><br><span class="line"><span class="comment"># 定义应用相关环境变量</span></span><br><span class="line"><span class="keyword">ENV</span> JAVA_OPTS=<span class="string">&quot;-Xms512m -Xmx2048m -Xmn614m -Xss256k -XX:SurvivorRatio=6 -XX:+UseG1GC -XX:MetaspaceSize=256m -XX:MaxMetaspaceSize=512m&quot;</span></span><br><span class="line"><span class="keyword">ENV</span> APP_HOME=/app</span><br><span class="line"><span class="keyword">ARG</span> JAR_NAME=app.jar</span><br><span class="line"></span><br><span class="line"><span class="comment"># 设置时区</span></span><br><span class="line"><span class="keyword">ENV</span> TZ=Asia/Shanghai</span><br><span class="line"><span class="keyword">RUN</span><span class="language-bash"> apk add tzdata \</span></span><br><span class="line"><span class="language-bash">    &amp;&amp; <span class="built_in">ln</span> -snf /usr/share/zoneinfo/<span class="variable">$TZ</span> /etc/localtime \</span></span><br><span class="line"><span class="language-bash">    &amp;&amp; <span class="built_in">echo</span> <span class="variable">$TZ</span> &gt; /etc/timezone \</span></span><br><span class="line"><span class="language-bash">    &amp;&amp; apk del tzdata \</span></span><br><span class="line"><span class="language-bash">    &amp;&amp; <span class="built_in">rm</span> -rf /var/cache/apk/*</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># 创建非root用户并设置权限</span></span><br><span class="line"><span class="keyword">RUN</span><span class="language-bash"> adduser -D appuser &amp;&amp; \</span></span><br><span class="line"><span class="language-bash">    <span class="built_in">mkdir</span> -p <span class="variable">$APP_HOME</span> &amp;&amp; \</span></span><br><span class="line"><span class="language-bash">    <span class="built_in">chown</span> -R appuser:appuser <span class="variable">$APP_HOME</span></span></span><br><span class="line"><span class="keyword">USER</span> appuser</span><br><span class="line"></span><br><span class="line"><span class="comment"># 设置工作目录和应用目录</span></span><br><span class="line"><span class="keyword">WORKDIR</span><span class="language-bash"> <span class="variable">$APP_HOME</span></span></span><br><span class="line"></span><br><span class="line"><span class="comment"># 复制已打包的 JAR 文件</span></span><br><span class="line"><span class="keyword">COPY</span><span class="language-bash"> <span class="variable">$&#123;JAR_NAME&#125;</span> <span class="variable">$APP_HOME</span>/<span class="variable">$&#123;JAR_NAME&#125;</span></span></span><br><span class="line"></span><br><span class="line"><span class="comment"># 暴露应用端口</span></span><br><span class="line"><span class="keyword">EXPOSE</span> <span class="number">9872</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># 启动命令（使用环境变量配置 JVM 参数）</span></span><br><span class="line"><span class="keyword">ENTRYPOINT</span><span class="language-bash"> [<span class="string">&quot;sh&quot;</span>, <span class="string">&quot;-c&quot;</span>, <span class="string">&quot;java <span class="variable">$JAVA_OPTS</span> -jar <span class="variable">$&#123;JAR_NAME&#125;</span>&quot;</span>]</span></span><br></pre></td></tr></table></figure>]]></content:encoded>
            
            
            <category domain="https://yww52.com/categories/%E6%88%91%E7%9A%84%E6%8A%80%E6%9C%AF%E5%AD%A6%E4%B9%A0%E7%89%A9%E8%AF%AD%E6%9E%9C%E7%84%B6%E6%9C%89%E9%97%AE%E9%A2%98/">我的技术学习物语果然有问题</category>
            
            <category domain="https://yww52.com/categories/%E6%88%91%E7%9A%84%E6%8A%80%E6%9C%AF%E5%AD%A6%E4%B9%A0%E7%89%A9%E8%AF%AD%E6%9E%9C%E7%84%B6%E6%9C%89%E9%97%AE%E9%A2%98/%E5%BC%80%E5%8F%91%E6%8A%80%E5%B7%A7/">开发技巧</category>
            
            
            
            <comments>https://yww52.com/posts/e31003e0/#disqus_thread</comments>
            
        </item>
        
        <item>
            <title>RabbitMQ</title>
            <link>https://yww52.com/posts/b543ced0/</link>
            <guid>https://yww52.com/posts/b543ced0/</guid>
            <pubDate>Sat, 25 Dec 2021 16:00:00 GMT</pubDate>
            
            <description>RabbitMQ的学习笔记。</description>
            
            
            
            <content:encoded><![CDATA[<img src="https://img.yww52.com/2021/12/2021-12-26top_img.jpg" alt="RabbitMQ" style="max-width:100%;height:auto;margin-bottom:15px;"
  /><h1 id="什么是MQ"><a href="#什么是MQ" class="headerlink" title="什么是MQ"></a>什么是MQ</h1><p>MQ，全称为<code>message queue</code>，消息队列，本质就是一个先进先出的队列，是一种跨进程的通信机制，用于上下游传递信息。</p><h1 id="为什么要使用MQ"><a href="#为什么要使用MQ" class="headerlink" title="为什么要使用MQ"></a>为什么要使用MQ</h1><p>MQ的优点</p><ol><li>流量削峰</li><li>异步处理</li><li>应用解耦</li></ol><h1 id="MQ分类"><a href="#MQ分类" class="headerlink" title="MQ分类"></a>MQ分类</h1><ol><li><code>ActiveMQ</code><ul><li>优点。单机吞吐量万级，时效性为ms级，可用性高，基于主从架构实现高可用性，丢失数据的概率较低。</li><li>缺点。早期项目，目前社区活跃度低，高吞吐量场景较少使用。</li></ul></li><li><code>Kafka</code><ul><li>优点。在大数据的消息传输方面，举足轻重。方面性能卓越，单机写入TPS约在每秒百万条，最大的优点就是吞吐量很高，时效性为ms级，可用性很高。而且是Kafka是分布式的，一个数据多个副本。</li><li>缺点。Kafka单机超过64个队列（分区），Load就会发生明显的飙高，发送消息响应时间变长，消息失败不支持重试。支持消息顺序，但是一台代理宕机后，就会产生消息乱序，社区更新较慢。</li></ul></li><li><code>RocketMQ</code><ul><li>优点。RocketMQ出自阿里巴巴，而且用Java语言实现的，对于学习Java的人来说还能自己定制。单机吞吐量达十万级，可用性很高，分布式架构，消息可以做到零丢失，功能较为完善，扩展性也很好，支持10亿级别的消息堆积，不会因为消息堆积而导致性能下降。</li><li>缺点。支持的客户端语言不多，目前是Java和C++，而且C++方面也还不成熟，没有在MQ核心中去实现JMS等接口，有些系统要迁移需要修改大量的代码。</li></ul></li><li><code>RabbitMQ</code><ul><li>优点。RabbitMQ由erlang实现，由于erlang的高并发特性，性能较好，吞吐量到万级，功能比较完备，稳定易用跨平台，而且支持多种语言，社区活跃度也很高。</li><li>缺点。商业版需要收费，学习成本较高。</li></ul></li></ol><h1 id="RabbitMQ的概念"><a href="#RabbitMQ的概念" class="headerlink" title="RabbitMQ的概念"></a>RabbitMQ的概念</h1><p>RabbitMQ是一个消息中间件，接收并转发消息，它只接收存储和转发消息数据，并不会处理消息。</p><p>四大核心概念。</p><ol><li><p><code>生产者</code>。</p><p>产生数据并发送消息的程序。</p></li><li><p><code>交换机</code>。</p><p>交换机一方面接收来自生产者的消息，另一方面它将消息推送到队列中。交换机必须确切知道如何处理它接收到的消息，是将这些消息推送到特定队列还是推送到多个队列，亦或者是把消息丢弃，这个得有交换机类型决定。</p></li><li><p><code>队列</code></p><p>队列是RabbitMQ中的使用的一个数据结构，用于存储消息。队列仅受主机的内存和磁盘限制的约束，本质上是一个消息缓冲区。生产者将消息发送到队列，消费者从队列中接收消息。</p></li><li><p><code>消费者</code></p><p>接收并处理消息的程序。</p></li></ol><h1 id="RabbitMQ安装"><a href="#RabbitMQ安装" class="headerlink" title="RabbitMQ安装"></a>RabbitMQ安装</h1><p>RabbitMQ的安装可以参考官方的文档。</p><p><a href="https://www.rabbitmq.com/download.html">https://www.rabbitmq.com/download.html</a></p><p>里面有多种安装的方式，建议使用docker来进行安装，不用考虑这么多环境的问题，只要安装了docker，就只需要一条命令即可。</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></pre></td><td class="code"><pre><span class="line">docker run -<span class="built_in">id</span> --name rabbitmq \</span><br><span class="line">-e RABBITMQ_DEFAULT_USER=username \</span><br><span class="line">-e RABBITMQ_DEFAULT_PASS=password \</span><br><span class="line">-p 15672:15672 -p 5672:5672 \</span><br><span class="line">rabbitmq:3.8-management</span><br></pre></td></tr></table></figure><p>安装的是管理版本，所以可以访问地址</p><p><a href="http://IP:15672/">http://IP:15672/</a></p><p>来进行登录管理RabbitMQ的web界面。</p><p>服务器记得开放15672和5672这两个端口，15672是web管理界面的接口，5672是RabbitMQ的服务端口。</p><p>如果没设置参数，默认的账号和密码都是<code>guest</code></p><p>官方的教程也是挺好的，可以参考一下。</p><p><a href="https://www.rabbitmq.com/getstarted.html">https://www.rabbitmq.com/getstarted.html</a></p><h1 id="Hello-World"><a href="#Hello-World" class="headerlink" title="Hello World"></a>Hello World</h1><p>引入MQ客户端的依赖。</p><figure class="highlight xml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">&lt;!-- RabbitMQ的客户端依赖 --&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.rabbitmq<span class="tag">&lt;/<span class="name">groupId</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">artifactId</span>&gt;</span>amqp-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>首先先创建一个常量类，用于存储一些配置参数。</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">package</span> com.yww.rabbitmq;</span><br><span class="line"></span><br><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * &lt;p&gt;</span></span><br><span class="line"><span class="comment"> *     连接的参数</span></span><br><span class="line"><span class="comment"> * &lt;/p&gt;</span></span><br><span class="line"><span class="comment"> *</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@author</span> yww</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_">Constant</span> &#123;</span><br><span class="line">    </span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     * 队列名称</span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">final</span> <span class="type">String</span> <span class="variable">QUEUE_NAME</span> <span class="operator">=</span> <span class="string">&quot;Hello&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"><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">HOSTNAME</span> <span class="operator">=</span> <span class="string">&quot;119.29.119.110&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"><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">USERNAME</span> <span class="operator">=</span> <span class="string">&quot;username&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"><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">PASSWORD</span> <span class="operator">=</span> <span class="string">&quot;password&quot;</span>;</span><br><span class="line"></span><br><span class="line">&#125;</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><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><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="keyword">package</span> com.yww.rabbitmq;</span><br><span class="line"></span><br><span class="line"><span class="keyword">import</span> com.rabbitmq.client.Channel;</span><br><span class="line"><span class="keyword">import</span> com.rabbitmq.client.Connection;</span><br><span class="line"><span class="keyword">import</span> com.rabbitmq.client.ConnectionFactory;</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.concurrent.TimeoutException;</span><br><span class="line"></span><br><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * &lt;p&gt;</span></span><br><span class="line"><span class="comment"> *     生产者</span></span><br><span class="line"><span class="comment"> * &lt;/p&gt;</span></span><br><span class="line"><span class="comment"> *</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@author</span> yww</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_">Producer</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> <span class="keyword">throws</span> IOException, TimeoutException &#123;</span><br><span class="line">        <span class="comment">// 创建连接工厂</span></span><br><span class="line">        <span class="type">ConnectionFactory</span> <span class="variable">factory</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">ConnectionFactory</span>();</span><br><span class="line">        <span class="comment">// 设置连接的配置,IP,用户名，密码</span></span><br><span class="line">        factory.setHost(Constant.HOSTNAME);</span><br><span class="line">        factory.setUsername(Constant.USERNAME);</span><br><span class="line">        factory.setPassword(Constant.PASSWORD);</span><br><span class="line">        <span class="comment">// 创建连接</span></span><br><span class="line">        <span class="type">Connection</span> <span class="variable">connection</span> <span class="operator">=</span> factory.newConnection();</span><br><span class="line">        <span class="comment">// 通过连接来创建一个信道</span></span><br><span class="line">        <span class="type">Channel</span> <span class="variable">channel</span> <span class="operator">=</span> connection.createChannel();</span><br><span class="line">        <span class="comment">/*</span></span><br><span class="line"><span class="comment">         * 通过一个信道来生成一个队列</span></span><br><span class="line"><span class="comment">         * 1. 队列的名称</span></span><br><span class="line"><span class="comment">         * 2. true表示声明的是一个持久化队列，该队列会在服务器重启后依旧存活</span></span><br><span class="line"><span class="comment">         * 3. true表示声明的是一个独占队列，被限制在这个连接中。</span></span><br><span class="line"><span class="comment">         * 4. 最后一个消费者断开连接之后，该队列是否自动删除。true表示自动删除，false表示不自动删除</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">        channel.queueDeclare(Constant.QUEUE_NAME, <span class="literal">false</span>, <span class="literal">false</span>, <span class="literal">false</span>, <span class="literal">null</span>);</span><br><span class="line"></span><br><span class="line">        <span class="type">String</span> <span class="variable">message</span> <span class="operator">=</span> <span class="string">&quot;Hello World!&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"><span class="comment">         * 1. 发送到哪个交换机</span></span><br><span class="line"><span class="comment">         * 2. 路由的key值是哪个，本次是队列的名称</span></span><br><span class="line"><span class="comment">         * 3. 其他参数信息</span></span><br><span class="line"><span class="comment">         * 4. 发送消息的消息体</span></span><br><span class="line"><span class="comment">         */</span></span><br><span class="line">        channel.basicPublish(<span class="string">&quot;&quot;</span>, Constant.QUEUE_NAME, <span class="literal">null</span>, message.getBytes());</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>消费者消费消息。</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">package</span> com.yww.rabbitmq;</span><br><span class="line"></span><br><span class="line"><span class="keyword">import</span> com.rabbitmq.client.*;</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.concurrent.TimeoutException;</span><br><span class="line"></span><br><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * &lt;p&gt;</span></span><br><span class="line"><span class="comment"> *     消费者</span></span><br><span class="line"><span class="comment"> * &lt;/p&gt;</span></span><br><span class="line"><span class="comment"> *</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@author</span> yww</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_">Consumer</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> <span class="keyword">throws</span> IOException, TimeoutException &#123;</span><br><span class="line">        <span class="type">ConnectionFactory</span> <span class="variable">factory</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">ConnectionFactory</span>();</span><br><span class="line">        factory.setHost(Constant.HOSTNAME);</span><br><span class="line">        factory.setUsername(Constant.USERNAME);</span><br><span class="line">        factory.setPassword(Constant.PASSWORD);</span><br><span class="line">        <span class="type">Connection</span> <span class="variable">connection</span> <span class="operator">=</span> factory.newConnection();</span><br><span class="line">        <span class="type">Channel</span> <span class="variable">channel</span> <span class="operator">=</span> connection.createChannel();</span><br><span class="line"></span><br><span class="line">        <span class="comment">// 消费者接收的回调</span></span><br><span class="line">        <span class="type">DeliverCallback</span> <span class="variable">deliverCallback</span> <span class="operator">=</span> (consumerTag, message) -&gt; &#123;</span><br><span class="line">            System.out.println(<span class="string">&quot;接收到的消息为：&quot;</span>);</span><br><span class="line">            <span class="type">String</span> <span class="variable">messageMessage</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">String</span>(message.getBody());</span><br><span class="line">            System.out.println(messageMessage);</span><br><span 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">CancelCallback</span> <span class="variable">cancelCallback</span> <span class="operator">=</span> consumerTag -&gt; &#123;</span><br><span class="line">            System.out.println(<span class="string">&quot;消费者取消了消费！&quot;</span>);</span><br><span class="line">            System.out.println(consumerTag);</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">         * 1. 队列的名称</span></span><br><span class="line"><span class="comment">         * 2. 消费成功之后是否要自动应答。true表示自动应答，false表示要手动应答</span></span><br><span class="line"><span class="comment">         * 3. 消费者接收的回调</span></span><br><span class="line"><span class="comment">         * 4. 消费者被取消消费的回调</span></span><br><span class="line"><span class="comment">         */</span></span><br><span class="line">        channel.basicConsume(Constant.QUEUE_NAME, <span class="literal">true</span>, deliverCallback, cancelCallback);</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><h1 id="Work-Queues"><a href="#Work-Queues" class="headerlink" title="Work Queues"></a>Work Queues</h1><p>Work Queues，工作队列或者说是任务队列，简单的理解为一个队列的任务可以分配给多个消费者消费。</p><p>它的主要思想是避免立即执行资源密集任务，而不得不等待它完成。相反我们安排任务在之后执行。我们把任务封装为消息并将其发送到队列。在后台运行的工作进 程将弹出任务并最终执行作业。当有多个工作线程时，这些工作线程将一起处理这些任务。</p><h2 id="循环调度"><a href="#循环调度" class="headerlink" title="循环调度"></a>循环调度</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><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><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="keyword">package</span> com.yww.rabbitmq;</span><br><span class="line"></span><br><span class="line"><span class="keyword">import</span> com.rabbitmq.client.Channel;</span><br><span class="line"><span class="keyword">import</span> com.rabbitmq.client.Connection;</span><br><span class="line"><span class="keyword">import</span> com.rabbitmq.client.ConnectionFactory;</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.concurrent.TimeoutException;</span><br><span class="line"></span><br><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * &lt;p&gt;</span></span><br><span class="line"><span class="comment"> *     获取信道工具类</span></span><br><span class="line"><span class="comment"> * &lt;/p&gt;</span></span><br><span class="line"><span class="comment"> *</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@author</span> yww</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_">MqUtil</span> &#123;</span><br><span class="line"></span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     * 队列名称</span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">final</span> <span class="type">String</span> <span class="variable">QUEUE_NAME</span> <span class="operator">=</span> <span class="string">&quot;Hello&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"><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">HOSTNAME</span> <span class="operator">=</span> <span class="string">&quot;119.29.119.110&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"><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">USERNAME</span> <span class="operator">=</span> <span class="string">&quot;username&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"><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">PASSWORD</span> <span class="operator">=</span> <span class="string">&quot;password&quot;</span>;</span><br><span class="line">    </span><br><span class="line">    </span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     * 获取信道</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@return</span>  Channel</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> Channel <span class="title function_">getChannel</span><span class="params">()</span> <span class="keyword">throws</span> IOException, TimeoutException &#123;</span><br><span class="line">        <span class="type">ConnectionFactory</span> <span class="variable">factory</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">ConnectionFactory</span>();</span><br><span class="line">        factory.setHost(HOSTNAME);</span><br><span class="line">        factory.setUsername(USERNAME);</span><br><span class="line">        factory.setPassword(PASSWORD);</span><br><span class="line">        <span class="type">Connection</span> <span class="variable">connection</span> <span class="operator">=</span> factory.newConnection();</span><br><span class="line">        <span class="keyword">return</span> connection.createChannel();</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><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><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.yww.rabbitmq;</span><br><span class="line"></span><br><span class="line"><span class="keyword">import</span> com.rabbitmq.client.CancelCallback;</span><br><span class="line"><span class="keyword">import</span> com.rabbitmq.client.Channel;</span><br><span class="line"><span class="keyword">import</span> com.rabbitmq.client.DeliverCallback;</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.concurrent.TimeoutException;</span><br><span class="line"></span><br><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * &lt;p&gt;</span></span><br><span class="line"><span class="comment"> *     第一个工作线程</span></span><br><span class="line"><span class="comment"> * &lt;/p&gt;</span></span><br><span class="line"><span class="comment"> *</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@author</span> yww</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">@date</span> 2021/12/25 22:28</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_">Worker1</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> <span class="keyword">throws</span> IOException, TimeoutException &#123;</span><br><span class="line">        <span class="type">Channel</span> <span class="variable">channel</span> <span class="operator">=</span> MqUtil.getChannel();</span><br><span class="line"></span><br><span class="line">        <span class="type">DeliverCallback</span> <span class="variable">deliverCallback</span> <span class="operator">=</span> (consumerTag, message) -&gt; &#123;</span><br><span class="line">            System.out.println(<span class="string">&quot;接收到的消息：&quot;</span>);</span><br><span class="line">            System.out.println(<span class="keyword">new</span> <span class="title class_">String</span>(message.getBody()));</span><br><span class="line">        &#125;;</span><br><span class="line"></span><br><span class="line">        <span class="type">CancelCallback</span> <span class="variable">cancelCallback</span> <span class="operator">=</span> consumerTag -&gt; &#123;</span><br><span class="line">            System.out.println(<span class="string">&quot;消费者取消消费这个消息&quot;</span>);</span><br><span class="line">            System.out.println(consumerTag);</span><br><span class="line">        &#125;;</span><br><span class="line">        System.out.println(<span class="string">&quot;当前工作线程为**1**，正在等待接收消息--------&quot;</span>);</span><br><span class="line">        <span class="comment">// 接收消息</span></span><br><span class="line">        channel.basicConsume(MqUtil.QUEUE_NAME, <span class="literal">true</span>, deliverCallback, cancelCallback);</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><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><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.yww.rabbitmq;</span><br><span class="line"></span><br><span class="line"><span class="keyword">import</span> com.rabbitmq.client.Channel;</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.Scanner;</span><br><span class="line"><span class="keyword">import</span> java.util.concurrent.TimeoutException;</span><br><span class="line"></span><br><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * &lt;p&gt;</span></span><br><span class="line"><span class="comment"> *     生产者</span></span><br><span class="line"><span class="comment"> *     发送大量的消息</span></span><br><span class="line"><span class="comment"> * &lt;/p&gt;</span></span><br><span class="line"><span class="comment"> *</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@author</span> yww</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_">Producer</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> <span class="keyword">throws</span> IOException, TimeoutException &#123;</span><br><span class="line">        <span class="type">Channel</span> <span class="variable">channel</span> <span class="operator">=</span> MqUtil.getChannel();</span><br><span class="line"></span><br><span class="line">        <span class="comment">// 信道指定队列</span></span><br><span class="line">        channel.queueDeclare(MqUtil.QUEUE_NAME, <span class="literal">false</span>, <span class="literal">false</span>, <span class="literal">false</span>, <span class="literal">null</span>);</span><br><span class="line">        <span class="comment">// 从控制台中接收信息</span></span><br><span class="line">        <span class="type">Scanner</span> <span class="variable">scanner</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">Scanner</span>(System.in);</span><br><span class="line">        <span class="keyword">while</span> (scanner.hasNext()) &#123;</span><br><span class="line">            <span class="type">String</span> <span class="variable">message</span> <span class="operator">=</span> scanner.nextLine();</span><br><span class="line">            channel.basicPublish(<span class="string">&quot;&quot;</span>, MqUtil.QUEUE_NAME, <span class="literal">null</span>, message.getBytes());</span><br><span class="line">            System.out.println(<span class="string">&quot;发送消息完成，本次的消息为----&quot;</span> + message);</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><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">AA</span><br><span class="line">发送消息完成，本次的消息为----AA</span><br><span class="line">BB</span><br><span class="line">发送消息完成，本次的消息为----BB</span><br><span class="line">CC</span><br><span class="line">发送消息完成，本次的消息为----CC</span><br><span class="line">DD</span><br><span class="line">发送消息完成，本次的消息为----DD</span><br></pre></td></tr></table></figure><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><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">当前工作线程为**1**，正在等待接收消息--------</span><br><span class="line">接收到的消息：</span><br><span class="line">AA</span><br><span class="line">接收到的消息：</span><br><span class="line">CC</span><br><span class="line"></span><br><span class="line">当前工作线程为**2**，正在等待接收消息--------</span><br><span class="line">接收到的消息：</span><br><span class="line">BB</span><br><span class="line">接收到的消息：</span><br><span class="line">DD</span><br></pre></td></tr></table></figure><blockquote><p>从这里可以看出一些规律。</p><p>每个消息是只能被消费一次的。</p><p>消息的消费的轮询的，就是说每个消费者消费了一次消息之后，就轮到下一个消费者去消费，不会连续消费。</p></blockquote><h2 id="消息应答"><a href="#消息应答" class="headerlink" title="消息应答"></a>消息应答</h2><p>消费者完成一个任务需要一段时间，在这段时间内如果它挂掉了，由于RabbitMQ一旦向消费者传递消息后就会立即将该消息标记为删除，所以就会丢失了这段消息的处理。所以为了保证消息在发送过程中不丢失，RabbitMQ引入了消息应答机制。</p><p>消息应答就是说，消费者在接收到消息并处理了之后，在通知RabbitMQ处理完成，RabbitMQ才会把该消息删除。</p><h3 id="自动应答"><a href="#自动应答" class="headerlink" title="自动应答"></a>自动应答</h3><p>这种方式表示消息发送后就会立即被认为已经传输完成，就会有可能出现上述的消息丢失的情况。这种模式会出现消息丢失的情况，但是可以适用于高吞吐量的场景，毕竟速度和安全并不能全都要。上述的代码演示都是可以看到是自动应答的。</p><p>这种模式尽量不使用，因为在大多数情况下，每一个消息都是很重要的，所以尽量使用自定义的应答方式。</p><h3 id="消息应答的方法"><a href="#消息应答的方法" class="headerlink" title="消息应答的方法"></a>消息应答的方法</h3><ul><li><p><code>Channel.basicAck()</code></p><p>肯定确认。表示MQ已经知道该消息已经被成功处理了，可以将其丢弃。</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><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> deliveryTag </span></span><br><span class="line"><span class="comment">    * <span class="doctag">@param</span> multiple 是否批量应答，true表示批量应答消息</span></span><br><span class="line"><span class="comment">    * <span class="doctag">@throws</span> java.io.IOException 如果遇到错误</span></span><br><span class="line"><span class="comment">    */</span></span><br><span class="line">   <span class="keyword">void</span> <span class="title function_">basicAck</span><span class="params">(<span class="type">long</span> deliveryTag, <span class="type">boolean</span> multiple)</span> <span class="keyword">throws</span> IOException;</span><br></pre></td></tr></table></figure></li></ul><ul><li><p><code>Channel.basicNack()</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><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> deliveryTag </span></span><br><span class="line"><span class="comment"> * <span class="doctag">@param</span> multiple 是否批量应答，true表示批量应答消息</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@param</span> requeue 如果被拒绝的消息应该被重新排队而不是丢弃/死信，则使用true</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@throws</span> java.io.IOException 如果遇到错误</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="keyword">void</span> <span class="title function_">basicNack</span><span class="params">(<span class="type">long</span> deliveryTag, <span class="type">boolean</span> multiple, <span class="type">boolean</span> requeue)</span> <span class="keyword">throws</span> IOException;</span><br></pre></td></tr></table></figure></li></ul><ul><li><p><code>Channel.basicReject()</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><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> deliveryTag </span></span><br><span class="line"><span class="comment"> * <span class="doctag">@param</span> requeue true表示如果被拒绝的消息应该被重新排队而不是被丢弃/死信，则使用true</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@throws</span> java.io.IOException 如果遇到错误</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="keyword">void</span> <span class="title function_">basicReject</span><span class="params">(<span class="type">long</span> deliveryTag, <span class="type">boolean</span> requeue)</span> <span class="keyword">throws</span> IOException;</span><br></pre></td></tr></table></figure></li></ul><blockquote><p>关于参数multiple这个批量应答的处理。</p><p>不建议使用批量应答，因为不能保证之前的消息是完成的，批量应答可能会出现应答了没完成的消息。</p></blockquote><h3 id="消息自动重新入队"><a href="#消息自动重新入队" class="headerlink" title="消息自动重新入队"></a>消息自动重新入队</h3><p>当消费者由于一些特殊情况导致ACK确认未成功发送给RabbitMQ，RabbitMQ就会让该消息重新排队（上述的requeue参数选择了消息不被丢弃），这就可以解决消息丢失的问题了。</p><h3 id="消息应答的测试"><a href="#消息应答的测试" class="headerlink" title="消息应答的测试"></a>消息应答的测试</h3><p>手动应答是消费者设置的，所以生产者不需要特殊的变化，跟上一例子一样。</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">Producer</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> <span class="keyword">throws</span> IOException, TimeoutException &#123;</span><br><span class="line">        <span class="type">Channel</span> <span class="variable">channel</span> <span class="operator">=</span> MqUtil.getChannel();</span><br><span class="line"></span><br><span class="line">        <span class="comment">// 信道指定队列</span></span><br><span class="line">        channel.queueDeclare(MqUtil.QUEUE_NAME, <span class="literal">false</span>, <span class="literal">false</span>, <span class="literal">false</span>, <span class="literal">null</span>);</span><br><span class="line">        <span class="comment">// 从控制台中接收信息</span></span><br><span class="line">        <span class="type">Scanner</span> <span class="variable">scanner</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">Scanner</span>(System.in);</span><br><span class="line">        <span class="keyword">while</span> (scanner.hasNext()) &#123;</span><br><span class="line">            <span class="type">String</span> <span class="variable">message</span> <span class="operator">=</span> scanner.nextLine();</span><br><span class="line">            channel.basicPublish(<span class="string">&quot;&quot;</span>, MqUtil.QUEUE_NAME, <span class="literal">null</span>, message.getBytes());</span><br><span class="line">            System.out.println(<span class="string">&quot;发送消息完成，本次的发送的消息为----&quot;</span> + message);</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">    </span><br><span class="line">&#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><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">package</span> com.yww.rabbitmq;</span><br><span class="line"></span><br><span class="line"><span class="keyword">import</span> com.rabbitmq.client.CancelCallback;</span><br><span class="line"><span class="keyword">import</span> com.rabbitmq.client.Channel;</span><br><span class="line"><span class="keyword">import</span> com.rabbitmq.client.DeliverCallback;</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.concurrent.TimeoutException;</span><br><span class="line"></span><br><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * &lt;p&gt;</span></span><br><span class="line"><span class="comment"> *     消费者1</span></span><br><span class="line"><span class="comment"> * &lt;/p&gt;</span></span><br><span class="line"><span class="comment"> *</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@author</span> yww</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_">consumer1</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> <span class="keyword">throws</span> IOException, TimeoutException &#123;</span><br><span class="line">        <span class="type">Channel</span> <span class="variable">channel</span> <span class="operator">=</span> MqUtil.getChannel();</span><br><span class="line">        System.out.println(<span class="string">&quot;消费者**1**等待接收消息， 该消费者处理消息时间较短&quot;</span>);</span><br><span class="line">        <span class="type">DeliverCallback</span> <span class="variable">deliverCallback</span> <span class="operator">=</span> (consumerTag, message) -&gt; &#123;</span><br><span class="line">            System.out.println(<span class="string">&quot;消费者**1**开始消费消息&quot;</span>);</span><br><span class="line">            <span class="type">String</span> <span class="variable">deliverMessage</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">String</span>(message.getBody());</span><br><span class="line">            <span class="comment">// 模拟消费消息需要一秒钟</span></span><br><span class="line">            <span class="keyword">try</span> &#123;</span><br><span class="line">                Thread.sleep(<span class="number">1000</span>);</span><br><span class="line">            &#125; <span class="keyword">catch</span> (InterruptedException e) &#123;</span><br><span class="line">                e.printStackTrace();</span><br><span class="line">            &#125;</span><br><span class="line">            System.out.println(<span class="string">&quot;消费者**1**接收到了消息，消息为： &quot;</span> + deliverMessage);</span><br><span class="line">            <span class="comment">// 手动应答(不批量)</span></span><br><span class="line">            channel.basicAck(message.getEnvelope().getDeliveryTag(), <span class="literal">false</span>);</span><br><span class="line">        &#125;;</span><br><span class="line"></span><br><span class="line">        <span class="type">CancelCallback</span> <span class="variable">cancelCallback</span> <span class="operator">=</span> consumerTag -&gt; &#123;</span><br><span class="line">            System.out.println(<span class="string">&quot;消费者**1**取消了消费消息，当前消息信息为：&quot;</span>);</span><br><span class="line">            System.out.println(consumerTag);</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">        channel.basicConsume(MqUtil.QUEUE_NAME, <span class="literal">false</span>, deliverCallback, cancelCallback);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">&#125;</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><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">package</span> com.yww.rabbitmq;</span><br><span class="line"></span><br><span class="line"><span class="keyword">import</span> com.rabbitmq.client.CancelCallback;</span><br><span class="line"><span class="keyword">import</span> com.rabbitmq.client.Channel;</span><br><span class="line"><span class="keyword">import</span> com.rabbitmq.client.DeliverCallback;</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.concurrent.TimeoutException;</span><br><span class="line"></span><br><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * &lt;p&gt;</span></span><br><span class="line"><span class="comment"> *     消费者2</span></span><br><span class="line"><span class="comment"> * &lt;/p&gt;</span></span><br><span class="line"><span class="comment"> *</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@author</span> yww</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_">consumer2</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> <span class="keyword">throws</span> IOException, TimeoutException &#123;</span><br><span class="line">        <span class="type">Channel</span> <span class="variable">channel</span> <span class="operator">=</span> MqUtil.getChannel();</span><br><span class="line">        System.out.println(<span class="string">&quot;消费者**2**等待接收消息， 该消费者处理消息时间较长&quot;</span>);</span><br><span class="line">        <span class="type">DeliverCallback</span> <span class="variable">deliverCallback</span> <span class="operator">=</span> (consumerTag, message) -&gt; &#123;</span><br><span class="line">            System.out.println(<span class="string">&quot;消费者**2**开始消费消息&quot;</span>);</span><br><span class="line">            <span class="type">String</span> <span class="variable">deliverMessage</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">String</span>(message.getBody());</span><br><span class="line">            <span class="comment">// 模拟消费消息需要二十秒钟</span></span><br><span class="line">            <span class="keyword">try</span> &#123;</span><br><span class="line">                Thread.sleep(<span class="number">30000</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;消费者**2**接收到了消息，消息为： &quot;</span> + deliverMessage);</span><br><span class="line">            <span class="comment">// 手动应答(不批量)</span></span><br><span class="line">            channel.basicAck(message.getEnvelope().getDeliveryTag(), <span class="literal">false</span>);</span><br><span class="line">        &#125;;</span><br><span class="line"></span><br><span class="line">        <span class="type">CancelCallback</span> <span class="variable">cancelCallback</span> <span class="operator">=</span> consumerTag -&gt; &#123;</span><br><span class="line">            System.out.println(<span class="string">&quot;消费者**2**取消了消费消息，当前消息信息为：&quot;</span>);</span><br><span class="line">            System.out.println(consumerTag);</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">        channel.basicConsume(MqUtil.QUEUE_NAME, <span class="literal">false</span>, deliverCallback, cancelCallback);</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><p>接下来开始测试。</p><blockquote><p>开启生产者和这两个消费者，当消费者2出现开始消费的字样，即进入了接收消费的回调之后，停止程序运行，模拟消息丢失。</p></blockquote><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br></pre></td><td class="code"><pre><span class="line">Producer的控制台</span><br><span class="line">aa</span><br><span class="line">发送消息完成，本次的发送的消息为----aa</span><br><span class="line">bb</span><br><span class="line">发送消息完成，本次的发送的消息为----bb</span><br><span class="line">cc</span><br><span class="line">发送消息完成，本次的发送的消息为----cc</span><br><span class="line"></span><br><span class="line">Comsumer1的控制台</span><br><span class="line">消费者**1**等待接收消息， 该消费者处理消息时间较短</span><br><span class="line">消费者**1**开始消费消息</span><br><span class="line">消费者**1**接收到了消息，消息为： bb</span><br><span class="line">-------------------此时关闭消费者2-------------------------</span><br><span class="line">消费者**1**开始消费消息</span><br><span class="line">消费者**1**接收到了消息，消息为： cc</span><br><span class="line"></span><br><span class="line">Comsumer2的控制台</span><br><span class="line">消费者**2**等待接收消息， 该消费者处理消息时间较长</span><br><span class="line">消费者**2**开始消费消息</span><br><span class="line">消费者**2**接收到了消息，消息为： aa</span><br><span class="line">消费者**2**开始消费消息</span><br><span class="line">-------------------此时关闭消费者2-------------------------</span><br></pre></td></tr></table></figure><p>从这个测试就可以看到，<code>cc</code>这个消息原本应该是在消费者2中消费的，但是我停下了消费者2的进程之后，会发现这个消息转交到了消费者1中执行。</p><h2 id="RabbitMQ持久化"><a href="#RabbitMQ持久化" class="headerlink" title="RabbitMQ持久化"></a>RabbitMQ持久化</h2><p>消息应答是消费者出现问题的时候的解决方法，但是当RabbitMQ服务宕机之后，消息还是可能会丢失，所以这就需要启动RabbitMQ的持久化机制了。</p><h3 id="队列实现持久化"><a href="#队列实现持久化" class="headerlink" title="队列实现持久化"></a>队列实现持久化</h3><p>之前是有提到过的，队列不开启持久化的话，MQ服务宕机重启的时候，队列就会丢失（因为是临时存储的），所以需要在声明队列的时候就要开启持久化。</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="comment">/*</span></span><br><span class="line"><span class="comment"> * 队列持久化的开启主要是第二个参数（durable）</span></span><br><span class="line"><span class="comment"> * true表示声明的是一个持久化队列，该队列会在服务器重启后依旧存活</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line">Queue.DeclareOk <span class="title function_">queueDeclare</span><span class="params">(String queue, <span class="type">boolean</span> durable, <span class="type">boolean</span> exclusive, <span class="type">boolean</span> autoDelete,</span></span><br><span class="line"><span class="params">                     Map&lt;String, Object&gt; arguments)</span> <span class="keyword">throws</span> IOException;</span><br></pre></td></tr></table></figure><h3 id="消息实现持久化"><a href="#消息实现持久化" class="headerlink" title="消息实现持久化"></a>消息实现持久化</h3><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></pre></td><td class="code"><pre><span class="line">   <span class="comment">/*</span></span><br><span class="line"><span class="comment">    * 消息持久化的开启主要是第三个参数（props）</span></span><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_">basicPublish</span><span class="params">(String exchange, String routingKey, BasicProperties props, <span class="type">byte</span>[] body)</span> <span class="keyword">throws</span> IOException;</span><br><span class="line"></span><br><span class="line"><span class="comment">// 需要配置一个常数（MessageProperties.PERSISTENT_TEXT_PLAIN）</span></span><br><span class="line">channel.basicPublish(<span class="string">&quot;&quot;</span>, Constant.QUEUE_NAME, MessageProperties.PERSISTENT_TEXT_PLAIN, message.getBytes());</span><br></pre></td></tr></table></figure><h3 id="持久化不代表不丢失"><a href="#持久化不代表不丢失" class="headerlink" title="持久化不代表不丢失"></a>持久化不代表不丢失</h3><p>队列和消息都实现持久化之后，并不能保证消息不会丢失，因为有很多特殊情况会影响结果，比如持久化到磁盘的过程中服务宕机，还是会可能丢失数据的，所以这种方法并不能避免一些特殊情况。所以还需要参考下述的发布确认。</p><h2 id="发布确认"><a href="#发布确认" class="headerlink" title="发布确认"></a>发布确认</h2><p>生产者将信道设置成 confirm 模式，一旦信道进入 confirm 模式，所有在该信道上面发布的消息都将会被指派一个唯一的 ID(从 1 开始)，一旦消息被投递到所有匹配的队列之后，broker 就会发送一个确认给生产者(包含消息的唯一 ID)，这就使得生产者知道消息已经正确到达目的队列了，如果消息和队列是可持久化的，那么确认消息会在将消息写入磁盘之后发出，broker 回传给生产者的确认消息中 delivery-tag 域包含了确认消息的序列号，此外 broker 也可以设置<code>basic.ack</code>的 multiple 域，表示到这个序列号之前的所有消息都已经得到了处理。</p><p>confirm 模式最大的好处在于他是异步的，一旦发布一条消息，生产者应用程序就可以在等信道返回确认的同时继续发送下一条消息，当消息最终得到确认之后，生产者应用便可以通过回调方法来处理该确认消息，如果 RabbitMQ 因为自身内部错误导致消息丢失，就会发送一条 nack 消息，生产者应用程序同样可以在回调方法中处理该 nack 消息。</p><h3 id="开启发布确认"><a href="#开启发布确认" class="headerlink" title="开启发布确认"></a>开启发布确认</h3><p>开启发布确认是在生产者端的，所以只需要在生产者端标记信道，开启即可，主要的方法就是这个。</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></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">Confirm.SelectOk <span class="title function_">confirmSelect</span><span class="params">()</span> <span class="keyword">throws</span> IOException;</span><br><span class="line"></span><br><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * 在生产者端开启即可</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line">channel.confirmSelect();</span><br></pre></td></tr></table></figure><p>这个方法是将信道标记成<code>confirm</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></pre></td><td class="code"><pre><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * 开启等待直到上次调用依赖发布的所有消息都被broker返回ack或者是nack。</span></span><br><span class="line"><span class="comment"> * 如果在非confirm信道上调用该方法，会抛出InterruptedException异常。</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="type">boolean</span> <span class="title function_">waitForConfirms</span><span class="params">()</span> <span class="keyword">throws</span> InterruptedException;</span><br><span class="line"></span><br><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * 开启等待直到自上次调用以来所有发布的消息都被broker返回ack或者是nack，或者是直到超时结束。</span></span><br><span class="line"><span class="comment"> * 如果超时过期就会抛出TimeoutException异常。</span></span><br><span class="line"><span class="comment"> * 如果在非confirm信道上调用该方法，会抛出InterruptedException异常。</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="type">boolean</span> <span class="title function_">waitForConfirms</span><span class="params">(<span class="type">long</span> timeout)</span> <span class="keyword">throws</span> InterruptedException, TimeoutException;</span><br><span class="line"></span><br><span class="line"><span class="comment">/** </span></span><br><span class="line"><span class="comment"> * 开启等待直到上次调用依赖发布的所有消息都被broker返回ack或者是nack。</span></span><br><span class="line"><span class="comment"> * 如果消息被broker返回了nack，会抛出IOException异常。</span></span><br><span class="line"><span class="comment"> * 如果在非confirm信道上调用该方法，会抛出InterruptedException异常。</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"> <span class="keyword">void</span> <span class="title function_">waitForConfirmsOrDie</span><span class="params">()</span> <span class="keyword">throws</span> IOException, InterruptedException;</span><br><span class="line"></span><br><span class="line"><span class="comment">/** </span></span><br><span class="line"><span class="comment"> * 开启等待直到上次调用依赖发布的所有消息都被broker返回ack或者是nack，或者是直到超时结束。</span></span><br><span class="line"><span class="comment"> * 如果消息被broker返回了nack，会抛出IOException异常。</span></span><br><span class="line"><span class="comment"> * 如果超时过期就会抛出TimeoutException异常。</span></span><br><span class="line"><span class="comment"> * 如果在非confirm信道上调用该方法，会抛出InterruptedException异常。</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="keyword">void</span> <span class="title function_">waitForConfirmsOrDie</span><span class="params">(<span class="type">long</span> timeout)</span> <span class="keyword">throws</span> IOException, InterruptedException, TimeoutException;</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></pre></td><td class="code"><pre><span class="line"><span class="type">boolean</span> <span class="title function_">waitForConfirms</span><span class="params">(<span class="type">long</span> timeout)</span> <span class="keyword">throws</span> InterruptedException, TimeoutException;</span><br></pre></td></tr></table></figure><h3 id="单个确认发布"><a href="#单个确认发布" class="headerlink" title="单个确认发布"></a>单个确认发布</h3><p>最简单的一种发布确认方式，是一种同步的确认发布，也就是说发布一个消息之后，只有它被确认的时候才会返回，如果在指定时间范围内没有被确认，就会抛出异常。</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><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><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.yww.rabbitmq.publish;</span><br><span class="line"></span><br><span class="line"><span class="keyword">import</span> com.rabbitmq.client.Channel;</span><br><span class="line"><span class="keyword">import</span> com.yww.rabbitmq.MqUtil;</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.concurrent.TimeoutException;</span><br><span class="line"></span><br><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * &lt;p&gt;</span></span><br><span class="line"><span class="comment"> *     单个确认发布</span></span><br><span class="line"><span class="comment"> * &lt;/p&gt;</span></span><br><span class="line"><span class="comment"> *</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@author</span> yww</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_">PublishOne</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> <span class="keyword">throws</span> IOException, TimeoutException, InterruptedException &#123;</span><br><span class="line">        <span class="type">Channel</span> <span class="variable">channel</span> <span class="operator">=</span> MqUtil.getChannel();</span><br><span class="line">        <span class="comment">// 开启发布确认</span></span><br><span class="line">        channel.confirmSelect();</span><br><span class="line">        channel.queueDeclare(MqUtil.QUEUE_NAME, <span class="literal">false</span>, <span class="literal">false</span>, <span class="literal">false</span>, <span class="literal">null</span>);</span><br><span class="line">        <span class="type">int</span> <span class="variable">total</span> <span class="operator">=</span> <span class="number">0</span>;</span><br><span class="line">        <span class="type">long</span> <span class="variable">beginTime</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">100</span>; i++) &#123;</span><br><span class="line">            <span class="type">String</span> <span class="variable">msg</span> <span class="operator">=</span> String.valueOf(i);</span><br><span class="line">            channel.basicPublish(<span class="string">&quot;&quot;</span>, MqUtil.QUEUE_NAME, <span class="literal">null</span>, msg.getBytes());</span><br><span class="line">            <span class="comment">// 发布确认，返回true则表示成功发布，返回false可以重新发布，也可以使用超时时间。</span></span><br><span class="line">            <span class="type">boolean</span> <span class="variable">flag</span> <span class="operator">=</span> channel.waitForConfirms();</span><br><span class="line">            <span class="keyword">if</span> (flag) &#123;</span><br><span class="line">                total++;</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;</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">        System.out.println(<span class="string">&quot;发布了100条单独确认消息耗时为：&quot;</span> + (endTime - beginTime) + <span class="string">&quot;ms&quot;</span>);</span><br><span class="line">        System.out.println(<span class="string">&quot;成功发布了消息数为：&quot;</span> + total);</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><figure class="highlight plaintext"><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">发布了1000条单独确认消息耗时为：29474ms</span><br><span class="line">成功发布了消息数为：1000</span><br></pre></td></tr></table></figure><h3 id="批量发布确认"><a href="#批量发布确认" class="headerlink" title="批量发布确认"></a>批量发布确认</h3><p>单个确认的效率很慢，所以可以先发布一批消息然后一起确认可以极大提高吞吐量，这种方案也是同步的发布确认方式。</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><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><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">package</span> com.yww.rabbitmq.publish;</span><br><span class="line"></span><br><span class="line"><span class="keyword">import</span> com.rabbitmq.client.Channel;</span><br><span class="line"><span class="keyword">import</span> com.yww.rabbitmq.MqUtil;</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.concurrent.TimeoutException;</span><br><span class="line"></span><br><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * &lt;p&gt;</span></span><br><span class="line"><span class="comment"> *     批量确认发布</span></span><br><span class="line"><span class="comment"> * &lt;/p&gt;</span></span><br><span class="line"><span class="comment"> *</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@author</span> yww</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_">PublishBatch</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> <span class="keyword">throws</span> IOException, TimeoutException, InterruptedException &#123;</span><br><span class="line">        <span class="type">Channel</span> <span class="variable">channel</span> <span class="operator">=</span> MqUtil.getChannel();</span><br><span class="line">        <span class="comment">// 开启发布确认</span></span><br><span class="line">        channel.confirmSelect();</span><br><span class="line">        channel.queueDeclare(MqUtil.QUEUE_NAME, <span class="literal">false</span>, <span class="literal">false</span>, <span class="literal">false</span>, <span class="literal">null</span>);</span><br><span class="line">        <span class="comment">// 批量确认消息数</span></span><br><span class="line">        <span class="type">int</span> <span class="variable">batchSize</span> <span class="operator">=</span> <span class="number">100</span>;</span><br><span class="line">        <span class="comment">// 未确认消息个数</span></span><br><span class="line">        <span class="type">int</span> <span class="variable">total</span> <span class="operator">=</span> <span class="number">0</span>;</span><br><span class="line">        <span class="type">long</span> <span class="variable">beginTime</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">1000</span>; i++) &#123;</span><br><span class="line">            <span class="type">String</span> <span class="variable">msg</span> <span class="operator">=</span> String.valueOf(i);</span><br><span class="line">            channel.basicPublish(<span class="string">&quot;&quot;</span>, MqUtil.QUEUE_NAME, <span class="literal">null</span>, msg.getBytes());</span><br><span class="line">            total++;</span><br><span class="line">            <span class="comment">// 如果发布的消息数等于设置的批量确认消息数，就进行发布确认</span></span><br><span class="line">            <span class="keyword">if</span> (total == batchSize) &#123;</span><br><span class="line">                channel.waitForConfirms();</span><br><span class="line">                total = <span class="number">0</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">if</span> (total &gt; <span class="number">0</span>) &#123;</span><br><span class="line">            channel.waitForConfirms();</span><br><span class="line">        &#125;</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">        System.out.println(<span class="string">&quot;发布了1000条批量确认消息耗时为：&quot;</span> + (endTime - beginTime) + <span class="string">&quot;ms&quot;</span>);</span><br><span class="line">        System.out.println(<span class="string">&quot;未确认的消息数为：&quot;</span> + total);</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><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="number">1000</span>条批量确认消息耗时为：292ms</span><br><span class="line">未确认的消息数为：<span class="number">0</span></span><br></pre></td></tr></table></figure><h3 id="异步确认发布"><a href="#异步确认发布" class="headerlink" title="异步确认发布"></a>异步确认发布</h3><p>异步确认发布在比同步的方式最重要的是效率能高很多，而且因为也是单个确认的，所以也是很可靠的。</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></pre></td><td class="code"><pre><span class="line"><span class="keyword">void</span> <span class="title function_">addConfirmListener</span><span class="params">(ConfirmListener listener)</span>;</span><br><span class="line"><span class="comment">// 一般使用下面这个方法，能知道哪些成功，哪些失败</span></span><br><span class="line">   ConfirmListener <span class="title function_">addConfirmListener</span><span class="params">(ConfirmCallback ackCallback, ConfirmCallback nackCallback)</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><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><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.yww.rabbitmq.publish;</span><br><span class="line"></span><br><span class="line"><span class="keyword">import</span> com.rabbitmq.client.Channel;</span><br><span class="line"><span class="keyword">import</span> com.rabbitmq.client.ConfirmCallback;</span><br><span class="line"><span class="keyword">import</span> com.yww.rabbitmq.MqUtil;</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.concurrent.TimeoutException;</span><br><span class="line"></span><br><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * &lt;p&gt;</span></span><br><span class="line"><span class="comment"> *     异步确认发布</span></span><br><span class="line"><span class="comment"> * &lt;/p&gt;</span></span><br><span class="line"><span class="comment"> *</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@author</span> yww</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_">PublishAsync</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> <span class="keyword">throws</span> IOException, TimeoutException, InterruptedException &#123;</span><br><span class="line">        <span class="type">Channel</span> <span class="variable">channel</span> <span class="operator">=</span> MqUtil.getChannel();</span><br><span class="line">        <span class="comment">// 开启发布确认</span></span><br><span class="line">        channel.confirmSelect();</span><br><span class="line">        channel.queueDeclare(MqUtil.QUEUE_NAME, <span class="literal">false</span>, <span class="literal">false</span>, <span class="literal">false</span>, <span class="literal">null</span>);</span><br><span class="line">        <span class="comment">// 监听器消息确认成功的回调</span></span><br><span class="line">        <span class="type">ConfirmCallback</span> <span class="variable">ackCallback</span> <span class="operator">=</span> (deliveryTag, multiple) -&gt; &#123;</span><br><span class="line">            System.out.println(<span class="string">&quot;这个确认消息为&quot;</span> + deliveryTag);</span><br><span class="line">        &#125;;</span><br><span class="line">        <span class="comment">// 监听器消息确认失败的回调（这里可以直接重发，或者是先记录下来）</span></span><br><span class="line">        <span class="type">ConfirmCallback</span> <span class="variable">nackCallback</span> <span class="operator">=</span> (deliveryTag, multiple) -&gt; &#123;</span><br><span class="line">            System.out.println(<span class="string">&quot;这个未确认消息为&quot;</span> + deliveryTag);</span><br><span class="line">        &#125;;</span><br><span class="line">        <span class="comment">// 准备一个监听器，监听哪些消息发布成功或者失败(异步通知)</span></span><br><span class="line">        channel.addConfirmListener(ackCallback, nackCallback);</span><br><span class="line">        <span class="type">long</span> <span class="variable">beginTime</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">1000</span>; i++) &#123;</span><br><span class="line">            <span class="type">String</span> <span class="variable">msg</span> <span class="operator">=</span> String.valueOf(i);</span><br><span class="line">            <span class="comment">// 这里只负责发送消息，不负责确认，确认消息由监听器负责</span></span><br><span class="line">            channel.basicPublish(<span class="string">&quot;&quot;</span>, MqUtil.QUEUE_NAME, <span class="literal">null</span>, msg.getBytes());</span><br><span class="line">        &#125;</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">        System.out.println(<span class="string">&quot;发布了1000条批量确认消息耗时为：&quot;</span> + (endTime - beginTime) + <span class="string">&quot;ms&quot;</span>);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>测试结果如下。</p><figure class="highlight plaintext"><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">发布了1000条批量确认消息耗时为：33ms</span><br><span class="line">(...省略了成功打印)</span><br></pre></td></tr></table></figure><h3 id="异步未确认消息的处理"><a href="#异步未确认消息的处理" class="headerlink" title="异步未确认消息的处理"></a>异步未确认消息的处理</h3><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></pre></td><td class="code"><pre><span class="line"><span class="keyword">package</span> com.yww.rabbitmq.publish;</span><br><span class="line"></span><br><span class="line"><span class="keyword">import</span> com.rabbitmq.client.Channel;</span><br><span class="line"><span class="keyword">import</span> com.rabbitmq.client.ConfirmCallback;</span><br><span class="line"><span class="keyword">import</span> com.yww.rabbitmq.MqUtil;</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.concurrent.ConcurrentNavigableMap;</span><br><span class="line"><span class="keyword">import</span> java.util.concurrent.ConcurrentSkipListMap;</span><br><span class="line"><span class="keyword">import</span> java.util.concurrent.TimeoutException;</span><br><span class="line"></span><br><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * &lt;p&gt;</span></span><br><span class="line"><span class="comment"> *     处理异步方式未被确认的消息</span></span><br><span class="line"><span class="comment"> * &lt;/p&gt;</span></span><br><span class="line"><span class="comment"> *</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@author</span> yww</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_">publishDeal</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> <span class="keyword">throws</span> IOException, TimeoutException, InterruptedException &#123;</span><br><span class="line">        <span class="type">Channel</span> <span class="variable">channel</span> <span class="operator">=</span> MqUtil.getChannel();</span><br><span class="line">        channel.confirmSelect();</span><br><span class="line">        channel.queueDeclare(MqUtil.QUEUE_NAME, <span class="literal">false</span>, <span class="literal">false</span>, <span class="literal">false</span>, <span class="literal">null</span>);</span><br><span class="line">        <span class="comment">// 准备一个队列存储消息</span></span><br><span class="line">        ConcurrentSkipListMap&lt;Long, String&gt; map = <span class="keyword">new</span> <span class="title class_">ConcurrentSkipListMap</span>&lt;&gt;();</span><br><span class="line">        <span class="comment">// 删除掉已经确认的消息</span></span><br><span class="line">        <span class="type">ConfirmCallback</span> <span class="variable">ackCallback</span> <span class="operator">=</span> (deliveryTag, multiple) -&gt; &#123;</span><br><span class="line">            <span class="comment">// 如果是批量的就一起清理</span></span><br><span class="line">            <span class="keyword">if</span> (multiple) &#123;</span><br><span class="line">                <span class="comment">// 获取到确认的消息</span></span><br><span class="line">                ConcurrentNavigableMap&lt;Long, String&gt; confirmed = map.headMap(deliveryTag);</span><br><span class="line">                confirmed.clear();</span><br><span class="line">            &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">                map.remove(deliveryTag);</span><br><span class="line">            &#125;</span><br><span class="line">            System.out.println(<span class="string">&quot;确认的消息为：&quot;</span> + deliveryTag);</span><br><span class="line">        &#125;;</span><br><span class="line">        <span class="type">ConfirmCallback</span> <span class="variable">nackCallback</span> <span class="operator">=</span> (deliveryTag, multiple) -&gt; &#123;</span><br><span class="line">            <span class="type">String</span> <span class="variable">nackMsg</span> <span class="operator">=</span> map.get(deliveryTag);</span><br><span class="line">            System.out.println(<span class="string">&quot;这个未确认消息的内容为：&quot;</span> + nackMsg);</span><br><span class="line">        &#125;;</span><br><span class="line">        channel.addConfirmListener(ackCallback, nackCallback);</span><br><span class="line">        <span class="type">long</span> <span class="variable">beginTime</span> <span class="operator">=</span> System.currentTimeMillis();</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">1000</span>; i++) &#123;</span><br><span class="line">            <span class="type">String</span> <span class="variable">msg</span> <span class="operator">=</span> String.valueOf(i);</span><br><span class="line">            <span class="type">long</span> <span class="variable">num</span> <span class="operator">=</span> channel.getNextPublishSeqNo();</span><br><span class="line">            channel.basicPublish(<span class="string">&quot;&quot;</span>, MqUtil.QUEUE_NAME, <span class="literal">null</span>, msg.getBytes());</span><br><span class="line">            <span class="comment">// 记录发送的所有消息</span></span><br><span class="line">            map.put(num, msg);</span><br><span class="line">        &#125;</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">        System.out.println(<span class="string">&quot;发布了1000条批量确认消息耗时为：&quot;</span> + (endTime - beginTime) + <span class="string">&quot;ms&quot;</span>);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h2 id="不公平调度"><a href="#不公平调度" class="headerlink" title="不公平调度"></a>不公平调度</h2><p>刚刚学习的时候是可以知道RabbitMQ默认是循环调度，也就是轮询的，这种调度方法，有优点也有缺点，有时候公平并不是一件好事，所以我们可以设置这个消费者的工作内容。</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="comment">/**</span></span><br><span class="line"><span class="comment">    * 设置工作量</span></span><br><span class="line"><span class="comment">    * 可以设置0到65535之间的数</span></span><br><span class="line"><span class="comment">    * <span class="doctag">@param</span> prefetchCount 设置该线程最多持有的任务数，如果是0则代表无限制</span></span><br><span class="line"><span class="comment">    */</span></span><br><span class="line">   <span class="keyword">void</span> <span class="title function_">basicQos</span><span class="params">(<span class="type">int</span> prefetchCount)</span> <span class="keyword">throws</span> IOException;</span><br><span class="line"></span><br><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment">    * 这样表示这个工作线程，在处理并确认一条消息的时间段</span></span><br><span class="line"><span class="comment">    * 不能向该工作线程发送新的消息，将会把消息分派给不忙的工作线程</span></span><br><span class="line"><span class="comment">    */</span></span><br><span class="line">channel.basicQos(<span class="number">1</span>);</span><br></pre></td></tr></table></figure><blockquote><p>注意当所有工作线程都很忙，你的队列有可能会被填满。</p><p>所以需要你添加更多的工作线程，或者是使用其他的策略。</p></blockquote><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="comment">/**</span></span><br><span class="line"><span class="comment">   * 请求特定的“服务质量”设置。</span></span><br><span class="line"><span class="comment">   * &lt;p&gt;</span></span><br><span class="line"><span class="comment">* 这些设置限制了服务器在需要确认之前将交付给消费者的数据量。</span></span><br><span class="line"><span class="comment">* 因此，它们提供了一种用户发起的流控制方法。</span></span><br><span class="line"><span class="comment">   * &lt;p&gt;</span></span><br><span class="line"><span class="comment">   * 注意预取计数必须在0到65535之间(unsigned short in AMQP 0-9-1)。</span></span><br><span class="line"><span class="comment">   *</span></span><br><span class="line"><span class="comment">   * <span class="doctag">@param</span> prefetchSize  服务器将传递的最大内容量(以字节为单位)，如果无限制，则为0</span></span><br><span class="line"><span class="comment">   * <span class="doctag">@param</span> prefetchCount 服务器将传递的最大消息数，如果无限制，则为0</span></span><br><span class="line"><span class="comment">   * <span class="doctag">@param</span> global        如果设置应应用于整个通道而不是每个使用者，则为true</span></span><br><span class="line"><span class="comment">   * <span class="doctag">@throws</span> java.io.IOException 如果遇到错误</span></span><br><span class="line"><span class="comment">   * <span class="doctag">@see</span> com.rabbitmq.client.AMQP.Basic.Qos</span></span><br><span class="line"><span class="comment">   */</span></span><br><span class="line">  <span class="keyword">void</span> <span class="title function_">basicQos</span><span class="params">(<span class="type">int</span> prefetchSize, <span class="type">int</span> prefetchCount, <span class="type">boolean</span> global)</span> <span class="keyword">throws</span> IOException;</span><br></pre></td></tr></table></figure><h1 id="Publish-Subscribe"><a href="#Publish-Subscribe" class="headerlink" title="Publish/Subscribe"></a>Publish/Subscribe</h1><p>在上述的示例中，使用了工作队列，工作队列会将每个任务交付给一个工作线程。当然我们也可以实现向多个消费者传递一条消息，这种模式被称为<code>Publish/Subscribe</code>，也叫<code>发布/订阅</code>。</p><h2 id="交换机（Exchanges）"><a href="#交换机（Exchanges）" class="headerlink" title="交换机（Exchanges）"></a>交换机（Exchanges）</h2><p>RabbitMQ消息传递模型的核心思想是生产者从不直接向队列中发送任何消息。实际上生产者经常甚至根本不知道消息是否会被传送到哪个队列。</p><p>实际情况是生产者只能将消息发送到交换机中，交换机的任务也很简单，一方面他接收来自生产者的消息，另一方面将消息推入队列。</p><p>是应该把这些消息放到特定队列，还是说把他们丢到许多队列中，或者说是丢弃，这些就是由交换机的类型来决定了。</p><p>交换机有几种可用的类型。</p><ol><li>direct（直接）</li><li>topic（主题）</li><li>headers（标题）</li><li>fanout（扇出）</li></ol><h2 id="无名交换机"><a href="#无名交换机" class="headerlink" title="无名交换机"></a>无名交换机</h2><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">channel.basicPublish(<span class="string">&quot;&quot;</span>, <span class="string">&quot;hello&quot;</span>, <span class="literal">null</span>, <span class="string">&quot;hello world&quot;</span>.getBytes());</span><br></pre></td></tr></table></figure><p>第一个参数是交换机的名称，空字符串就表示默认或者说是无名交换机。消息能路由发送到队列中其实就是由<code>routingKey</code>指定的。</p><h2 id="临时队列"><a href="#临时队列" class="headerlink" title="临时队列"></a>临时队列</h2><p>临时队列用于临时存储消息，一旦断开了消费者的连接，队列就会被自动删除。</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"><span class="type">String</span> <span class="variable">queueName</span> <span class="operator">=</span> channel.queueDeclare().getQueue();</span><br></pre></td></tr></table></figure>]]></content:encoded>
            
            
            <category domain="https://yww52.com/categories/%E6%88%91%E7%9A%84%E6%8A%80%E6%9C%AF%E5%AD%A6%E4%B9%A0%E7%89%A9%E8%AF%AD%E6%9E%9C%E7%84%B6%E6%9C%89%E9%97%AE%E9%A2%98/">我的技术学习物语果然有问题</category>
            
            <category domain="https://yww52.com/categories/%E6%88%91%E7%9A%84%E6%8A%80%E6%9C%AF%E5%AD%A6%E4%B9%A0%E7%89%A9%E8%AF%AD%E6%9E%9C%E7%84%B6%E6%9C%89%E9%97%AE%E9%A2%98/%E5%BC%80%E5%8F%91%E6%8A%80%E8%83%BD/">开发技能</category>
            
            
            
            <comments>https://yww52.com/posts/b543ced0/#disqus_thread</comments>
            
        </item>
        
        <item>
            <title>SpringCloud</title>
            <link>https://yww52.com/posts/96a41905/</link>
            <guid>https://yww52.com/posts/96a41905/</guid>
            <pubDate>Thu, 16 Dec 2021 16:00:00 GMT</pubDate>
            
            <description>SpringCloud的学习笔记。</description>
            
            
            
            <content:encoded><![CDATA[<img src="https://img.yww52.com/2021/12/2021-12-17top_img.jpg" alt="SpringCloud" style="max-width:100%;height:auto;margin-bottom:15px;"
  /><h1 id="服务注册中心"><a href="#服务注册中心" class="headerlink" title="服务注册中心"></a>服务注册中心</h1><h2 id="Eureka（停更）"><a href="#Eureka（停更）" class="headerlink" title="Eureka（停更）"></a>Eureka（停更）</h2><p>Eureka是Netflix开发的一个服务发现框架，SpringCloud将它集成在其子项目<code>spring-cloud-netflix</code>中，主要的功能是<code>服务注册</code>和<code>服务发现</code>。</p><p>Eureka有两个组件，对应了两个角色。服务中心和服务的客户端。</p><h3 id="Eureka-Server"><a href="#Eureka-Server" class="headerlink" title="Eureka Server"></a>Eureka Server</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></pre></td><td class="code"><pre><span class="line"><span class="tag">&lt;<span class="name">dependency</span>&gt;</span></span><br><span class="line">       <span class="tag">&lt;<span class="name">groupId</span>&gt;</span>org.springframework.cloud<span class="tag">&lt;/<span class="name">groupId</span>&gt;</span></span><br><span class="line">       <span class="tag">&lt;<span class="name">artifactId</span>&gt;</span>spring-cloud-starter-netflix-eureka-server<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><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">eureka:</span></span><br><span class="line">  <span class="attr">instance:</span></span><br><span class="line">    <span class="comment"># eureka服务端的实例名称</span></span><br><span class="line">    <span class="attr">hostname:</span> <span class="string">localhost:7001</span></span><br><span class="line">  <span class="attr">client:</span></span><br><span class="line">    <span class="comment"># false表示不向注册中心注册自己这个微服务</span></span><br><span class="line">    <span class="attr">register-with-eureka:</span> <span class="literal">false</span></span><br><span class="line">    <span class="comment"># false表示当前服务是注册中心，并不需要去检索服务</span></span><br><span class="line">    <span class="attr">fetch-registry:</span> <span class="literal">false</span></span><br><span class="line">    <span class="attr">service-url:</span></span><br><span class="line">      <span class="comment"># 设置与Eureka Server交互的地址查询服务和注册服务依赖的地址</span></span><br><span class="line">      <span class="attr">defaultZone:</span> <span class="string">http://localhost:7001/eureka</span></span><br></pre></td></tr></table></figure><p>开启Eureka 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></pre></td><td class="code"><pre><span class="line"><span class="meta">@EnableEurekaServer</span></span><br><span class="line"><span class="meta">@SpringBootApplication</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">EurekaMain</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(EurekaMain.class, args);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h3 id="Eureka-Client"><a href="#Eureka-Client" class="headerlink" title="Eureka Client"></a>Eureka Client</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></pre></td><td class="code"><pre><span class="line"><span class="tag">&lt;<span class="name">dependency</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">groupId</span>&gt;</span>org.springframework.cloud<span class="tag">&lt;/<span class="name">groupId</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">artifactId</span>&gt;</span>spring-cloud-starter-netflix-eureka-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>编写配置文件。</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></pre></td><td class="code"><pre><span class="line"><span class="attr">eureka:</span></span><br><span class="line">  <span class="attr">client:</span></span><br><span class="line">    <span class="comment"># 表示是否将当前服务注册进注册中心，默认为true</span></span><br><span class="line">    <span class="attr">register-with-eureka:</span> <span class="literal">true</span></span><br><span class="line">    <span class="comment"># 是否从注册中心抓取已有的注册信息，默认为true。单节点无所谓，集群一定要为true才能配合ribbon使用负载均衡</span></span><br><span class="line">    <span class="attr">fetch-registry:</span> <span class="literal">true</span></span><br><span class="line">    <span class="attr">service-url:</span></span><br><span class="line">      <span class="comment"># 服务中心的地址</span></span><br><span class="line">      <span class="attr">defaultZone:</span> <span class="string">http://localhost:7001/eureka</span></span><br><span class="line">  <span class="attr">instance:</span></span><br><span class="line">    <span class="comment"># 实例名称</span></span><br><span class="line">    <span class="attr">instance-id:</span> <span class="string">payment8001</span></span><br><span class="line">    <span class="comment"># 访问路径显示IP地址</span></span><br><span class="line">    <span class="attr">prefer-ip-address:</span> <span class="literal">true</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></pre></td><td class="code"><pre><span class="line"><span class="meta">@EnableEurekaClient</span></span><br><span class="line"><span class="meta">@SpringBootApplication</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">PaymentMain</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(PaymentMain.class, args);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h3 id="Eureka的集群"><a href="#Eureka的集群" class="headerlink" title="Eureka的集群"></a>Eureka的集群</h3><p>集群的配置主要就是要注意，两两注册。</p><p>两个Server端。</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></pre></td><td class="code"><pre><span class="line"><span class="attr">eureka:</span></span><br><span class="line">  <span class="attr">instance:</span></span><br><span class="line">    <span class="attr">hostname:</span> <span class="string">eureka7001.com</span></span><br><span class="line">  <span class="attr">client:</span></span><br><span class="line">    <span class="attr">register-with-eureka:</span> <span class="literal">false</span></span><br><span class="line">    <span class="attr">fetch-registry:</span> <span class="literal">false</span></span><br><span class="line">    <span class="attr">service-url:</span></span><br><span class="line">      <span class="comment"># 集群就指向其他服务中心的地址，单机版就指向自己</span></span><br><span class="line">      <span class="attr">defaultZone:</span> <span class="string">http://eureka7002.com:7002/eureka</span></span><br></pre></td></tr></table></figure><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></pre></td><td class="code"><pre><span class="line"><span class="attr">eureka:</span></span><br><span class="line">  <span class="attr">instance:</span></span><br><span class="line">    <span class="attr">hostname:</span> <span class="string">eureka7002.com</span></span><br><span class="line">  <span class="attr">client:</span></span><br><span class="line">    <span class="attr">register-with-eureka:</span> <span class="literal">false</span></span><br><span class="line">    <span class="attr">fetch-registry:</span> <span class="literal">false</span></span><br><span class="line">    <span class="attr">service-url:</span></span><br><span class="line">      <span class="comment"># 集群就指向其他服务中心的地址，单机版就指向自己</span></span><br><span class="line">      <span class="attr">defaultZone:</span> <span class="string">http://eureka7001.com:7001/eureka</span></span><br></pre></td></tr></table></figure><p>Client端的配置。</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></pre></td><td class="code"><pre><span class="line"><span class="attr">eureka:</span></span><br><span class="line">  <span class="attr">client:</span></span><br><span class="line">    <span class="attr">register-with-eureka:</span> <span class="literal">true</span></span><br><span class="line">    <span class="attr">fetch-registry:</span> <span class="literal">true</span></span><br><span class="line">    <span class="attr">service-url:</span></span><br><span class="line">      <span class="comment"># 将全部的注册中心的地址写入</span></span><br><span class="line">      <span class="attr">defaultZone:</span> <span class="string">http://eureka7001.com:7001/eureka,http://eureka7002.com:7002/eureka</span></span><br><span class="line">  <span class="attr">instance:</span></span><br><span class="line">    <span class="attr">instance-id:</span> <span class="string">payment8001</span></span><br><span class="line">    <span class="attr">prefer-ip-address:</span> <span class="literal">true</span></span><br></pre></td></tr></table></figure><h3 id="服务发现"><a href="#服务发现" class="headerlink" title="服务发现"></a>服务发现</h3><p>通过发现Client的服务发现，来获取Client端的服务信息。</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></pre></td><td class="code"><pre><span class="line"><span class="meta">@SpringBootApplication</span></span><br><span class="line"><span class="meta">@EnableEurekaClient</span></span><br><span class="line"><span class="meta">@EnableDiscoveryClient</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">PaymentMain</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(PaymentMain.class, args);</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><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><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">@Slf4j</span></span><br><span class="line"><span class="meta">@RequestMapping(&quot;/payment&quot;)</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">PaymentController</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">@Resource</span></span><br><span class="line">    <span class="keyword">private</span> DiscoveryClient discoveryClient;</span><br><span class="line"></span><br><span class="line">    <span class="comment">/**</span></span><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;/discovery&quot;)</span></span><br><span class="line">    <span class="keyword">public</span> Result&lt;DiscoveryClient&gt; <span class="title function_">discovery</span><span class="params">()</span> &#123;</span><br><span class="line">        <span class="comment">// 获取注册的微服务列表信息</span></span><br><span class="line">        List&lt;String&gt; services = discoveryClient.getServices();</span><br><span class="line">        <span class="keyword">for</span> (String service : services) &#123;</span><br><span class="line">            log.info(<span class="string">&quot;服务------&quot;</span> + service);</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="comment">// 根据具体的微服务ID获取其中所有的实例</span></span><br><span class="line">        List&lt;ServiceInstance&gt; instances = discoveryClient.getInstances(<span class="string">&quot;CLOUD-PAYMENT-PROVIDER&quot;</span>);</span><br><span class="line">        <span class="keyword">for</span> (ServiceInstance instance : instances) &#123;</span><br><span class="line">            log.info(<span class="string">&quot;服务ID-----&quot;</span> + instance.getServiceId());</span><br><span class="line">            log.info(<span class="string">&quot;主机名称-----&quot;</span> + instance.getHost());</span><br><span class="line">            log.info(<span class="string">&quot;实例端口号-----&quot;</span> + instance.getPort());</span><br><span class="line">            log.info(<span class="string">&quot;实例地址&quot;</span> + instance.getUri());</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>&lt;&gt;(<span class="number">200</span>,<span class="string">&quot;获取信息成功&quot;</span>,<span class="built_in">this</span>.discoveryClient);</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="Eureka的自我保护"><a href="#Eureka的自我保护" class="headerlink" title="Eureka的自我保护"></a>Eureka的自我保护</h3><h4 id="自我保护机制"><a href="#自我保护机制" class="headerlink" title="自我保护机制"></a>自我保护机制</h4><p>在Eureka的服务注册中心会看到一行红字，看到了这段红字提示，就代表Eureka进入了自我保护机制。</p><blockquote><p>EMERGENCY! EUREKA MAY BE INCORRECTLY CLAIMING INSTANCES ARE UP WHEN THEY’RE NOT. RENEWALS ARE LESSER THAN THRESHOLD AND HENCE THE INSTANCES ARE NOT BEING EXPIRED JUST TO BE SAFE.</p></blockquote><p>简单的机器翻译为。</p><blockquote><p>紧急情况！ EUREKA 可能不正确地声称实例已启动，但实际上并非如此。续订少于阈值，因此实例不会只是为了安全而过期。</p></blockquote><p>保护模式主要用于一组客户端和服务端之间存在网络分区场景下的保护，一旦进入保护模式，服务端将会尝试保护器服务注册表中的信息，不删除服务注册中的数据，也就是不会注销任何微服务。简单来说就是服务注册中心中的某一个Client微服务不可用了，Eureka并不会立刻清理该微服务信息，依旧会保存在服务注册中心。</p><blockquote><p>默认情况下，如果EurekaServer在一定时间内没有接收到某个微服务实例的心跳，Eureka会注销该实例（默认90秒）。</p><p>如果出现了网络分区的情况，Client与Server之间无法正常通信，这样的注销行为就会很不安全，因为有可能是因为网络问题导致无法接收到心跳，而不是该Client状态不健康，就不应该立刻注销该微服务。Eureka通过自我保护模式来解决了这个问题。</p><p>当EurekaServer节点短时间内丢失过多Client时，该节点就会进入自我保护模式。自我保护模式中，Server不会注销任何服务实例，即使是不健康的实例，也会保留。</p></blockquote><p>所以Eureka属于CAP分支中的<code>AP</code>分支。</p><h4 id="如何关闭自我保护"><a href="#如何关闭自我保护" class="headerlink" title="如何关闭自我保护"></a>如何关闭自我保护</h4><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></pre></td><td class="code"><pre><span class="line"><span class="attr">eureka:</span></span><br><span class="line">  <span class="attr">instance:</span></span><br><span class="line">    <span class="attr">hostname:</span> <span class="string">eureka7001.com</span></span><br><span class="line">  <span class="attr">server:</span></span><br><span class="line">    <span class="comment"># 关闭自我保护机制,默认是开启的</span></span><br><span class="line">    <span class="attr">enable-self-preservation:</span> <span class="literal">false</span></span><br><span class="line">    <span class="comment"># 间隔时间</span></span><br><span class="line">    <span class="attr">eviction-interval-timer-in-ms:</span> <span class="number">2000</span></span><br><span class="line">  <span class="attr">client:</span></span><br><span class="line">    <span class="attr">register-with-eureka:</span> <span class="literal">false</span></span><br><span class="line">    <span class="attr">fetch-registry:</span> <span class="literal">false</span></span><br><span class="line">    <span class="attr">service-url:</span></span><br><span class="line">      <span class="attr">defaultZone:</span> <span class="string">http://eureka7002.com:7002/eureka</span></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></pre></td><td class="code"><pre><span class="line"><span class="attr">eureka:</span></span><br><span class="line">  <span class="attr">client:</span></span><br><span class="line">    <span class="attr">register-with-eureka:</span> <span class="literal">true</span></span><br><span class="line">    <span class="attr">fetch-registry:</span> <span class="literal">true</span></span><br><span class="line">    <span class="attr">service-url:</span></span><br><span class="line">      <span class="attr">defaultZone:</span> <span class="string">http://eureka7001.com:7001/eureka,http://eureka7002.com:7002/eureka</span></span><br><span class="line">  <span class="attr">instance:</span></span><br><span class="line">    <span class="attr">instance-id:</span> <span class="string">payment8001</span></span><br><span class="line">    <span class="attr">prefer-ip-address:</span> <span class="literal">true</span></span><br><span class="line">    <span class="comment"># EurekaClient向服务端发送心跳的时间间隔，默认为30秒，单位为秒</span></span><br><span class="line">    <span class="attr">lease-renewal-interval-in-seconds:</span> <span class="number">30</span></span><br><span class="line">    <span class="comment"># Eureka服务端在收到最后一次心跳后的等待时间上限，超时就剔除，默认为90秒，单位为秒</span></span><br><span class="line">    <span class="attr">lease-expiration-duration-in-seconds:</span> <span class="number">90</span></span><br></pre></td></tr></table></figure><h2 id="Consul"><a href="#Consul" class="headerlink" title="Consul"></a>Consul</h2><h3 id="简介"><a href="#简介" class="headerlink" title="简介"></a>简介</h3><p>官网地址<a href="https://www.consul.io/">https://www.consul.io/</a></p><p>官网的介绍。</p><blockquote><p>Consul 是一种服务网格解决方案，提供具有服务发现、配置和分段功能的全功能控制平面。这些功能中的每一个都可以根据需要单独使用，也可以一起使用以构建完整的服务网格。</p><p>Consul 需要一个数据平面并支持代理和本地集成模型。</p><p>Consul 附带一个简单的内置代理，因此一切都可以开箱即用，而且还支持 3rd 方代理集成，例如 Envoy。</p></blockquote><p>主要特点。</p><blockquote><ol><li><p>服务发现：Consul 的客户端可以注册一个服务，例如 api或mysql，其他客户端可以使用 Consul 来发现给定服务的提供者。使用 DNS 或 HTTP，应用程序可以轻松找到它们所依赖的服务。</p></li><li><p>健康检查：Consul 客户端可以提供任意数量的健康检查，要么与给定的服务相关联（“网络服务器是否返回 200 OK”），要么与本地节点（“内存利用率低于 90%”）相关联。操作员可以使用此信息来监视集群健康状况，并且服务发现组件可以使用它来将流量从不健康的主机路由出去。</p></li><li><p>KV 存储：应用程序可以将 Consul 的分层键/值存储用于多种目的，包括动态配置、功能标记、协调、领导选举等。简单的 HTTP API 使其易于使用。</p></li><li><p>安全服务通信：Consul 可以为服务生成和分发 TLS 证书，以建立相互的 TLS 连接。 意图 可用于定义允许哪些服务进行通信。可以通过实时更改意图轻松管理服务分段，而不是使用复杂的网络拓扑和静态防火墙规则。</p></li><li><p>多数据中心：Consul 支持开箱即用的多个数据中心。这意味着 Consul 的用户不必担心构建额外的抽象层以扩展到多个区域。</p></li></ol></blockquote><h3 id="下载使用"><a href="#下载使用" class="headerlink" title="下载使用"></a>下载使用</h3><p>win10下载之后就只有一个<code>consul.exe</code>文件，直接在文件夹中使用CMD即可。</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></pre></td><td class="code"><pre><span class="line"># 查看版本信息</span><br><span class="line">consul -version</span><br><span class="line"># 使用开发模式启动consul</span><br><span class="line">consul agent -dev</span><br></pre></td></tr></table></figure><p>然后访问consul的首页。<a href="http://localhost:8500">http://localhost:8500</a></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></pre></td><td class="code"><pre><span class="line"><span class="tag">&lt;<span class="name">dependency</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">groupId</span>&gt;</span>org.springframework.cloud<span class="tag">&lt;/<span class="name">groupId</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">artifactId</span>&gt;</span>spring-cloud-starter-consul-discovery<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><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></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">8006</span></span><br><span class="line"></span><br><span class="line"><span class="attr">spring:</span></span><br><span class="line">  <span class="attr">application:</span></span><br><span class="line">    <span class="attr">name:</span> <span class="string">consul-provider-payment</span></span><br><span class="line">  <span class="attr">cloud:</span></span><br><span class="line">    <span class="comment"># consul注册中心地址</span></span><br><span class="line">    <span class="attr">consul:</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">8500</span></span><br><span class="line">      <span class="attr">discovery:</span></span><br><span class="line">        <span class="attr">service-name:</span> <span class="string">$&#123;spring.application.name&#125;</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></pre></td><td class="code"><pre><span class="line"><span class="meta">@SpringBootApplication</span></span><br><span class="line"><span class="meta">@EnableDiscoveryClient</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">PaymentMain</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(PaymentMain.class, args);</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>在Consul的控制台就能查看到该服务的该实例。</p><h2 id="服务注册中心异同点"><a href="#服务注册中心异同点" class="headerlink" title="服务注册中心异同点"></a>服务注册中心异同点</h2><div class="table-container"><table><thead><tr><th style="text-align:center">组件名</th><th style="text-align:center">实现语言</th><th style="text-align:center">CAP</th><th style="text-align:center">服务健康检查</th><th style="text-align:center">对外暴露接口</th><th style="text-align:center">SpringCloud集合</th></tr></thead><tbody><tr><td style="text-align:center">Eureka</td><td style="text-align:center">Java</td><td style="text-align:center">AP</td><td style="text-align:center">可配支持</td><td style="text-align:center">HTTP</td><td style="text-align:center">集成</td></tr><tr><td style="text-align:center">Consul</td><td style="text-align:center">GO</td><td style="text-align:center">CP</td><td style="text-align:center">支持</td><td style="text-align:center">HTTP/DNS</td><td style="text-align:center">集成</td></tr><tr><td style="text-align:center">Zookeeper</td><td style="text-align:center">Java</td><td style="text-align:center">CP</td><td style="text-align:center">支持</td><td style="text-align:center">客户端</td><td style="text-align:center">集成</td></tr></tbody></table></div><h2 id="Nacos"><a href="#Nacos" class="headerlink" title="Nacos"></a>Nacos</h2><p>Nacos可以实现服务注册中心，后续提出来专门学习。</p><h1 id="服务调用"><a href="#服务调用" class="headerlink" title="服务调用"></a>服务调用</h1><h2 id="Ribbon（维护模式）"><a href="#Ribbon（维护模式）" class="headerlink" title="Ribbon（维护模式）"></a>Ribbon（维护模式）</h2><p>Ribbon是基于Neftlix Ribbon实现的一套客户端负载均衡工具，主要功能是提供客户端的软件负载均衡算法和服务调用。</p><p>工作流程。</p><ol><li>先选择EurekaServer，优先选择在同一个区域内负载较少的server</li><li>在根据用户指定的策略，再从server获取的服务注册列表选择一个地址</li></ol><p>Ribbon就是负载均衡加上RestTemplate调用。</p><h3 id="使用Ribbon"><a href="#使用Ribbon" class="headerlink" title="使用Ribbon"></a>使用Ribbon</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></pre></td><td class="code"><pre><span class="line">   <span class="tag">&lt;<span class="name">dependency</span>&gt;</span></span><br><span class="line">     <span class="tag">&lt;<span class="name">groupId</span>&gt;</span>org.springframework.cloud<span class="tag">&lt;/<span class="name">groupId</span>&gt;</span></span><br><span class="line">     <span class="tag">&lt;<span class="name">artifactId</span>&gt;</span>spring-cloud-starter-netflix-ribbon<span class="tag">&lt;/<span class="name">artifactId</span>&gt;</span></span><br><span class="line">   <span class="tag">&lt;/<span class="name">dependency</span>&gt;</span></span><br><span class="line"></span><br><span class="line"><span class="comment">&lt;!--  只要引入了这个client依赖，就不用引入上面的ribbon依赖了</span></span><br><span class="line"><span class="comment">lient是包含了ribbon依赖的，不想用eureka就直接引入上面的ribbon依赖  --&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.cloud<span class="tag">&lt;/<span class="name">groupId</span>&gt;</span></span><br><span class="line">       <span class="tag">&lt;<span class="name">artifactId</span>&gt;</span>spring-cloud-starter-netflix-eureka-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>Ribbon是基于RestTemplate调用实现的，所以要使用Ribbon还需要去RestTemplate配置类上开启。</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><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_">RestConfig</span> &#123;</span><br><span class="line"></span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     *  LoadBalanced开启负载均衡</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="meta">@LoadBalanced</span></span><br><span class="line">    <span class="keyword">public</span> RestTemplate <span class="title function_">getRestTemplate</span><span class="params">()</span> &#123;</span><br><span class="line">        <span class="keyword">return</span> <span class="keyword">new</span> <span class="title class_">RestTemplate</span>();</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h3 id="Ribbon常见的负载均衡实现"><a href="#Ribbon常见的负载均衡实现" class="headerlink" title="Ribbon常见的负载均衡实现"></a>Ribbon常见的负载均衡实现</h3><p>Ribbon的核心接口IROLE，用于制定一个负载均衡规则的实现。</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><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_">IRule</span>&#123;</span><br><span class="line">    <span class="comment">/*</span></span><br><span class="line"><span class="comment">     * choose one alive server from lb.allServers or</span></span><br><span class="line"><span class="comment">     * lb.upServers according to key</span></span><br><span class="line"><span class="comment">     * </span></span><br><span class="line"><span class="comment">     * @return choosen Server object. NULL is returned if none</span></span><br><span class="line"><span class="comment">     *  server is available </span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line"></span><br><span class="line">    <span class="keyword">public</span> Server <span class="title function_">choose</span><span class="params">(Object key)</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_">setLoadBalancer</span><span class="params">(ILoadBalancer lb)</span>;</span><br><span class="line">    </span><br><span class="line">    <span class="keyword">public</span> ILoadBalancer <span class="title function_">getLoadBalancer</span><span class="params">()</span>;    </span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>它的一些实现类就是一些常用的负载均衡规则。</p><ol><li><p><code>com.netflix.loadbalancer.RoundRobinRule</code></p><p>轮询</p></li><li><p><code>com.netflix.loadbalancer.RandomRule</code></p><p>随机</p></li><li><p><code>com.netflix.loadbalancer.RetryRule</code></p><p>先按照轮询的策略获取服务，如果获取服务失败则在指定时间内进行重试，获取可用的服务</p></li><li><p><code>WeightedResponseTimeRule</code></p><p>对轮询的扩展，响应速度越快的实例选择权重越大，越容易被选择</p></li><li><p><code>BestAvailableRule</code></p><p>会过滤调由于多次访问故障而处于断路器跳闸状态的服务，然后选择一个并发量最小的服务</p></li><li><p><code>AvailabilityFilteringRule</code></p><p>先过滤故障实例，再选择并发较小的实例</p></li><li><p><code>ZoneAvoidanceRule</code></p><p>默认规则，复合判断server所在区域的性能和server的可用性选择服务器</p></li></ol><h3 id="修改负载均衡规则"><a href="#修改负载均衡规则" class="headerlink" title="修改负载均衡规则"></a>修改负载均衡规则</h3><p>官方说明不能被<code>@ComponentScan</code>注解给扫描到，而<code>@SpringBootApplication</code>启动类注解中是包含了<code>@ComponentScan</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><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><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"> * &lt;p&gt;</span></span><br><span class="line"><span class="comment"> *     自定义负载均衡规则类</span></span><br><span class="line"><span class="comment"> * &lt;/p&gt;</span></span><br><span class="line"><span class="comment"> *</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@author</span> yww</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></span><br><span class="line"><span class="meta">@Configuration</span></span><br><span class="line"><span class="comment">// 使用MyRule类名可能会出现问题</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">MySelfRule</span> &#123;</span><br><span class="line">    <span class="meta">@Bean</span></span><br><span class="line">    <span class="keyword">public</span> IRule <span class="title function_">myRule</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_">RandomRule</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></pre></td><td class="code"><pre><span class="line"><span class="meta">@SpringBootApplication</span></span><br><span class="line"><span class="meta">@EnableEurekaClient</span></span><br><span class="line"><span class="meta">@RibbonClient(name = &quot;CLOUD-PAYMENT-PROVIDER&quot;, configuration = MySelfRule.class)</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">OrderMain</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(OrderMain.class,args);</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>轮询的原理就是通过取模，确定请求的位置，类似于hash的实现。</p><p>实现的两个方法。（com.netflix.loadbalancer.RoundRobinRule）</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> Server <span class="title function_">choose</span><span class="params">(ILoadBalancer lb, Object key)</span> &#123;</span><br><span class="line">    <span class="keyword">if</span> (lb == <span class="literal">null</span>) &#123;</span><br><span class="line">        log.warn(<span class="string">&quot;no load balancer&quot;</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"></span><br><span class="line">    <span class="type">Server</span> <span class="variable">server</span> <span class="operator">=</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> <span class="number">0</span>;</span><br><span class="line">    <span class="keyword">while</span> (server == <span class="literal">null</span> &amp;&amp; count++ &lt; <span class="number">10</span>) &#123;</span><br><span class="line">        List&lt;Server&gt; reachableServers = lb.getReachableServers();</span><br><span class="line">        List&lt;Server&gt; allServers = lb.getAllServers();</span><br><span class="line">        <span class="type">int</span> <span class="variable">upCount</span> <span class="operator">=</span> reachableServers.size();</span><br><span class="line">        <span class="type">int</span> <span class="variable">serverCount</span> <span class="operator">=</span> allServers.size();</span><br><span class="line"></span><br><span class="line">        <span class="keyword">if</span> ((upCount == <span class="number">0</span>) || (serverCount == <span class="number">0</span>)) &#123;</span><br><span class="line">            log.warn(<span class="string">&quot;No up servers available from load balancer: &quot;</span> + lb);</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="type">int</span> <span class="variable">nextServerIndex</span> <span class="operator">=</span> incrementAndGetModulo(serverCount);</span><br><span class="line">        server = allServers.get(nextServerIndex);</span><br><span class="line"></span><br><span class="line">        <span class="keyword">if</span> (server == <span class="literal">null</span>) &#123;</span><br><span class="line">            <span class="comment">/* Transient. */</span></span><br><span class="line">            Thread.<span class="keyword">yield</span>();</span><br><span class="line">            <span class="keyword">continue</span>;</span><br><span class="line">        &#125;</span><br><span class="line"></span><br><span class="line">        <span class="keyword">if</span> (server.isAlive() &amp;&amp; (server.isReadyToServe())) &#123;</span><br><span class="line">            <span class="keyword">return</span> (server);</span><br><span class="line">        &#125;</span><br><span class="line"></span><br><span class="line">        <span class="comment">// Next.</span></span><br><span class="line">        server = <span class="literal">null</span>;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">if</span> (count &gt;= <span class="number">10</span>) &#123;</span><br><span class="line">        log.warn(<span class="string">&quot;No available alive servers after 10 tries from load balancer: &quot;</span></span><br><span class="line">                + lb);</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">return</span> server;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="keyword">private</span> <span class="type">int</span> <span class="title function_">incrementAndGetModulo</span><span class="params">(<span class="type">int</span> modulo)</span> &#123;</span><br><span class="line">    <span class="keyword">for</span> (;;) &#123;</span><br><span class="line">        <span class="type">int</span> <span class="variable">current</span> <span class="operator">=</span> nextServerCyclicCounter.get();</span><br><span class="line">        <span class="type">int</span> <span class="variable">next</span> <span class="operator">=</span> (current + <span class="number">1</span>) % modulo;</span><br><span class="line">        <span class="keyword">if</span> (nextServerCyclicCounter.compareAndSet(current, next))</span><br><span class="line">            <span class="keyword">return</span> next;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h2 id="LoadBalancer"><a href="#LoadBalancer" class="headerlink" title="LoadBalancer"></a>LoadBalancer</h2><p>可能以后会学习。</p><h2 id="Feign"><a href="#Feign" class="headerlink" title="Feign"></a>Feign</h2><p>因为有Feign的替代版了，所以这个暂时不考虑，详情请参考OpenFeign。</p><h2 id="OpenFeign"><a href="#OpenFeign" class="headerlink" title="OpenFeign"></a>OpenFeign</h2><p>Feign是一个声明式WebService客户端，使用在服务消费端。</p><p>前面使用的是Ribbon+RestTemplate的服务调用方法，利用RestTemplate对http请求进行封装处理，调用其他服务的接口。Feign在此的基础上进一步封装，只需要添加一个注解，即可实现服务调用。</p><p>Feign是SpringCloud组件中一个轻量级的RESTful的HTTP服务客户端，Feign内置了Ribbon，用来做客户端负载均衡。</p><p>OpenFeign是在Feign的基础上支持了SpringMVC的注解，实现类中做负载均衡并调用其他服务。</p><h3 id="简单使用"><a href="#简单使用" class="headerlink" title="简单使用"></a>简单使用</h3><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">&lt;dependency&gt;</span><br><span class="line">           &lt;groupId&gt;org.springframework.cloud&lt;/groupId&gt;</span><br><span class="line">           &lt;artifactId&gt;spring-cloud-starter-openfeign&lt;/artifactId&gt;</span><br><span class="line">       &lt;/dependency&gt;</span><br></pre></td></tr></table></figure><p>这个依赖是包含了Ribbon依赖的，所以使用其中的负载均衡功能。</p><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></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">application:</span></span><br><span class="line">    <span class="attr">name:</span> <span class="string">feign-payment-consumer</span></span><br><span class="line"></span><br><span class="line"><span class="attr">eureka:</span></span><br><span class="line">  <span class="attr">client:</span></span><br><span class="line">    <span class="attr">register-with-eureka:</span> <span class="literal">false</span></span><br><span class="line">    <span class="attr">service-url:</span></span><br><span class="line">      <span class="attr">defaultZone:</span> <span class="string">http://eureka7001.com:7001/eureka,http://eureka7002.com:7002/eureka</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></pre></td><td class="code"><pre><span class="line"><span class="meta">@SpringBootApplication</span></span><br><span class="line"><span class="meta">@EnableFeignClients</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">FeignOrderMain</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(FeignOrderMain.class, args);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>创建业务服务接口。即通过这个接口，Feign就会调用指定服务的接口。</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><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"> * &lt;p&gt;</span></span><br><span class="line"><span class="comment"> *     业务服务接口</span></span><br><span class="line"><span class="comment"> * &lt;/p&gt;</span></span><br><span class="line"><span class="comment"> *</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@author</span> yww</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">@date</span> 2021/12/14 18:55</span></span><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">@FeignClient(&quot;CLOUD-PAYMENT-PROVIDER&quot;)</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">interface</span> <span class="title class_">PaymentFeignService</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    数据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;/payment/getById/&#123;id&#125;&quot;)</span></span><br><span class="line">    Result&lt;Payment&gt; <span class="title function_">getById</span><span class="params">(<span class="meta">@PathVariable(&quot;id&quot;)</span> Long id)</span>;</span><br><span class="line"></span><br><span class="line">&#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></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;/consumer&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">@Resource</span></span><br><span class="line">    <span class="keyword">private</span> PaymentFeignService service;</span><br><span class="line">    <span class="meta">@GetMapping(&quot;getById/&#123;id&#125;&quot;)</span></span><br><span class="line">    <span class="keyword">public</span> Result&lt;Payment&gt; <span class="title function_">getById</span><span class="params">(<span class="meta">@PathVariable(&quot;id&quot;)</span>Long id)</span> &#123;</span><br><span class="line">        <span class="keyword">return</span> service.getById(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><p>总结。</p><ol><li>主启动类上开启使用Feign。(@EnableFeignClients)</li><li>编写service业务接口类，指定调用的服务提供者。(@FeignClient(“CLOUD-PAYMENT-PROVIDER”))</li><li>编写的抽象接口就使用服务提供者的接口。</li></ol><h3 id="超时控制"><a href="#超时控制" class="headerlink" title="超时控制"></a>超时控制</h3><p>因为是调用服务提供者的接口，所以就可能会出现超时的情况，可用通过配置进行超时控制。</p><p>就比如Feign默认的配置超时时间是1秒，即调用的接口1秒之内没有结果回应，Feign客户端就不会继续等待了，直接返回报错。</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></pre></td><td class="code"><pre><span class="line">feign:</span><br><span class="line">  client:</span><br><span class="line">    config:</span><br><span class="line">  # 单位ms </span><br><span class="line">      <span class="keyword">default</span>:</span><br><span class="line">        # 建立连接后从服务器读取到可用资源所用的时间</span><br><span class="line">        connectTimeout: <span class="number">5000</span></span><br><span class="line">        # 建立连接建立所用时间</span><br><span class="line">        readTimeout: <span class="number">5000</span></span><br></pre></td></tr></table></figure><h3 id="日志打印功能"><a href="#日志打印功能" class="headerlink" title="日志打印功能"></a>日志打印功能</h3><p>OpenFeign日志级别。</p><ol><li><p>NONE</p><p>默认的，不显示任何日志</p></li><li><p>BASIC</p><p>仅记录请求方法，URL，响应状态码及执行时间</p></li><li><p>HEADERS</p><p>除了BASIC中定义的信息之外，还有请求和响应的头信息</p></li><li><p>FULL</p><p>除了HEADERS中定义的信息之外，还有请求和响应的正文及元数据</p></li></ol><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="comment">/**</span></span><br><span class="line"><span class="comment"> * &lt;p&gt;</span></span><br><span class="line"><span class="comment"> *     Feign的配置类</span></span><br><span class="line"><span class="comment"> * &lt;/p&gt;</span></span><br><span class="line"><span class="comment"> *</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@author</span> yww</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_">FeignConfig</span> &#123;</span><br><span class="line">    </span><br><span class="line">    <span class="meta">@Bean</span></span><br><span class="line">    Logger.Level <span class="title function_">feignLoggerLevel</span><span class="params">()</span> &#123;</span><br><span class="line">        <span class="keyword">return</span> Logger.Level.FULL;</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><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">logging:</span></span><br><span class="line">  <span class="attr">level:</span></span><br><span class="line">    <span class="comment"># 业务接口类，debug是等级 </span></span><br><span class="line">    <span class="attr">com.yww.springcloud.service.PaymentFeignService:</span> <span class="string">debug</span></span><br></pre></td></tr></table></figure><h1 id="服务降级"><a href="#服务降级" class="headerlink" title="服务降级"></a>服务降级</h1><h2 id="Hystrix（维护模式）"><a href="#Hystrix（维护模式）" class="headerlink" title="Hystrix（维护模式）"></a>Hystrix（维护模式）</h2><blockquote><p>服务雪崩</p><p>多个微服务之间的调用的时候，假设微服务A调用微服务B和微服务C，微服务B和微服务C又调用其他的微服务，这就是所谓的“扇出”。</p><p>如果扇出的链路上某个微服务的调用响应时间过长或者不可用，对微服务A的调用就会占用越来越多的系统资源，进而引起系统崩溃，这就是所谓的雪崩效应。</p></blockquote><p>Hystrix是一个用于处理分布式系统的延迟和容错的开源库，比如在分布式系统里，许多依赖不可避免地会调用失败（超时，异常等），Hystrix就是用于保证不会因为一个依赖出现问题而导致整个服务失败地情况，避免级联故障。</p><p>当一个服务发生了故障，无法返回结果，就会通过断路器地故障监控，向调用方返回一个符合预期地备选响应，而不是直接导致整个服务调用的失败。</p><p>Hystrix的主要功能。</p><ol><li>服务降级</li><li>服务熔断</li><li>接近实时的监控</li></ol><h3 id="一些重要的概念"><a href="#一些重要的概念" class="headerlink" title="一些重要的概念"></a>一些重要的概念</h3><ul><li><p>服务降级</p><p>服务的负载过大，但是资源是有限的，根据降级的策略牺牲一些服务或者是功能，保证核心服务的正常执行。</p></li><li><p>服务熔断</p><p>当上游服务因为某种原因而调用失败，下游服务为了保证整体服务的可用性，就会不再继续调用失败的上游服务，返回一个设定的结果，快速释放资源。</p></li><li><p>服务限流</p><p>服务限制请求的速率，比如服务设定了一个访问阈值，在一段时间内的访问次数超过了阈值，超过的部分就会直接拒绝访问。</p></li></ul><blockquote><p>降级和熔断</p><p>降级和熔断的概念很相似，但其实是不一样的。</p><p>降级强调的是整体架构的可用性，熔断强调的是当前服务的可用性。</p><p>降级的目的在于应对系统自身的故障，而熔断的目的在于应对当前系统依赖的外部系统或者第三方系统的故障。</p></blockquote><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></pre></td><td class="code"><pre><span class="line"><span class="tag">&lt;<span class="name">dependency</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">groupId</span>&gt;</span>org.springframework.cloud<span class="tag">&lt;/<span class="name">groupId</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">artifactId</span>&gt;</span>spring-cloud-starter-netflix-hystrix<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><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><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_">PaymentService</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> id ID</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@return</span>  返回OK</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_">paymentInfoOk</span><span class="params">(Integer id)</span> &#123;</span><br><span class="line">        <span class="keyword">return</span> <span class="string">&quot;线程池&quot;</span> + Thread.currentThread().getName() + <span class="string">&quot;访问成功, id为: &quot;</span> + 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> id ID</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@return</span>  返回Fault</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_">paymentInfoTimeOut</span><span class="params">(Integer id)</span> &#123;</span><br><span class="line">        <span class="keyword">try</span> &#123;</span><br><span class="line">            TimeUnit.SECONDS.sleep(<span class="number">3</span>);</span><br><span class="line">        &#125; <span class="keyword">catch</span> (InterruptedException e) &#123;</span><br><span class="line">            e.printStackTrace();</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">return</span> <span class="string">&quot;线程池&quot;</span> + Thread.currentThread().getName() + <span class="string">&quot;超时访问, id为: &quot;</span> + 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><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></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;payment&quot;)</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">PaymentController</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> PaymentService service;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Value(&quot;$&#123;server.port&#125;&quot;)</span></span><br><span class="line">    <span class="keyword">private</span> String port;</span><br><span class="line"></span><br><span class="line"></span><br><span class="line">    <span class="meta">@GetMapping(&quot;/ok/&#123;id&#125;&quot;)</span></span><br><span class="line">    <span class="keyword">public</span> String <span class="title function_">infoOk</span><span class="params">(<span class="meta">@PathVariable(&quot;id&quot;)</span>Integer id)</span> &#123;</span><br><span class="line">        <span class="keyword">return</span> service.paymentInfoOk(id);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@GetMapping(&quot;/timeout/&#123;id&#125;&quot;)</span></span><br><span class="line">    <span class="keyword">public</span> String <span class="title function_">infoTimeOut</span><span class="params">(<span class="meta">@PathVariable(&quot;id&quot;)</span>Integer id)</span> &#123;</span><br><span class="line">        <span class="keyword">return</span> service.paymentInfoTimeOut(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><h3 id="高压力测试"><a href="#高压力测试" class="headerlink" title="高压力测试"></a>高压力测试</h3><blockquote><p>前提</p><p>访问正常请求接口，基本都是秒回信息。</p><p>访问超时请求接口，则会等待三秒钟才返回信息。</p></blockquote><p>使用JMeter对超时请求接口进行压力测试。</p><p>模拟多线程多个请求访问超时请求接口，自己访问正常请求接口，会发现已经不是秒回信息了，可能会出现超时，停顿的情况。</p><p>因为tomcat的默认的工作线程数被打满了，没有多余的线程来分解压力和处理。</p><p>这还只是服务提供者的自测，若是还有服务消费者，会极大可能出现请求超时的情况，导致服务消费者的请求不能及时完成，拖死客户端。</p><p>正是因为这种情况的出现，所以需要我们进行降级，容错，限流等。</p><blockquote><p>解决的问题</p><ol><li><p>服务端出现超时或者宕机了，调用者不能一直卡死等待，需要服务降级</p></li><li><p>服务端成功返回，但是调用者自己出现故障或又自我要求，自己处理降级</p></li></ol></blockquote><h3 id="服务提供者的服务降级"><a href="#服务提供者的服务降级" class="headerlink" title="服务提供者的服务降级"></a>服务提供者的服务降级</h3><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></pre></td><td class="code"><pre><span class="line"><span class="meta">@SpringBootApplication</span></span><br><span class="line"><span class="meta">@EnableEurekaClient</span></span><br><span class="line"><span class="comment">// @EnableCircuitBreaker</span></span><br><span class="line"><span class="meta">@EnableCircuitBreaker</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">HystrixPaymentMain</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(HystrixPaymentMain.class, args);</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><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><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_">PaymentService</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> id ID</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@return</span>  返回OK</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_">paymentInfoOk</span><span class="params">(Integer id)</span> &#123;</span><br><span class="line">        <span class="keyword">return</span> <span class="string">&quot;线程池&quot;</span> + Thread.currentThread().getName() + <span class="string">&quot;访问成功, id为: &quot;</span> + 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">     * 设置2秒钟之内可以正常执行，逻辑里停了3秒，模拟降级情况</span></span><br><span class="line"><span class="comment">     * 如果逻辑出现异常或者错误，不是超时的问题，也会进行降级</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@param</span> id ID</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@return</span>  返回Fault</span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    <span class="meta">@HystrixCommand(fallbackMethod = &quot;timeoutHandler&quot;, commandProperties = &#123;</span></span><br><span class="line"><span class="meta">            @HystrixProperty(name = &quot;execution.isolation.thread.timeoutInMilliseconds&quot;,value = &quot;2000&quot;)</span></span><br><span class="line"><span class="meta">    &#125;)</span></span><br><span class="line">    <span class="keyword">public</span> String <span class="title function_">paymentInfoTimeOut</span><span class="params">(Integer id)</span> &#123;</span><br><span class="line">        <span class="keyword">try</span> &#123;</span><br><span class="line">            TimeUnit.SECONDS.sleep(<span class="number">3</span>);</span><br><span class="line">        &#125; <span class="keyword">catch</span> (InterruptedException e) &#123;</span><br><span class="line">            e.printStackTrace();</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">return</span> <span class="string">&quot;线程池&quot;</span> + Thread.currentThread().getName() + <span class="string">&quot;超时访问, id为: &quot;</span> + 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_">timeoutHandler</span><span class="params">(Integer id)</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">&#125;</span><br></pre></td></tr></table></figure><h3 id="服务消费者的服务降级"><a href="#服务消费者的服务降级" class="headerlink" title="服务消费者的服务降级"></a>服务消费者的服务降级</h3><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></pre></td><td class="code"><pre><span class="line"><span class="attr">feign:</span></span><br><span class="line">  <span class="attr">hystrix:</span></span><br><span class="line">    <span class="attr">enabled:</span> <span class="literal">true</span></span><br></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></pre></td><td class="code"><pre><span class="line"><span class="meta">@SpringBootApplication</span></span><br><span class="line"><span class="meta">@EnableFeignClients</span></span><br><span class="line"><span class="meta">@EnableHystrix</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">FeignHystrixMain</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(FeignHystrixMain.class, args);</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><span class="line">26</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;consumer&quot;)</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">HystrixController</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> FeignConsumer server;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@GetMapping(&quot;/ok/&#123;id&#125;&quot;)</span></span><br><span class="line">    <span class="keyword">public</span> String <span class="title function_">ok</span><span class="params">(<span class="meta">@PathVariable(&quot;id&quot;)</span>Integer id)</span> &#123;</span><br><span class="line">        <span class="keyword">return</span> server.infoOk(id);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@GetMapping(&quot;/timeout/&#123;id&#125;&quot;)</span></span><br><span class="line">    <span class="meta">@HystrixCommand(fallbackMethod = &quot;timeoutHandler&quot;, commandProperties = &#123;</span></span><br><span class="line"><span class="meta">            @HystrixProperty(name = &quot;execution.isolation.thread.timeoutInMilliseconds&quot;,value = &quot;1000&quot;)</span></span><br><span class="line"><span class="meta">    &#125;)</span></span><br><span class="line">    <span class="keyword">public</span> String <span class="title function_">timeout</span><span class="params">(<span class="meta">@PathVariable(&quot;id&quot;)</span>Integer id)</span> &#123;</span><br><span class="line">        <span class="keyword">return</span> server.infoTimeOut(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_">timeoutHandler</span><span class="params">(<span class="meta">@PathVariable(&quot;id&quot;)</span>Integer id)</span> &#123;</span><br><span class="line">        <span class="keyword">return</span> <span class="string">&quot;消费端调用超时，请稍后再试！&quot;</span> + 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><blockquote><p>一般是调用端配置超时设置，在哪里配置或者两端都配置还要根据具体业务需求。</p></blockquote><h3 id="全局服务降级"><a href="#全局服务降级" class="headerlink" title="全局服务降级"></a>全局服务降级</h3><p>刚刚演示的是某一个方法的服务降级，若是方法过多，就会导致降级处理的方法也会变多，所以大多数都是会用同一个降级处理方法。</p><ol><li>在controller的地方添加降级配置<code>@DefaultProperties</code></li><li>编写全局降级处理方法</li><li>给需要降级的接口添加<code>@HystrixCommand</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><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><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">@RestController</span></span><br><span class="line"><span class="meta">@Slf4j</span></span><br><span class="line"><span class="meta">@RequestMapping(&quot;consumer&quot;)</span></span><br><span class="line"><span class="meta">@DefaultProperties(defaultFallback = &quot;globalTimeout&quot;, commandProperties = &#123;</span></span><br><span class="line"><span class="meta">        @HystrixProperty(name = &quot;execution.isolation.thread.timeoutInMilliseconds&quot;,value = &quot;1000&quot;)</span></span><br><span class="line"><span class="meta">&#125;)</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">HystrixController</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> FeignConsumer server;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@GetMapping(&quot;/ok/&#123;id&#125;&quot;)</span></span><br><span class="line">    <span class="keyword">public</span> String <span class="title function_">ok</span><span class="params">(<span class="meta">@PathVariable(&quot;id&quot;)</span>Integer id)</span> &#123;</span><br><span class="line">        <span class="keyword">return</span> server.infoOk(id);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@GetMapping(&quot;/timeout/&#123;id&#125;&quot;)</span></span><br><span class="line">    <span class="meta">@HystrixCommand(fallbackMethod = &quot;timeoutHandler&quot;, commandProperties = &#123;</span></span><br><span class="line"><span class="meta">            @HystrixProperty(name = &quot;execution.isolation.thread.timeoutInMilliseconds&quot;,value = &quot;1000&quot;)</span></span><br><span class="line"><span class="meta">    &#125;)</span></span><br><span class="line">    <span class="keyword">public</span> String <span class="title function_">timeout</span><span class="params">(<span class="meta">@PathVariable(&quot;id&quot;)</span>Integer id)</span> &#123;</span><br><span class="line">        <span class="keyword">return</span> server.infoTimeOut(id);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line"></span><br><span class="line">    <span class="meta">@GetMapping(&quot;/timeout/&#123;id&#125;&quot;)</span></span><br><span class="line">    <span class="comment">// 不具体指定降级配置</span></span><br><span class="line">    <span class="meta">@HystrixCommand</span></span><br><span class="line">    <span class="keyword">public</span> String <span class="title function_">timeout2</span><span class="params">(<span class="meta">@PathVariable(&quot;id&quot;)</span>Integer id)</span> &#123;</span><br><span class="line">        <span class="keyword">return</span> server.infoTimeOut(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></span><br><span class="line">    <span class="keyword">public</span> String <span class="title function_">timeoutHandler</span><span class="params">(<span class="meta">@PathVariable(&quot;id&quot;)</span>Integer id)</span> &#123;</span><br><span class="line">        <span class="keyword">return</span> <span class="string">&quot;消费端调用超时，请稍后再试！&quot;</span> + 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></span><br><span class="line">    <span class="keyword">public</span> String <span class="title function_">globalTimeout</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">&#125;</span><br></pre></td></tr></table></figure><h3 id="指定服务降级处理类"><a href="#指定服务降级处理类" class="headerlink" title="指定服务降级处理类"></a>指定服务降级处理类</h3><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><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><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">@FeignClient(value = &quot;HYSTRIX-PROVIDER-PAYMENT&quot;,fallback = FeignConsumerImpl.class)</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">interface</span> <span class="title class_">FeignConsumer</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> id    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;/payment/ok/&#123;id&#125;&quot;)</span></span><br><span class="line">    String <span class="title function_">infoOk</span><span class="params">(<span class="meta">@PathVariable(&quot;id&quot;)</span>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">@param</span> id    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;/payment/timeout/&#123;id&#125;&quot;)</span></span><br><span class="line">    String <span class="title function_">infoTimeOut</span><span class="params">(<span class="meta">@PathVariable(&quot;id&quot;)</span>Integer id)</span>;</span><br><span class="line"></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="comment">/**</span></span><br><span class="line"><span class="comment"> * &lt;p&gt;</span></span><br><span class="line"><span class="comment"> *     服务降级处理类</span></span><br><span class="line"><span class="comment"> * 首先先实现业务接口，然后处理降级情况。</span></span><br><span class="line"><span class="comment"> * &lt;/p&gt;</span></span><br><span class="line"><span class="comment"> *</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@author</span> yww</span></span><br><span class="line"><span class="comment"> **/</span></span><br><span class="line"><span class="meta">@Component</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">FeignConsumerImpl</span> <span class="keyword">implements</span> <span class="title class_">FeignConsumer</span>&#123;</span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="keyword">public</span> String <span class="title function_">infoOk</span><span class="params">(Integer id)</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 class="meta">@Override</span></span><br><span class="line">    <span class="keyword">public</span> String <span class="title function_">infoTimeOut</span><span class="params">(Integer id)</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">&#125;</span><br></pre></td></tr></table></figure><blockquote><p>注意这两种降级的方法还是有点区别的。</p><p>在业务接口的降级处理其实是配置Feign的降级，也就是服务提供端出现问题的处理，在controller的降级处理其实算是在服务消费者的降级处理。</p></blockquote><h3 id="服务熔断"><a href="#服务熔断" class="headerlink" title="服务熔断"></a>服务熔断</h3><p>服务熔断是一种保护机制，当扇出链路的某个微服务出错不可用或者响应时间太长，会进行服务的降级，进而熔断该节点微服务的调用，快速返回错误的响应信息，当检测到该节点微服务调用响应正常后，恢复调用链路。</p><p>简单的说，就是当一段时间内失败的调用达到指定的阈值后，就会触发熔断机制。</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><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><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">@Service</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">PaymentService</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">     *  circuitBreaker.enabled                    是否开启断路器</span></span><br><span class="line"><span class="comment">     *  circuitBreaker.requestVolumeThreshold       请求的次数，默认20个请求</span></span><br><span class="line"><span class="comment">     *  circuitBreaker.sleepWindowInMilliseconds    时间窗口期，默认十秒</span></span><br><span class="line"><span class="comment">     *  circuitBreaker.errorThresholdPercentage     请求失败率的峰值，默认50%失败率</span></span><br><span class="line"><span class="comment">     *  配置组合的意义</span></span><br><span class="line"><span class="comment">     *  在10000ms内，不足20次，不会打开断路器</span></span><br><span class="line"><span class="comment">     *在10000ms内，超过了20次，若是失败率达到了50%就会触发断路器</span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    <span class="meta">@HystrixCommand(fallbackMethod = &quot;paymentCircuitBreakerFallback&quot;, commandProperties = &#123;</span></span><br><span class="line"><span class="meta">            @HystrixProperty(name = &quot;circuitBreaker.enabled&quot;, value = &quot;true&quot;),</span></span><br><span class="line"><span class="meta">            @HystrixProperty(name = &quot;circuitBreaker.requestVolumeThreshold&quot;, value = &quot;10&quot;),</span></span><br><span class="line"><span class="meta">            @HystrixProperty(name = &quot;circuitBreaker.sleepWindowInMilliseconds&quot;, value = &quot;10000&quot;),</span></span><br><span class="line"><span class="meta">            @HystrixProperty(name = &quot;circuitBreaker.errorThresholdPercentage&quot;, value = &quot;60&quot;)</span></span><br><span class="line"><span class="meta">    &#125;)</span></span><br><span class="line">    <span class="keyword">public</span> String <span class="title function_">paymentCircuitBreaker</span><span class="params">(<span class="meta">@PathVariable(&quot;id&quot;)</span> Integer id)</span> &#123;</span><br><span class="line">        <span class="keyword">if</span> (id &lt; <span class="number">0</span>) &#123;</span><br><span class="line">            <span class="keyword">throw</span> <span class="keyword">new</span> <span class="title class_">RuntimeException</span>();</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="type">String</span> <span class="variable">number</span> <span class="operator">=</span> IdUtil.simpleUUID();</span><br><span class="line">        <span class="keyword">return</span> Thread.currentThread().getName() + <span class="string">&quot;\t&quot;</span> + <span class="string">&quot;调用成功， UUID为&quot;</span> + number;</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_">paymentCircuitBreakerFallback</span><span class="params">(<span class="meta">@PathVariable(&quot;id&quot;)</span> Integer id)</span> &#123;</span><br><span class="line">        <span class="keyword">return</span> <span class="string">&quot;id 不能为负数，请稍后再试！ 当前ID为：&quot;</span> + 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><blockquote><p>断路器开启之后，请求就不会调用主逻辑了，即不用判断请求正确还是错误，直接调用fallback，直接服务降级。</p><p>一段时间（默认是5秒），断路器是半开状态，会让其中一个请求进行转发，如果成功，断路器关闭，若失败，则继续开启</p><p>熔断类型。</p><ol><li>Open。请求不再调用当前服务，直接进行服务降级处理，打开时间达到设置时间，进入半熔断状态。</li><li>Half Open。部分请求根据规则调用当前服务，若是请求成功并且符合规则则认为服务恢复正常，关闭熔断。</li><li>Closed。不对服务进行熔断处理。</li></ol></blockquote><h3 id="服务熔断所有的配置"><a href="#服务熔断所有的配置" class="headerlink" title="服务熔断所有的配置"></a>服务熔断所有的配置</h3><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">@HystrixCommand(fallbackMethod = &quot;fallbackMethod&quot;, </span></span><br><span class="line"><span class="meta">                groupKey = &quot;strGroupCommand&quot;, </span></span><br><span class="line"><span class="meta">                commandKey = &quot;strCommand&quot;, </span></span><br><span class="line"><span class="meta">                threadPoolKey = &quot;strThreadPool&quot;,</span></span><br><span class="line"><span class="meta">                commandProperties = &#123;</span></span><br><span class="line"><span class="meta">                    // 设置隔离策略，THREAD 表示线程池 SEMAPHORE：信号池隔离</span></span><br><span class="line"><span class="meta">                    @HystrixProperty(name = &quot;execution.isolation.strategy&quot;, value = &quot;THREAD&quot;),</span></span><br><span class="line"><span class="meta">                    // 当隔离策略选择信号池隔离的时候，用来设置信号池的大小（最大并发数）</span></span><br><span class="line"><span class="meta">                    @HystrixProperty(name = &quot;execution.isolation.semaphore.maxConcurrentRequests&quot;, value = &quot;10&quot;),</span></span><br><span class="line"><span class="meta">                    // 配置命令执行的超时时间</span></span><br><span class="line"><span class="meta">                    @HystrixProperty(name = &quot;execution.isolation.thread.timeoutinMilliseconds&quot;, value = &quot;10&quot;),</span></span><br><span class="line"><span class="meta">                    // 是否启用超时时间</span></span><br><span class="line"><span class="meta">                    @HystrixProperty(name = &quot;execution.timeout.enabled&quot;, value = &quot;true&quot;),</span></span><br><span class="line"><span class="meta">                    // 执行超时的时候是否中断</span></span><br><span class="line"><span class="meta">                    @HystrixProperty(name = &quot;execution.isolation.thread.interruptOnTimeout&quot;, value = &quot;true&quot;),</span></span><br><span class="line"><span class="meta">                    // 执行被取消的时候是否中断</span></span><br><span class="line"><span class="meta">                    @HystrixProperty(name = &quot;execution.isolation.thread.interruptOnCancel&quot;, value = &quot;true&quot;),</span></span><br><span class="line"><span class="meta">                    // 允许回调方法执行的最大并发数</span></span><br><span class="line"><span class="meta">                    @HystrixProperty(name = &quot;fallback.isolation.semaphore.maxConcurrentRequests&quot;, value = &quot;10&quot;),</span></span><br><span class="line"><span class="meta">                    // 服务降级是否启用，是否执行回调函数</span></span><br><span class="line"><span class="meta">                    @HystrixProperty(name = &quot;fallback.enabled&quot;, value = &quot;true&quot;),</span></span><br><span class="line"><span class="meta">                    // 是否启用断路器</span></span><br><span class="line"><span class="meta">                    @HystrixProperty(name = &quot;circuitBreaker.enabled&quot;, value = &quot;true&quot;),</span></span><br><span class="line"><span class="meta">                    // 该属性用来设置在滚动时间窗中，断路器熔断的最小请求数。例如，默认该值为 20 的时候，如果滚动时间窗（默认10秒）内仅收到了19个请求， 即使这19个请求都失败了，断路器也不会打开。</span></span><br><span class="line"><span class="meta">                    @HystrixProperty(name = &quot;circuitBreaker.requestVolumeThreshold&quot;, value = &quot;20&quot;),</span></span><br><span class="line"><span class="meta">                    // 该属性用来设置在滚动时间窗中，表示在滚动时间窗中，在请求数量超过 circuitBreaker.requestVolumeThreshold 的情况下，如果错误请求数的百分比超过50, 就把断路器设置为 &quot;打开&quot; 状态，否则就设置为 &quot;关闭&quot; 状态。</span></span><br><span class="line"><span class="meta">                    @HystrixProperty(name = &quot;circuitBreaker.errorThresholdPercentage&quot;, value = &quot;50&quot;),</span></span><br><span class="line"><span class="meta">                    // 该属性用来设置当断路器打开之后的休眠时间窗。 休眠时间窗结束之后，会将断路器置为 &quot;半开&quot; 状态，尝试熔断的请求命令，如果依然失败就将断路器继续设置为 &quot;打开&quot; 状态，如果成功就设置为 &quot;关闭&quot; 状态。</span></span><br><span class="line"><span class="meta">                    @HystrixProperty(name = &quot;circuitBreaker.sleepWindowinMilliseconds&quot;, value = &quot;5000&quot;),</span></span><br><span class="line"><span class="meta">                    // 断路器强制打开</span></span><br><span class="line"><span class="meta">                    @HystrixProperty(name = &quot;circuitBreaker.forceOpen&quot;, value = &quot;false&quot;),</span></span><br><span class="line"><span class="meta">                    // 断路器强制关闭</span></span><br><span class="line"><span class="meta">                    @HystrixProperty(name = &quot;circuitBreaker.forceClosed&quot;, value = &quot;false&quot;),</span></span><br><span class="line"><span class="meta">                    // 滚动时间窗设置，该时间用于断路器判断健康度时需要收集信息的持续时间</span></span><br><span class="line"><span class="meta">                    @HystrixProperty(name = &quot;metrics.rollingStats.timeinMilliseconds&quot;, value = &quot;10000&quot;),</span></span><br><span class="line"><span class="meta">                    // 该属性用来设置滚动时间窗统计指标信息时划分&quot;桶&quot;的数量，断路器在收集指标信息的时候会根据设置的时间窗长度拆分成多个 &quot;桶&quot; 来累计各度量值，每个&quot;桶&quot;记录了一段时间内的采集指标。</span></span><br><span class="line"><span class="meta">                    // 比如 10 秒内拆分成 10 个&quot;桶&quot;收集这样，所以 timeinMilliseconds 必须能被 numBuckets 整除。否则会抛异常</span></span><br><span class="line"><span class="meta">                    @HystrixProperty(name = &quot;metrics.rollingStats.numBuckets&quot;, value = &quot;10&quot;),</span></span><br><span class="line"><span class="meta">                    // 该属性用来设置对命令执行的延迟是否使用百分位数来跟踪和计算。如果设置为 false, 那么所有的概要统计都将返回 -1。</span></span><br><span class="line"><span class="meta">                    @HystrixProperty(name = &quot;metrics.rollingPercentile.enabled&quot;, value = &quot;false&quot;),</span></span><br><span class="line"><span class="meta">                    // 该属性用来设置百分位统计的滚动窗口的持续时间，单位为毫秒。</span></span><br><span class="line"><span class="meta">                    @HystrixProperty(name = &quot;metrics.rollingPercentile.timeInMilliseconds&quot;, value = &quot;60000&quot;),</span></span><br><span class="line"><span class="meta">                    // 该属性用来设置百分位统计滚动窗口中使用 “ 桶 ”的数量。</span></span><br><span class="line"><span class="meta">                    @HystrixProperty(name = &quot;metrics.rollingPercentile.numBuckets&quot;, value = &quot;60000&quot;),</span></span><br><span class="line"><span class="meta">                    // 该属性用来设置在执行过程中每个 “桶” 中保留的最大执行次数。如果在滚动时间窗内发生超过该设定值的执行次数，</span></span><br><span class="line"><span class="meta">                    // 就从最初的位置开始重写。例如，将该值设置为100, 滚动窗口为10秒，若在10秒内一个 “桶 ”中发生了500次执行，</span></span><br><span class="line"><span class="meta">                    // 那么该 “桶” 中只保留 最后的100次执行的统计。另外，增加该值的大小将会增加内存量的消耗，并增加排序百分位数所需的计算时间。</span></span><br><span class="line"><span class="meta">                    @HystrixProperty(name = &quot;metrics.rollingPercentile.bucketSize&quot;, value = &quot;100&quot;),</span></span><br><span class="line"><span class="meta">                    </span></span><br><span class="line"><span class="meta">                    // 该属性用来设置采集影响断路器状态的健康快照（请求的成功、 错误百分比）的间隔等待时间。</span></span><br><span class="line"><span class="meta">                    @HystrixProperty(name = &quot;metrics.healthSnapshot.intervalinMilliseconds&quot;, value = &quot;500&quot;),</span></span><br><span class="line"><span class="meta">                    // 是否开启请求缓存</span></span><br><span class="line"><span class="meta">                    @HystrixProperty(name = &quot;requestCache.enabled&quot;, value = &quot;true&quot;),</span></span><br><span class="line"><span class="meta">                    // HystrixCommand的执行和事件是否打印日志到 HystrixRequestLog 中</span></span><br><span class="line"><span class="meta">                    @HystrixProperty(name = &quot;requestLog.enabled&quot;, value = &quot;true&quot;),</span></span><br><span class="line"><span class="meta">                &#125;,</span></span><br><span class="line"><span class="meta">                threadPoolProperties = &#123;</span></span><br><span class="line"><span class="meta">                    // 该参数用来设置执行命令线程池的核心线程数，该值也就是命令执行的最大并发量</span></span><br><span class="line"><span class="meta">                    @HystrixProperty(name = &quot;coreSize&quot;, value = &quot;10&quot;),</span></span><br><span class="line"><span class="meta">                    // 该参数用来设置线程池的最大队列大小。当设置为 -1 时，线程池将使用 SynchronousQueue 实现的队列，否则将使用 LinkedBlockingQueue 实现的队列。</span></span><br><span class="line"><span class="meta">                    @HystrixProperty(name = &quot;maxQueueSize&quot;, value = &quot;-1&quot;),</span></span><br><span class="line"><span class="meta">                    // 该参数用来为队列设置拒绝阈值。 通过该参数， 即使队列没有达到最大值也能拒绝请求。</span></span><br><span class="line"><span class="meta">                    // 该参数主要是对 LinkedBlockingQueue 队列的补充,因为 LinkedBlockingQueue 队列不能动态修改它的对象大小，而通过该属性就可以调整拒绝请求的队列大小了。</span></span><br><span class="line"><span class="meta">                    @HystrixProperty(name = &quot;queueSizeRejectionThreshold&quot;, value = &quot;5&quot;),</span></span><br><span class="line"><span class="meta">                &#125;</span></span><br><span class="line"><span class="meta">               )</span></span><br><span class="line"><span class="keyword">public</span> String <span class="title function_">doThing</span><span class="params">()</span> &#123;</span><br><span class="line">...</span><br><span class="line">&#125;</span><br><span class="line"></span><br></pre></td></tr></table></figure><h3 id="HystrixDashboard"><a href="#HystrixDashboard" class="headerlink" title="HystrixDashboard"></a>HystrixDashboard</h3><p>HystrixDashboard准实时的调用监控，持续的通过记录所有通过Hystrix发起的请求的执行信息。</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></pre></td><td class="code"><pre><span class="line"><span class="tag">&lt;<span class="name">dependency</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">groupId</span>&gt;</span>org.springframework.cloud<span class="tag">&lt;/<span class="name">groupId</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">artifactId</span>&gt;</span>spring-cloud-starter-netflix-hystrix-dashboard<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><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><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">@EnableHystrixDashboard</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">HystrixDashBoard</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(HystrixDashBoard.class, args);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>然后启动就可以访问到页面了。<a href="http://localhost:9001/hystrix">http://localhost:9001/hystrix</a></p><p>被监控的服务也要设置一下。</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">@SpringBootApplication</span></span><br><span class="line"><span class="meta">@EnableEurekaClient</span></span><br><span class="line"><span class="meta">@EnableCircuitBreaker</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">HystrixPaymentMain</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(HystrixPaymentMain.class, args);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">   <span class="comment">/**</span></span><br><span class="line"><span class="comment">     * 此配置是为了服务监控而配置</span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    <span class="meta">@Bean</span></span><br><span class="line">    <span class="keyword">public</span> ServletRegistrationBean <span class="title function_">getServlet</span><span class="params">()</span> &#123;</span><br><span class="line">        <span class="type">HystrixMetricsStreamServlet</span> <span class="variable">streamServlet</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">HystrixMetricsStreamServlet</span>();</span><br><span class="line">        <span class="type">ServletRegistrationBean</span> <span class="variable">registrationBean</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">ServletRegistrationBean</span>(streamServlet);</span><br><span class="line">        registrationBean.setLoadOnStartup(<span class="number">1</span>);</span><br><span class="line">        registrationBean.addUrlMappings(<span class="string">&quot;/hystrix.stream&quot;</span>);</span><br><span class="line">        registrationBean.setName(<span class="string">&quot;HystrixMetricsStreamServlet&quot;</span>);</span><br><span class="line">        <span class="keyword">return</span> registrationBean;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>然后在<a href="http://localhost:9001/hystrix">http://localhost:9001/hystrix</a>填写监控的地址。</p><p><a href="http://localhost:8003/hystrix.stream">http://localhost:8003/hystrix.stream</a></p><h2 id="Sentinel"><a href="#Sentinel" class="headerlink" title="Sentinel"></a>Sentinel</h2><p>后续单独提出，这里只需要知道能实现服务降级。</p><h1 id="服务网关"><a href="#服务网关" class="headerlink" title="服务网关"></a>服务网关</h1><h2 id="Zuul"><a href="#Zuul" class="headerlink" title="Zuul"></a>Zuul</h2><p>暂不考虑。</p><h2 id="Zuul2"><a href="#Zuul2" class="headerlink" title="Zuul2"></a>Zuul2</h2><p>Zuul2是Zuul的替代版，但是由于开发组的情况复杂，所以暂不考虑。</p><h2 id="SpringCloudGateway"><a href="#SpringCloudGateway" class="headerlink" title="SpringCloudGateway"></a>SpringCloudGateway</h2><p> SpringCloudGateway新一代的网关，主要当作为服务的入口。</p><h3 id="重要的概念"><a href="#重要的概念" class="headerlink" title="重要的概念"></a>重要的概念</h3><ol><li><p>路由（Route）</p><p>路由是构建网关的基本模块，由ID，目标URI，一系列的断言和过滤器组成，如果断言为true则匹配该路由</p></li><li><p>断言（Predicate）</p><p>参考了Java8的一个重要的函数接口<code>Predicate</code>，开发人员可以匹配HTTP请求中的所有内容，如果请求与断言相匹配则进行路由</p></li><li><p>过滤（Filter）</p><p>使用过滤器，可以在请求被路由前或者请求之后对请求进行修改</p></li></ol><h3 id="简单的使用（yml配置）"><a href="#简单的使用（yml配置）" class="headerlink" title="简单的使用（yml配置）"></a>简单的使用（yml配置）</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></pre></td><td class="code"><pre><span class="line"><span class="tag">&lt;<span class="name">dependency</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">groupId</span>&gt;</span>org.springframework.cloud<span class="tag">&lt;/<span class="name">groupId</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">artifactId</span>&gt;</span>spring-cloud-starter-gateway<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><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">9527</span></span><br><span class="line"></span><br><span class="line"><span class="attr">spring:</span></span><br><span class="line">  <span class="attr">application:</span></span><br><span class="line">    <span class="attr">name:</span> <span class="string">cloud-gateway</span></span><br><span class="line"></span><br><span class="line"><span class="attr">eureka:</span></span><br><span class="line">  <span class="attr">instance:</span></span><br><span class="line">    <span class="attr">hostname:</span> <span class="string">cloud-gateway</span></span><br><span class="line">  <span class="attr">client:</span></span><br><span class="line">    <span class="attr">register-with-eureka:</span> <span class="literal">true</span></span><br><span class="line">    <span class="attr">fetch-registry:</span> <span class="literal">true</span></span><br><span class="line">    <span class="attr">service-url:</span></span><br><span class="line">      <span class="attr">defaultZone:</span> <span class="string">http://eureka7001.com:7001/eureka</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></pre></td><td class="code"><pre><span class="line"><span class="meta">@SpringBootApplication</span></span><br><span class="line"><span class="meta">@EnableEurekaClient</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">GatewayMain</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(GatewayMain.class, args);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>网关的配置主要是在yml配置文件。</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></pre></td><td class="code"><pre><span class="line"><span class="attr">spring:</span></span><br><span class="line">  <span class="attr">application:</span></span><br><span class="line">    <span class="attr">name:</span> <span class="string">cloud-gateway</span></span><br><span class="line">  <span class="attr">cloud:</span></span><br><span class="line">    <span class="attr">gateway:</span></span><br><span class="line">      <span class="attr">routes:</span></span><br><span class="line">        <span class="bullet">-</span> <span class="attr">id:</span> <span class="string">routh1</span>                <span class="comment"># 路由ID，需要唯一</span></span><br><span class="line">          <span class="attr">uri:</span> <span class="string">http://localhost:8001</span>  <span class="comment"># 匹配后提供的服务路由地址</span></span><br><span class="line">          <span class="attr">predicates:</span>               <span class="comment"># 断言，路径匹配进行路由</span></span><br><span class="line">            <span class="bullet">-</span> <span class="string">Path=/payment/**</span></span><br></pre></td></tr></table></figure><blockquote><p>启动网关服务的时候，出现了一个小错误。</p><p>原因是因为多添加了一个web依赖导致的，网关服务是不需要web依赖的，删除即可。</p></blockquote><p>此时网关服务启动了，只要符合断言的路径就会按照服务地址访问。</p><blockquote><p><a href="http://localhost:9527/payment/**">http://localhost:9527/payment/**</a> </p><p>这样的路由就是符合路由的，经过网关转发就会转发到下面的路由。</p><p><a href="http://localhost:8001/payment/**">http://localhost:8001/payment/**</a> </p></blockquote><h3 id="编码方式配置"><a href="#编码方式配置" class="headerlink" title="编码方式配置"></a>编码方式配置</h3><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">package</span> com.yww.springcloud.config;</span><br><span class="line"></span><br><span class="line"><span class="keyword">import</span> org.springframework.cloud.gateway.route.RouteLocator;</span><br><span class="line"><span class="keyword">import</span> org.springframework.cloud.gateway.route.builder.RouteLocatorBuilder;</span><br><span class="line"><span class="keyword">import</span> org.springframework.context.annotation.Bean;</span><br><span class="line"><span class="keyword">import</span> org.springframework.context.annotation.Configuration;</span><br><span class="line"></span><br><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * &lt;p&gt;</span></span><br><span class="line"><span class="comment"> *     网关配置</span></span><br><span class="line"><span class="comment"> * &lt;/p&gt;</span></span><br><span class="line"><span class="comment"> *</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@author</span> yww</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></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_">GatewayConfig</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> RouteLocator <span class="title function_">customRouteLocator</span><span class="params">(RouteLocatorBuilder routeLocatorBuilder)</span> &#123;</span><br><span class="line">        RouteLocatorBuilder.<span class="type">Builder</span> <span class="variable">routes</span> <span class="operator">=</span> routeLocatorBuilder.routes();</span><br><span class="line">        <span class="comment">// 路由ID -&gt; 断言 -&gt; 提供的匹配路径</span></span><br><span class="line">        routes.route(<span class="string">&quot;route&quot;</span>, r -&gt; r.path(<span class="string">&quot;/payment/**&quot;</span>)</span><br><span class="line">                .uri(<span class="string">&quot;http://localhost:8001&quot;</span>)</span><br><span class="line">        ).build();</span><br><span class="line">        <span class="keyword">return</span> routes.build();</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><p>我们在配置路由的时候就可以发现，路由地址是写死的，也就是指定了一个微服务，如果服务是集群的，就很麻烦了，所以可以考虑用微服务名来实现动态路由进行转发。</p><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></pre></td><td class="code"><pre><span class="line"><span class="attr">spring:</span></span><br><span class="line">  <span class="attr">application:</span></span><br><span class="line">    <span class="attr">name:</span> <span class="string">cloud-gateway</span></span><br><span class="line">  <span class="attr">cloud:</span></span><br><span class="line">    <span class="attr">gateway:</span></span><br><span class="line">      <span class="attr">discovery:</span></span><br><span class="line">        <span class="attr">locator:</span></span><br><span class="line">          <span class="comment"># 自动以注册中心的服务名进行路由注册</span></span><br><span class="line">          <span class="attr">enabled:</span> <span class="literal">true</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></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_">GatewayConfig</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> RouteLocator <span class="title function_">customRouteLocator</span><span class="params">(RouteLocatorBuilder routeLocatorBuilder)</span> &#123;</span><br><span class="line">        RouteLocatorBuilder.<span class="type">Builder</span> <span class="variable">routes</span> <span class="operator">=</span> routeLocatorBuilder.routes();</span><br><span class="line">        routes.route(<span class="string">&quot;route&quot;</span>, r -&gt; r.path(<span class="string">&quot;/payment/**&quot;</span>)</span><br><span class="line">                <span class="comment">// lb开启负载均衡，不添加启动不了</span></span><br><span class="line">                .uri(<span class="string">&quot;lb://CLOUD-PAYMENT-PROVIDER&quot;</span>)</span><br><span class="line">        ).build();</span><br><span class="line">        <span class="keyword">return</span> routes.build();</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="常用Predicate"><a href="#常用Predicate" class="headerlink" title="常用Predicate"></a>常用Predicate</h3><p>断言其实是有很多种的，上述使用的都只是<code>path</code>，即路径符合就返回true，接下来了解一下常用的Predicate。</p><h4 id="Path断言"><a href="#Path断言" class="headerlink" title="Path断言"></a>Path断言</h4><p>Path断言表示路径符合断言即为成功。</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></pre></td><td class="code"><pre><span class="line"><span class="attr">spring:</span></span><br><span class="line">  <span class="attr">application:</span></span><br><span class="line">    <span class="attr">name:</span> <span class="string">cloud-gateway</span></span><br><span class="line">  <span class="attr">cloud:</span></span><br><span class="line">    <span class="attr">gateway:</span></span><br><span class="line">      <span class="attr">routes:</span></span><br><span class="line">        <span class="bullet">-</span> <span class="attr">id:</span> <span class="string">routh1</span></span><br><span class="line">          <span class="attr">uri:</span> <span class="string">http://localhost:8001</span></span><br><span class="line">          <span class="attr">predicates:</span></span><br><span class="line">          <span class="comment"># http://localhost:9527/payment/get符合断言</span></span><br><span class="line">          <span class="comment"># http://localhost:9527/yww/payment/get不符合断言</span></span><br><span class="line">            <span class="bullet">-</span> <span class="string">Path=/payment/**</span></span><br></pre></td></tr></table></figure><h4 id="关于时间的断言"><a href="#关于时间的断言" class="headerlink" title="关于时间的断言"></a>关于时间的断言</h4><blockquote><p>时间的配置可以如下。</p><p>比如2021-12-24T11:14:05.346576Z[Etc/UTC]</p><p>可以通过ZonedDateTime.now()的方法生成。</p></blockquote><ul><li>After断言代表指定时间之后即为符合断言。（后面接一个时间）</li><li>Before断言代表指定时间之前即为符合断言。（后面接一个时间）</li><li>Between断言代表指定时间内即为符合断言。（后面接两个时间，配置文件隔开用逗号）</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><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><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">spring:</span></span><br><span class="line">  <span class="attr">application:</span></span><br><span class="line">    <span class="attr">name:</span> <span class="string">cloud-gateway</span></span><br><span class="line">  <span class="attr">cloud:</span></span><br><span class="line">    <span class="attr">gateway:</span></span><br><span class="line">      <span class="attr">routes:</span></span><br><span class="line">        <span class="bullet">-</span> <span class="attr">id:</span> <span class="string">routh1</span></span><br><span class="line">          <span class="attr">uri:</span> <span class="string">http://localhost:8001</span></span><br><span class="line">          <span class="attr">predicates:</span></span><br><span class="line">          <span class="comment"># After时间之后</span></span><br><span class="line">            <span class="bullet">-</span> <span class="string">After=2021-12-24T11:14:05.346576Z[Etc/UTC]</span></span><br><span class="line">            <span class="comment"># Before时间之前</span></span><br><span class="line">            <span class="bullet">-</span> <span class="string">Before=2021-12-24T12:14:05.346576Z[Etc/UTC]</span></span><br><span class="line">            <span class="comment"># Between两个时间之间</span></span><br><span class="line">            <span class="bullet">-</span> <span class="string">Between=2021-12-24T11:14:05.346576Z[Etc/UTC],2021-12-24T12:14:05.346576Z[Etc/UTC]</span></span><br></pre></td></tr></table></figure><h4 id="Cookie断言"><a href="#Cookie断言" class="headerlink" title="Cookie断言"></a>Cookie断言</h4><p>Cookie断言代表请求带了指定Cokkie后才符合断言。</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></pre></td><td class="code"><pre><span class="line"><span class="attr">spring:</span></span><br><span class="line">  <span class="attr">application:</span></span><br><span class="line">    <span class="attr">name:</span> <span class="string">cloud-gateway</span></span><br><span class="line">  <span class="attr">cloud:</span></span><br><span class="line">    <span class="attr">gateway:</span></span><br><span class="line">      <span class="attr">routes:</span></span><br><span class="line">        <span class="bullet">-</span> <span class="attr">id:</span> <span class="string">routh1</span></span><br><span class="line">          <span class="attr">uri:</span> <span class="string">http://localhost:8001</span></span><br><span class="line">          <span class="attr">predicates:</span></span><br><span class="line">          <span class="comment"># 携带了这个Cookie，之后接键值对，值可以是正则表达式</span></span><br><span class="line">            <span class="bullet">-</span> <span class="string">Cookie=username,yww</span></span><br></pre></td></tr></table></figure><h4 id="Header断言"><a href="#Header断言" class="headerlink" title="Header断言"></a>Header断言</h4><p>Header断言代表请求带了指定请求头后才符合断言。</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></pre></td><td class="code"><pre><span class="line"><span class="attr">spring:</span></span><br><span class="line">  <span class="attr">application:</span></span><br><span class="line">    <span class="attr">name:</span> <span class="string">cloud-gateway</span></span><br><span class="line">  <span class="attr">cloud:</span></span><br><span class="line">    <span class="attr">gateway:</span></span><br><span class="line">      <span class="attr">routes:</span></span><br><span class="line">        <span class="bullet">-</span> <span class="attr">id:</span> <span class="string">routh1</span></span><br><span class="line">          <span class="attr">uri:</span> <span class="string">http://localhost:8001</span></span><br><span class="line">          <span class="attr">predicates:</span></span><br><span class="line">          <span class="comment"># 携带了这个Header请求头，之后接键值对，值可以是正则表达式</span></span><br><span class="line">          <span class="comment"># 这里表示请求头中有X-Request-Id，且值为整数才符合断言</span></span><br><span class="line">            <span class="bullet">-</span> <span class="string">Header=X-Request-Id,</span> <span class="string">\d+</span></span><br></pre></td></tr></table></figure><h4 id="Host断言"><a href="#Host断言" class="headerlink" title="Host断言"></a>Host断言</h4><p>Host断言代表指定主机符合断言。</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></pre></td><td class="code"><pre><span class="line"><span class="attr">spring:</span></span><br><span class="line">  <span class="attr">application:</span></span><br><span class="line">    <span class="attr">name:</span> <span class="string">cloud-gateway</span></span><br><span class="line">  <span class="attr">cloud:</span></span><br><span class="line">    <span class="attr">gateway:</span></span><br><span class="line">      <span class="attr">routes:</span></span><br><span class="line">        <span class="bullet">-</span> <span class="attr">id:</span> <span class="string">routh1</span></span><br><span class="line">          <span class="attr">uri:</span> <span class="string">http://localhost:8001</span></span><br><span class="line">          <span class="attr">predicates:</span></span><br><span class="line">          <span class="comment"># Host后接一组匹配的域名列表</span></span><br><span class="line">            <span class="bullet">-</span> <span class="string">Host=**.somehost.org,**.anotherhost.org</span></span><br></pre></td></tr></table></figure><h4 id="Method断言"><a href="#Method断言" class="headerlink" title="Method断言"></a>Method断言</h4><p>Header断言代表请求带了指定请求方法才符合断言。</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></pre></td><td class="code"><pre><span class="line"><span class="attr">spring:</span></span><br><span class="line">  <span class="attr">application:</span></span><br><span class="line">    <span class="attr">name:</span> <span class="string">cloud-gateway</span></span><br><span class="line">  <span class="attr">cloud:</span></span><br><span class="line">    <span class="attr">gateway:</span></span><br><span class="line">      <span class="attr">routes:</span></span><br><span class="line">        <span class="bullet">-</span> <span class="attr">id:</span> <span class="string">routh1</span></span><br><span class="line">          <span class="attr">uri:</span> <span class="string">http://localhost:8001</span></span><br><span class="line">          <span class="attr">predicates:</span></span><br><span class="line">          <span class="comment"># 使用了GET方法即为符合断言</span></span><br><span class="line">            <span class="bullet">-</span> <span class="string">Method-GET</span></span><br></pre></td></tr></table></figure><h4 id="Query断言"><a href="#Query断言" class="headerlink" title="Query断言"></a>Query断言</h4><p>Query断言代表请求需要带指定参数。</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></pre></td><td class="code"><pre><span class="line"><span class="attr">spring:</span></span><br><span class="line">  <span class="attr">application:</span></span><br><span class="line">    <span class="attr">name:</span> <span class="string">cloud-gateway</span></span><br><span class="line">  <span class="attr">cloud:</span></span><br><span class="line">    <span class="attr">gateway:</span></span><br><span class="line">      <span class="attr">routes:</span></span><br><span class="line">        <span class="bullet">-</span> <span class="attr">id:</span> <span class="string">routh1</span></span><br><span class="line">          <span class="attr">uri:</span> <span class="string">http://localhost:8001</span></span><br><span class="line">          <span class="attr">predicates:</span></span><br><span class="line">          <span class="comment"># 携带了username参数，且值为整数（正则表达式）</span></span><br><span class="line">            <span class="bullet">-</span> <span class="string">Query=username,</span> <span class="string">\d+</span></span><br></pre></td></tr></table></figure><h3 id="过滤器"><a href="#过滤器" class="headerlink" title="过滤器"></a>过滤器</h3><p>Spring Cloud Gateway内置了多种路由过滤器，由<code>GatewayFilter</code>的工厂类来产生，主要用于添加或修改请求和响应。</p><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></pre></td><td class="code"><pre><span class="line"><span class="attr">spring:</span></span><br><span class="line">  <span class="attr">application:</span></span><br><span class="line">    <span class="attr">name:</span> <span class="string">cloud-gateway</span></span><br><span class="line">  <span class="attr">cloud:</span></span><br><span class="line">    <span class="attr">gateway:</span></span><br><span class="line">      <span class="attr">routes:</span></span><br><span class="line">        <span class="bullet">-</span> <span class="attr">id:</span> <span class="string">routh1</span></span><br><span class="line">          <span class="attr">uri:</span> <span class="string">http://localhost:8001</span></span><br><span class="line">          <span class="attr">filters:</span></span><br><span class="line">            <span class="comment"># 过滤器工厂会在匹配的请求头上加上X-Request-Id这个请求头，且值为1024</span></span><br><span class="line">          <span class="bullet">-</span> <span class="string">AddRequestParameter=X-Request-Id,1024</span></span><br></pre></td></tr></table></figure><p>自带的太多了，所以主要学习自定义的过滤器。</p><p>实现步骤。</p><ol><li><p>实现两个重要接口。<code>GlobalFilter</code>和<code>Ordered</code>。</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> *     这个接口是代表优先级顺序的，一般是优先级越小越高。</span></span><br><span class="line"><span class="comment"> **/</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">interface</span> <span class="title class_">Ordered</span> &#123;</span><br><span class="line">    <span class="type">int</span> <span class="variable">HIGHEST_PRECEDENCE</span> <span class="operator">=</span> -<span class="number">2147483648</span>;</span><br><span class="line">    <span class="type">int</span> <span class="variable">LOWEST_PRECEDENCE</span> <span class="operator">=</span> <span class="number">2147483647</span>;</span><br><span class="line">    <span class="type">int</span> <span class="title function_">getOrder</span><span class="params">()</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></pre></td><td class="code"><pre><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> *     这个接口是代表实现网关全局过滤器的主要实现。</span></span><br><span class="line"><span class="comment"> **/</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">interface</span> <span class="title class_">GlobalFilter</span> &#123;</span><br><span class="line">    Mono&lt;Void&gt; <span class="title function_">filter</span><span class="params">(ServerWebExchange exchange, GatewayFilterChain chain)</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><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"> * &lt;p&gt;</span></span><br><span class="line"><span class="comment"> *     自定义网关过滤器</span></span><br><span class="line"><span class="comment"> * &lt;/p&gt;</span></span><br><span class="line"><span class="comment"> *</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@author</span> yww</span></span><br><span class="line"><span class="comment"> **/</span></span><br><span class="line"><span class="meta">@Component</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">GateWayFilter</span> <span class="keyword">implements</span> <span class="title class_">GlobalFilter</span>, Ordered &#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">@Override</span></span><br><span class="line">    <span class="keyword">public</span> Mono&lt;Void&gt; <span class="title function_">filter</span><span class="params">(ServerWebExchange exchange, GatewayFilterChain chain)</span> &#123;</span><br><span class="line">        System.out.println(<span class="string">&quot;请求进入全局过滤器------&quot;</span> + <span class="keyword">new</span> <span class="title class_">Date</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> exchange.getRequest().getQueryParams().getFirst(<span class="string">&quot;username&quot;</span>);</span><br><span class="line">        <span class="comment">// 如果该值等于yww即为合法用户直接放行</span></span><br><span class="line">        <span class="keyword">if</span> (<span class="string">&quot;yww&quot;</span>.equals(username)) &#123;</span><br><span class="line">            <span class="keyword">return</span> chain.filter(exchange);</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="comment">// 值不为yww则为非法用户，直接返回响应</span></span><br><span class="line">        System.out.println(<span class="string">&quot;非法用户&quot;</span>);</span><br><span class="line">        exchange.getResponse().setStatusCode(HttpStatus.NOT_ACCEPTABLE);</span><br><span class="line">        <span class="keyword">return</span> exchange.getResponse().setComplete();</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     * 过滤器的优先级，值越小优先级越高</span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="keyword">public</span> <span class="type">int</span> <span class="title function_">getOrder</span><span class="params">()</span> &#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">&#125;</span><br></pre></td></tr></table></figure></li></ol><h1 id="服务配置"><a href="#服务配置" class="headerlink" title="服务配置"></a>服务配置</h1><h2 id="Config"><a href="#Config" class="headerlink" title="Config"></a>Config</h2><p> SpringCloud Config为微服务架构中的微服务提供了集中化的外部配置支持，配置服务器为各个不同微服务应用的所有环境提供了一个中心化的外部配置。</p><p>SpringCloud Config分为服务端和客户端两部分。</p><ul><li>服务端也称为分布式配置中心，它是一个独立的微服务应用，用来连接配置服务器并为客户端提供获取配置信息。</li><li>客户端则是通过指定的配置中心来管理应用资源，以及与业务相关的配置内容，并在启动的时候从配置中心获取和加载配置信息配置服务器默认采用git来存储配置信息，这样就有助于对环境配置进行版本管理，并且可以通过git客户端工具来方便的管理和访问配置内容。</li></ul><blockquote><p>SrpingCloud Config默认是使用git来存储配置文件，使用的是http/https访问的形式</p></blockquote><h3 id="简单使用-1"><a href="#简单使用-1" class="headerlink" title="简单使用"></a>简单使用</h3><h4 id="服务端"><a href="#服务端" class="headerlink" title="服务端"></a>服务端</h4><ol><li><p>因为使用的是git来存储配置文件，所以需要提前创建一个github仓库，并提前添加一些配置文件。</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></pre></td><td class="code"><pre><span class="line"><span class="tag">&lt;<span class="name">dependency</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">groupId</span>&gt;</span>org.springframework.cloud<span class="tag">&lt;/<span class="name">groupId</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">artifactId</span>&gt;</span>spring-cloud-config-server<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>服务端的配置文件编写。</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><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="attr">server:</span></span><br><span class="line">  <span class="attr">port:</span> <span class="number">3344</span></span><br><span class="line"></span><br><span class="line"><span class="attr">spring:</span></span><br><span class="line">  <span class="attr">application:</span></span><br><span class="line">    <span class="attr">name:</span> <span class="string">cloud-config-center</span></span><br><span class="line">  <span class="attr">cloud:</span></span><br><span class="line">    <span class="attr">config:</span></span><br><span class="line">      <span class="attr">server:</span></span><br><span class="line">        <span class="attr">git:</span></span><br><span class="line">          <span class="comment"># 仓库地址（ssh方式连接不上就使用https的方式）</span></span><br><span class="line">          <span class="attr">uri:</span> <span class="string">git@github.com:whyneh/SpringCloudConfig.git</span></span><br><span class="line">          <span class="comment"># 仓库的相对地址(即扫描仓库下的某个文件加)</span></span><br><span class="line">          <span class="attr">search-paths:</span></span><br><span class="line">            <span class="bullet">-</span> <span class="string">SpringCloudConfig</span></span><br><span class="line">          <span class="comment"># 如果是私有的仓库，还需要添加github的用户名和密码</span></span><br><span class="line">          <span class="attr">username:</span></span><br><span class="line">          <span class="attr">password:</span> </span><br><span class="line">      <span class="comment"># 读取的分支</span></span><br><span class="line">      <span class="attr">label:</span> <span class="string">main</span></span><br><span class="line"><span class="attr">eureka:</span></span><br><span class="line">  <span class="attr">client:</span></span><br><span class="line">    <span class="attr">service-url:</span></span><br><span class="line">      <span class="attr">defaultZone:</span> <span class="string">http://localhost:7001/eureka</span></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><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">@EnableConfigServer</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">ConfigApplication</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(ConfigApplication.class, args);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></li><li><p>可以通过URL<localhost:3344/main/config-dev.yml>就能读取的github仓库里的配置文件了，至此服务端已经完成建立。</p></li></ol><h4 id="客户端"><a href="#客户端" class="headerlink" title="客户端"></a>客户端</h4><ol><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></pre></td><td class="code"><pre><span class="line"><span class="tag">&lt;<span class="name">dependency</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">groupId</span>&gt;</span>org.springframework.cloud<span class="tag">&lt;/<span class="name">groupId</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">artifactId</span>&gt;</span>spring-cloud-starter-config<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>主要的核心就在于配置文件。</p><ul><li>application.yml是用户级别的配置文件</li><li>bootstrap.yml是系统级的配置文件，优先级更高。</li><li>它会先去加载bootstrap.yml的配置，然后在加载application.yml配置</li><li>在client模块就需要使用bootstrap.yml</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><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><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">3355</span></span><br><span class="line"></span><br><span class="line"><span class="attr">spring:</span></span><br><span class="line">  <span class="attr">application:</span></span><br><span class="line">    <span class="attr">name:</span> <span class="string">config-client</span></span><br><span class="line">  <span class="attr">cloud:</span></span><br><span class="line">    <span class="attr">config:</span></span><br><span class="line">      <span class="comment"># 分支名称</span></span><br><span class="line">      <span class="attr">label:</span> <span class="string">main</span></span><br><span class="line">      <span class="comment"># 配置文件名称</span></span><br><span class="line">      <span class="attr">name:</span> <span class="string">config</span></span><br><span class="line">      <span class="comment"># 后缀名称</span></span><br><span class="line">      <span class="attr">profile:</span> <span class="string">dev</span></span><br><span class="line">      <span class="comment"># 配置中心地址</span></span><br><span class="line">      <span class="attr">uri:</span> <span class="string">http://localhost:3344</span></span><br><span class="line">      <span class="comment"># 上述四个配置结合起来就表示读取http://localhost:3344/main/config-dev.yml的文件</span></span><br><span class="line"><span class="attr">eureka:</span></span><br><span class="line">  <span class="attr">client:</span></span><br><span class="line">    <span class="attr">service-url:</span></span><br><span class="line">      <span class="attr">defaultZone:</span> <span class="string">http://localhost:7001/eurek</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><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">@EnableEurekaClient</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">ConfigClientMain</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(ConfigClientMain.class, args);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></li><li><p>测试是否能够读取github上的配置文件。</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><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="keyword">public</span> <span class="keyword">class</span> <span class="title class_">ConfigClientController</span> &#123;</span><br><span class="line">    <span class="comment">// 读取配置文件中的config下的info信息</span></span><br><span class="line">    <span class="meta">@Value(&quot;$&#123;config.info&#125;&quot;)</span></span><br><span class="line">    <span class="keyword">private</span> String configInfo;</span><br><span class="line">    </span><br><span class="line">    <span class="meta">@GetMapping(&quot;/configInfo&quot;)</span></span><br><span class="line">    <span class="keyword">public</span> String <span class="title function_">getConfigInfo</span><span class="params">()</span> &#123;</span><br><span class="line">        <span class="keyword">return</span> <span class="built_in">this</span>.configInfo;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></li><li><p>然后访问这个接口<localhost:3355/configInfo>就能获取到这段配置文本了。</p></li></ol><h3 id="分布式配置的动态刷新问题"><a href="#分布式配置的动态刷新问题" class="headerlink" title="分布式配置的动态刷新问题"></a>分布式配置的动态刷新问题</h3><p>当我们修改了github上的配置文件的内容后，服务端的配置中心读取到是新的配置文件，但是客户端怎么读取，都是之前的配置文件的内容，这就是分布式配置的动态刷新问题。（总不能重启客户端吧，那动态配置的好处就不能体现了）</p><h3 id="手动刷新"><a href="#手动刷新" class="headerlink" title="手动刷新"></a>手动刷新</h3><p>避免客户端每次都要刷新，就需要解决中国刷新问题。</p><ol><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></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-actuator<span class="tag">&lt;/<span class="name">artifactId</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;/<span class="name">dependency</span>&gt;</span></span><br></pre></td></tr></table></figure></li><li><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></pre></td><td class="code"><pre><span class="line"><span class="comment"># 暴露监控端口</span></span><br><span class="line"><span class="attr">management:</span></span><br><span class="line">  <span class="attr">endpoints:</span></span><br><span class="line">    <span class="attr">web:</span></span><br><span class="line">      <span class="attr">exposure:</span></span><br><span class="line">        <span class="attr">include:</span> <span class="string">&quot;*&quot;</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><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><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">@RefreshScope</span></span><br><span class="line"><span class="meta">@RestController</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">ConfigClientController</span> &#123;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Value(&quot;$&#123;config.info&#125;&quot;)</span></span><br><span class="line">    <span class="keyword">private</span> String configInfo;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@GetMapping(&quot;/configInfo&quot;)</span></span><br><span class="line">    <span class="keyword">public</span> String <span class="title function_">getConfigInfo</span><span class="params">()</span> &#123;</span><br><span class="line">        <span class="keyword">return</span> <span class="built_in">this</span>.configInfo;</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>每次当github配置文件发生变更时，配置中心会跟着变，此时发送一个POST请求刷新客户端的配置即可生效。</p><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">curl -X POST <span class="string">&quot;http://localhost:3355/actuator/refresh&quot;</span></span><br></pre></td></tr></table></figure></li><li><p>这就成功刷新了客户端的配置内容到最新的版本。</p></li></ol><blockquote><p>从手动刷新这里也能看出来一个问题，那就是手动刷新只能刷新一个服务。</p><p>如果有很多个服务，怎样实现动态刷新全部的服务配置文件，还是需要思考的。</p><p>这里就可以配合SpringCloud Bus消息总线来实现这个功能。</p><p>SpringCloud Config大多数都是和SpringCloud Bus配合使用的。 </p></blockquote><h2 id="Nacos-1"><a href="#Nacos-1" class="headerlink" title="Nacos"></a>Nacos</h2><p>Nacos可以实现服务配置，后续会单独学习。</p><h1 id="消息总线"><a href="#消息总线" class="headerlink" title="消息总线"></a>消息总线</h1><h2 id="Bus"><a href="#Bus" class="headerlink" title="Bus"></a>Bus</h2><p>SpringCloud Bus是用来将分布式系统的节点与轻量级消息系统链接起来的框架，整合了Java的事件处理机制和消息中间件的功能。</p><p>Bus目前只支持两种消息代理。</p><ol><li>RabbitMQ</li><li>Kafka</li></ol><p>SpringCloud Bus配合SpringCloud Config使用可以实现配置的动态刷新。</p>]]></content:encoded>
            
            
            <category domain="https://yww52.com/categories/%E6%88%91%E7%9A%84%E6%8A%80%E6%9C%AF%E5%AD%A6%E4%B9%A0%E7%89%A9%E8%AF%AD%E6%9E%9C%E7%84%B6%E6%9C%89%E9%97%AE%E9%A2%98/">我的技术学习物语果然有问题</category>
            
            <category domain="https://yww52.com/categories/%E6%88%91%E7%9A%84%E6%8A%80%E6%9C%AF%E5%AD%A6%E4%B9%A0%E7%89%A9%E8%AF%AD%E6%9E%9C%E7%84%B6%E6%9C%89%E9%97%AE%E9%A2%98/%E5%88%86%E5%B8%83%E5%BC%8F/">分布式</category>
            
            
            
            <comments>https://yww52.com/posts/96a41905/#disqus_thread</comments>
            
        </item>
        
        <item>
            <title>lambda表达式与Stream流</title>
            <link>https://yww52.com/posts/8a8aa27b/</link>
            <guid>https://yww52.com/posts/8a8aa27b/</guid>
            <pubDate>Sun, 05 Dec 2021 16:00:00 GMT</pubDate>
            
            <description>Java8的新特性，优化代码。</description>
            
            
            
            <content:encoded><![CDATA[<img src="https://img.yww52.com/2021/12/2021-12-6top_img.jpg" alt="lambda表达式与Stream流" style="max-width:100%;height:auto;margin-bottom:15px;"
  /><h1 id="Lambda表达式"><a href="#Lambda表达式" class="headerlink" title="Lambda表达式"></a>Lambda表达式</h1><p>Lambda表达式出现主要是为了简化代码，若某一个接口只有一个抽象方法，就能使用Lambda表达式来进行简化这个接口的实现类。</p><h2 id="Lambda表达式的六种情况"><a href="#Lambda表达式的六种情况" class="headerlink" title="Lambda表达式的六种情况"></a>Lambda表达式的六种情况</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></pre></td><td class="code"><pre><span class="line"><span class="comment">// 1 无参，无返回值</span></span><br><span class="line">      <span class="type">Runnable</span> <span class="variable">r</span> <span class="operator">=</span> () -&gt; System.out.println(<span class="string">&quot;1&quot;</span>);</span><br><span class="line">      <span class="comment">// 2 一个参数，无返回值</span></span><br><span class="line">      Consumer&lt;String&gt; con = (String s) -&gt; &#123;</span><br><span class="line">          System.out.println(s);</span><br><span class="line">      &#125;;</span><br><span class="line">      <span class="comment">// 3 数据类型可以省略，编译器有类型推断</span></span><br><span class="line">      Consumer&lt;String&gt; con1 = (s) -&gt; &#123;</span><br><span class="line">          System.out.println(s);</span><br><span class="line">      &#125;;</span><br><span class="line">      <span class="comment">// 4 若只需要一个参数，小括号能省略</span></span><br><span class="line">      Consumer&lt;String&gt; con2 = s -&gt; &#123;</span><br><span class="line">          System.out.println(s);</span><br><span class="line">      &#125;;</span><br><span class="line">      <span class="comment">// 5 需要两个或以上的参数，可以有返回值,不只有一条语句</span></span><br><span class="line">      Comparator&lt;Integer&gt; com1 = (o1, o2) -&gt; &#123;</span><br><span class="line">          System.out.println(o1);</span><br><span class="line">          System.out.println(o2);</span><br><span class="line">          <span class="keyword">return</span> o1.compareTo(o2);</span><br><span class="line">      &#125;;</span><br><span class="line">      <span class="comment">// 6 Lambda体只有一条语句，大括号能省略，return也可以省略</span></span><br><span class="line">      Consumer&lt;String&gt; con3 = s -&gt; System.out.println(s);</span><br></pre></td></tr></table></figure><h2 id="Java内置的四大核心函数式接口"><a href="#Java内置的四大核心函数式接口" class="headerlink" title="Java内置的四大核心函数式接口"></a>Java内置的四大核心函数式接口</h2><ol><li><p><code>Consumer&lt;T&gt;</code></p><p>消费型接口   对T进行操作,不返回结果，方法为 <code>void accept(T t)</code></p></li><li><p><code>Supplier&lt;T&gt;</code></p><p>供给型接口   不接收参数，返回类型T的数据，方法为  <code>T get()</code></p></li><li><p><code>Function&lt;T,R&gt;</code></p><p>函数型接口   对类型为T的对象操作，返回R类型的数据，方法为 <code>R apply(T t)</code></p></li><li><p><code>Predicate&lt;T&gt;</code></p><p>断定型接口   对类型T的数据进行判断，返回Boolean值，方法为 <code>boolean test(T t)</code></p></li></ol><h2 id="方法引用"><a href="#方法引用" class="headerlink" title="方法引用"></a>方法引用</h2><p>在lambda体中引用另一个方法，即用另一个方法来实现lambda体。</p><p>使用格式     <code>类（或对象）:: 方法名</code></p><ol><li>对象::非静态方法</li><li>类::静态方法</li><li>类::非静态方法</li></ol><p>方法引用的方法的形参列表和返回类型要与lambda一样才能使用该方法引用（针对于前两种格式）</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></pre></td><td class="code"><pre><span class="line"><span class="comment">// 比如   Comparator 中的int compare(T1 t1, T2 t2)</span></span><br><span class="line"><span class="comment">// String中的int t1.compareTo(t2)</span></span><br><span class="line"><span class="comment">// 这种就是形参列表不一样，但是是由t1来调用一个t2参数的方法，所以就可以用第三种格式</span></span><br><span class="line"><span class="comment">// t1作为方法的调用者，t2作为形参</span></span><br><span class="line">String::compareTo</span><br></pre></td></tr></table></figure><h2 id="构造器引用"><a href="#构造器引用" class="headerlink" title="构造器引用"></a>构造器引用</h2><p>相当于lambda引用中创建一个对象，调用了一个构造方法。</p><figure class="highlight java"><table><tr><td class="gutter"><pre><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">// lambda表达式写法</span></span><br><span class="line">Supplier&lt;String&gt; str = s -&gt; <span class="keyword">new</span> <span class="title class_">String</span>(s);</span><br><span class="line"><span class="comment">// 构造器引用的写法</span></span><br><span class="line">Supplier&lt;String&gt; str = String::<span class="keyword">new</span>;</span><br></pre></td></tr></table></figure><h1 id="Stream流"><a href="#Stream流" class="headerlink" title="Stream流"></a>Stream流</h1><p>Stream流的使用主要分为三个步骤。</p><ol><li>创建Stream流。从数据源（集合，数组等）转换成Stream流。</li><li>中间操作。对数据源的数据进行处理。</li><li>终止操作。执行终止操作，就会执行中间操作的处理，产生结果。</li></ol><h2 id="创建Stream流"><a href="#创建Stream流" class="headerlink" title="创建Stream流"></a>创建Stream流</h2><ol><li>集合获取Stream流的方法<ul><li><code>list.stream()</code>串行</li><li><code>list.paralleStream()</code> 并行</li></ul></li><li>数组获取Stream流的方法<ul><li>通过Arrays的静态方法stream()得到Stream流<code>Arrays.stream(int[])</code></li></ul></li><li>通过<code>Stream.of()</code>来获取Stream流</li><li>创建无限流（用于编造数据）<ul><li><code>Stream.iterate()</code></li><li><code>Stream.generate()</code></li></ul></li></ol><h2 id="Stream流的中间操作"><a href="#Stream流的中间操作" class="headerlink" title="Stream流的中间操作"></a>Stream流的中间操作</h2><ol><li><p>筛选与切片</p><ul><li><p><code>filter(Predicate p)</code></p><p>从流中排除某些元素，即满足Predicate函数的元素</p></li><li><p><code>limit(n)</code></p><p>截断流，使其元素不超过给定的元素，即筛选出来的元素不超过n</p></li><li><p><code>skip(n)</code></p><p>跳过元素，返回一个除了前n个数据的流，若数据源不足n个，则返回空流</p></li><li><p><code>distinct()</code></p><p>通过流生成元素的hashCode和equals方法去除重复的元素</p></li></ul></li><li><p>映射</p><ul><li><p><code>map(Function f)</code></p><p>接收一个函数作为参数，将元素转换成其他形式或提取信息</p></li><li><p><code>flatMap(Function f)</code></p><p>将一个函数作为参数，将流中的每个值都换成另一个流，然后把所有流连接成一个流</p></li></ul></li><li><p>排序</p><ul><li><p><code>sorted()</code></p><p>没有参数，自然排序，默认从小到大的排序,如果操作的元素是类，就会抛出异常，需要该类实现Comparable接口</p></li><li><p><code>sorted(Comparator com)</code></p><p>一个Comparator实例，定制排序</p></li></ul></li></ol><h2 id="Stream流的终止操作"><a href="#Stream流的终止操作" class="headerlink" title="Stream流的终止操作"></a>Stream流的终止操作</h2><ol><li><p>匹配与查找</p><ul><li><p><code>boolean allMatch(Predicate p)</code></p><p>检查是否所有元素是否匹配该断定函数接口</p></li><li><p><code>boolean anyMatch(Predicate p)</code>  </p><p>检查是否至少有一个元素匹配该断定函数接口</p></li><li><p><code>boolean noneMatch(Predicate p)</code> </p><p>检查是否没有元素匹配该断定函数接口</p></li><li><p><code>findFirst()</code></p><p>返回第一个元素</p></li><li><p><code>findAny()</code></p><p>返回任意一个元素</p></li><li><p><code>count()</code></p><p>返回流中的总个数</p></li><li><p><code>max(Comparator c)</code></p><p>返回流中的最大值</p></li><li><p><code>min(Comparator c)</code></p><p>返回流中的最小值</p></li><li><p><code>forEach(Consumer c)</code></p><p>内部迭代</p></li></ul></li><li><p>归约</p><ul><li><p><code>reduce(T identity, BinaryOperator&lt;T&gt; bin)</code></p><p>可以将流中元素反复结合起来得到一个值，返回这个T类型的值</p><p>比如这个求和的例子<code>list.stream.reduce(0, Integer::sum);</code></p></li><li><p><code>reduce(BinaryOperator&lt;T&gt; bin)</code></p><p>可以将流中元素反复结合起来得到一个值，返回这个<code>Optional&lt;T&gt;</code>类型的值</p></li></ul></li><li><p>收集</p></li></ol><p><code>collect(Collector c)</code></p><p>将流转换为其他形式的数据源,调用这个Collector接口的方法得到新建数据源</p><p>一般使用实现了接口的Collectors的类的方法</p><p>常用方法为</p><ul><li><p><code>List&lt;T&gt; toList()</code> </p><p>将元素收集到创建的List</p></li><li><p><code>Set&lt;t&gt; toSet()</code></p><p>将元素收集到创建的Set</p></li><li><p><code>Collection&lt;T&gt; toCollection()</code></p><p>将元素收集到创建的Collection</p></li><li><p><code>Long counting()</code></p><p>计算流中的个数</p></li><li><p><code>Integer summingInt()</code></p><p>对流中元素的整数属性求和</p></li><li><p><code>Double averagingInt()</code></p><p>求流中的元素Integer属性的平均值</p></li></ul><h1 id="Optional容器类"><a href="#Optional容器类" class="headerlink" title="Optional容器类"></a>Optional容器类</h1><p><code>Optional&lt;T&gt;</code>类是一个容器类，可以报错类型T的值，代表这个值存在。</p><p>或者仅仅保存null,表示这个值不存在，可以避免空指针异常</p><p>如果值存在，则isPresent()方法返回true,调用get()方法会返回该对象</p><h2 id="创建Optional类对象的方法"><a href="#创建Optional类对象的方法" class="headerlink" title="创建Optional类对象的方法"></a>创建Optional类对象的方法</h2><ol><li><p><code>Optional.of(T t)</code></p><p>创建一个实例，t是非空的</p></li><li><p><code>Optional.isEmpty()</code></p><p>创建一个空的实例</p></li><li><p><code>Optional.ofNullable(T t)</code></p><p>创建一个实例，t是可以空的</p></li></ol><h2 id="判断Optional中是否包含对象"><a href="#判断Optional中是否包含对象" class="headerlink" title="判断Optional中是否包含对象"></a>判断Optional中是否包含对象</h2><ol><li><p><code>boolean isPresent()</code></p><p>判断是否包含对象</p></li><li><p><code>void ifPresent(Consumer&lt;? super T&gt; consumer)</code></p><p>如果有值就会执行Consumer接口的实现代码，并且会作为参数传给它</p></li></ol><h2 id="获取Optional容器的对象"><a href="#获取Optional容器的对象" class="headerlink" title="获取Optional容器的对象"></a>获取Optional容器的对象</h2><ol><li><p><code>T get()</code></p><p>如果调用对象包含值，则返回该值，否则抛异常</p></li><li><p><code>T orElse(T other)</code></p><p>如果有值则返回，没有返回指定的other值</p></li><li><p><code>T orElseGet(Supplier&lt;? extends T&gt; other)</code></p><p>如果有值就将其返回，否则返回由Supplier接口实现提供的对象</p></li><li><p><code>T orElseThrow(Supplier&lt;? extends X&gt; exceptionSupplier)</code></p><p>如果有值就将其返回，否则抛出由Supplier接口实现提供的异常</p></li></ol>]]></content:encoded>
            
            
            <category domain="https://yww52.com/categories/%E6%88%91%E7%9A%84%E6%8A%80%E6%9C%AF%E5%AD%A6%E4%B9%A0%E7%89%A9%E8%AF%AD%E6%9E%9C%E7%84%B6%E6%9C%89%E9%97%AE%E9%A2%98/">我的技术学习物语果然有问题</category>
            
            <category domain="https://yww52.com/categories/%E6%88%91%E7%9A%84%E6%8A%80%E6%9C%AF%E5%AD%A6%E4%B9%A0%E7%89%A9%E8%AF%AD%E6%9E%9C%E7%84%B6%E6%9C%89%E9%97%AE%E9%A2%98/Java%E5%9F%BA%E7%A1%80/">Java基础</category>
            
            
            
            <comments>https://yww52.com/posts/8a8aa27b/#disqus_thread</comments>
            
        </item>
        
        <item>
            <title>异常处理</title>
            <link>https://yww52.com/posts/28877bf/</link>
            <guid>https://yww52.com/posts/28877bf/</guid>
            <pubDate>Sat, 09 Oct 2021 16:00:00 GMT</pubDate>
            
            <description>常用的全局异常处理。</description>
            
            
            
            <content:encoded><![CDATA[<img src="https://img.yww52.com/2021/10/2021-10-10top_img.jpg" alt="异常处理" style="max-width:100%;height:auto;margin-bottom:15px;"
  /><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="comment">/**</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@author</span> yww</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@since</span> 2021/10/9</span></span><br><span class="line"><span class="comment"> **/</span></span><br><span class="line"><span class="meta">@Getter</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_">GlobalException</span> <span class="keyword">extends</span> <span class="title class_">RuntimeException</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">private</span> <span class="type">Integer</span> <span class="variable">code</span> <span class="operator">=</span> FAILED.getCode();</span><br><span class="line"></span><br><span class="line">    <span class="comment">/**</span></span><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> <span class="keyword">final</span> String message;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">public</span> <span class="title function_">GlobalException</span><span class="params">(String message)</span> &#123;</span><br><span class="line">        <span class="built_in">this</span>.message = message;</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_">GlobalException</span><span class="params">(ResultCode resultCode)</span> &#123;</span><br><span class="line">        <span class="built_in">this</span>.code = resultCode.getCode();</span><br><span class="line">        <span class="built_in">this</span>.message = resultCode.getMessage();</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><blockquote><p>这里需要注意的是，自定义异常需要继承RuntimeException（运行时异常）。</p></blockquote><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><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><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"> *  &lt;p&gt;</span></span><br><span class="line"><span class="comment"> *     全局异常处理</span></span><br><span class="line"><span class="comment"> *  &lt;/p&gt;</span></span><br><span class="line"><span class="comment"> *</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@author</span> yww</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@since</span> 2021/10/10</span></span><br><span class="line"><span class="comment"> **/</span></span><br><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_">ControllerAdviceHandler</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">     * 统一处理GlobalException异常，异常处理顺序是从小异常到大异常。</span></span><br><span class="line"><span class="comment">     *</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@param</span> e 服务异常</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(value = GlobalException.class)</span></span><br><span class="line">    <span class="keyword">public</span> &lt;T&gt; Result&lt;T&gt; <span class="title function_">globalExceptionHandler</span><span class="params">(GlobalException e, HttpServletRequest request)</span> &#123;</span><br><span class="line">        log.error(<span class="string">&quot;&gt;&gt; global exception: &#123;&#125;, &#123;&#125;, &#123;&#125;&quot;</span>, request.getRequestURI(), e.getCode(), e.getMessage());</span><br><span class="line">        <span class="type">String</span> <span class="variable">errMessage</span> <span class="operator">=</span> e.getMessage();</span><br><span class="line">        <span class="comment">// 防止空的错误信息</span></span><br><span class="line">        <span class="keyword">if</span> (StringUtils.isBlank(errMessage)) &#123;</span><br><span class="line">            errMessage = <span class="string">&quot;服务器繁忙&quot;</span>;</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">return</span> Result.failure(e.getCode(), errMessage);</span><br><span 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> e 服务异常</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(value = Exception.class)</span></span><br><span class="line">    <span class="keyword">public</span> &lt;T&gt; Result&lt;T&gt; <span class="title function_">defaultErrorHandler</span><span class="params">(Exception e, HttpServletRequest request)</span> &#123;</span><br><span class="line">        log.error(<span class="string">&quot;&gt;&gt; 服务器内部错误 &quot;</span> + request.getRequestURI(), e.getMessage());</span><br><span class="line">        <span class="keyword">return</span> Result.failure(<span class="number">500</span>, <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><blockquote><p>@RestControllerAdvice注解其实是@ControllerAdvice和@ResponseBody的合并。</p><p>@ControllerAdvice通常配合@ExceptionHandler来捕抓异常信息。</p><p>@ControllerAdvice可以捕抓到系统抛出的异常，然后使用@ExceptionHandler匹配具体处理异常信息。</p></blockquote><h1 id="特殊的异常处理"><a href="#特殊的异常处理" class="headerlink" title="特殊的异常处理"></a>特殊的异常处理</h1><p>有些异常是不会走全局异常处理的，即不会被捕捉到，比如在过滤器中的异常，这种异常是没有经过<code>controller</code>的，所以发生了异常，也不会通过被上述的方式捕捉到。</p><p>以下是一个捕捉Token过滤器异常的例子。</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * &lt;p&gt;</span></span><br><span class="line"><span class="comment"> *      Token身份验证过滤器</span></span><br><span class="line"><span class="comment"> * &lt;/p&gt;</span></span><br><span class="line"><span class="comment"> *</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@author</span> yww</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@since</span> 2022/10/20</span></span><br><span class="line"><span class="comment"> */</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_">TokenAuthenticationFilter</span> <span class="keyword">extends</span> <span class="title class_">BasicAuthenticationFilter</span> &#123;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="keyword">protected</span> <span class="keyword">void</span> <span class="title function_">doFilterInternal</span><span class="params">(HttpServletRequest request, HttpServletResponse response, FilterChain chain)</span> &#123;</span><br><span class="line">        <span class="keyword">try</span> &#123;</span><br><span class="line">            .....</span><br><span class="line">            <span class="comment">// 处理Token验证的异常</span></span><br><span class="line">        &#125; <span class="keyword">catch</span> (AlgorithmMismatchException | SignatureVerificationException | TokenExpiredException</span><br><span class="line">                 | MissingClaimException | IncorrectClaimException e) &#123;</span><br><span class="line">            errorHandler(request, response, 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="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> e        异常</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_">errorHandler</span><span class="params">(HttpServletRequest request, HttpServletResponse response, Exception e)</span> &#123;</span><br><span class="line">        <span class="keyword">if</span> (e <span class="keyword">instanceof</span> AlgorithmMismatchException) &#123;</span><br><span class="line">            resolver.resolveException(request, response, <span class="literal">null</span>, <span class="keyword">new</span> <span class="title class_">AlgorithmMismatchException</span>(e.getMessage()));</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> (e <span class="keyword">instanceof</span> TokenExpiredException) &#123;</span><br><span class="line">            resolver.resolveException(request, response, <span class="literal">null</span>, <span class="keyword">new</span> <span class="title class_">TokenExpiredException</span>(</span><br><span class="line">                    e.getMessage(),</span><br><span class="line">                    ((TokenExpiredException) e).getExpiredOn())</span><br><span class="line">            );</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> (e <span class="keyword">instanceof</span> MissingClaimException) &#123;</span><br><span class="line">            resolver.resolveException(request, response, <span class="literal">null</span>, <span class="keyword">new</span> <span class="title class_">MissingClaimException</span>(</span><br><span class="line">                    ((MissingClaimException) e).getClaimName())</span><br><span class="line">            );</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> (e <span class="keyword">instanceof</span> IncorrectClaimException) &#123;</span><br><span class="line">            resolver.resolveException(request, response, <span class="literal">null</span>, <span class="keyword">new</span> <span class="title class_">IncorrectClaimException</span>(</span><br><span class="line">                    e.getMessage(),</span><br><span class="line">                    ((IncorrectClaimException) e).getClaimName(),</span><br><span class="line">                    ((IncorrectClaimException) e).getClaimValue())</span><br><span class="line">            );</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> (e <span class="keyword">instanceof</span> JWTVerificationException) &#123;</span><br><span class="line">            resolver.resolveException(request, response, <span class="literal">null</span>, <span class="keyword">new</span> <span class="title class_">JWTVerificationException</span>(</span><br><span class="line">                    e.getMessage(),</span><br><span class="line">                    e.getCause())</span><br><span class="line">            );</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>]]></content:encoded>
            
            
            <category domain="https://yww52.com/categories/%E6%88%91%E7%9A%84%E6%8A%80%E6%9C%AF%E5%AD%A6%E4%B9%A0%E7%89%A9%E8%AF%AD%E6%9E%9C%E7%84%B6%E6%9C%89%E9%97%AE%E9%A2%98/">我的技术学习物语果然有问题</category>
            
            <category domain="https://yww52.com/categories/%E6%88%91%E7%9A%84%E6%8A%80%E6%9C%AF%E5%AD%A6%E4%B9%A0%E7%89%A9%E8%AF%AD%E6%9E%9C%E7%84%B6%E6%9C%89%E9%97%AE%E9%A2%98/Spring/">Spring</category>
            
            
            
            <comments>https://yww52.com/posts/28877bf/#disqus_thread</comments>
            
        </item>
        
        <item>
            <title>分布式session的解决方案</title>
            <link>https://yww52.com/posts/86ff1dad/</link>
            <guid>https://yww52.com/posts/86ff1dad/</guid>
            <pubDate>Wed, 09 Jun 2021 16:00:00 GMT</pubDate>
            
            <description>Session主要的目的是会话控制，但是在分布式环境下，Session就会出现一些问题，接下来就来探讨一下会出现的问题以及解决方案。</description>
            
            
            
            <content:encoded><![CDATA[<img src="https://img.yww52.com/2021/6/2021-6-10/top_img.jpg" alt="分布式session的解决方案" style="max-width:100%;height:auto;margin-bottom:15px;"
  /><h1 id="session"><a href="#session" class="headerlink" title="session"></a>session</h1><blockquote><p> Session，在计算机领域，被称为“会话控制”。</p></blockquote><p>会话控制，这就提出了两个问题</p><ol><li>什么是会话</li><li>为什么要控制会话</li><li>怎样控制会话</li></ol><h2 id="什么是会话"><a href="#什么是会话" class="headerlink" title="什么是会话"></a>什么是会话</h2><p>会话按字意来看就是进行对话，其实真正含义就差不多是这个意思。</p><blockquote><p>在计算机术语中，会话是指一个终端用户与交互系统进行通讯的过程，比如从输入账户密码进入网上的一个系统到退出系统就是一个会话过程。</p></blockquote><p>也可以理解为两个计算机之间的对话，这就是会话。</p><p>一个计算机的网络世界就像是被集体孤立的个人，虽然不能说孤立的个体是毫无作用的，但是不融入集体的个体，是不能发挥出个体的真正意义的，所以每个个体的交流是很重要的。所以在网络世界中，会话也是很重要的。</p><h2 id="为什么要控制会话"><a href="#为什么要控制会话" class="headerlink" title="为什么要控制会话"></a>为什么要控制会话</h2><p>会话大部分都是建立在HTTP的基础上的，但是HTTP是一个无状态的协议，于是就出现了一个很重要的问题，会话这么多，服务端该怎么区分这些会话是哪个用户的？所以就需要将这些会话进行控制区分。</p><h2 id="怎样控制会话"><a href="#怎样控制会话" class="headerlink" title="怎样控制会话"></a>怎样控制会话</h2><p>服务端需要某种机制来将这些会话进行区分，这个机制就是session。</p><p>用户通过HTTP与服务端进行会话，服务端为每一个用户生成特定的sessionId来标记该用户，之后每个会话携带着特定sessionId，服务端就能确定该会话属于哪一个用户的了。</p><p>控制会话的大致思路就是这样了。</p><h1 id="分布式环境下的session"><a href="#分布式环境下的session" class="headerlink" title="分布式环境下的session"></a>分布式环境下的session</h1><p>在上述对session的描述中，会发现控制会话的方案是很好的。但是这仅限于单体架构，服务端只有一个服务器在提供服务，该服务存储着所有的sessionId，通过该服务器可以识别出所有被记录的用户。</p><p><img src="https://img.yww52.com/2021/6/2021-6-10/img1.png" alt=""></p><p>但是在如今这个大流量的时代，大多时候，单个服务器是不能承担大规模的请求，所以就出现了集群方案，将单个服务器拓展为多个服务器提供服务，减轻单个服务器的压力。</p><p><img src="https://img.yww52.com/2021/6/2021-6-10/img2.png" alt=""></p><p>但是从这个图中就会发现一个问题。</p><p>假设客户端A与服务端进行了一次会话，第一个请求发送到了服务器A，服务器A为客户端的这次会话生成了一个sessionId，但是客户端A的请求是有可能发送到服务器B的，但是此时的服务器B是没有记录这次会话的，所以也就是没有状态的，这违背了session的初心。其根本原因就在于session是没有共享。</p><p>接下来就探讨一下在分布式环境下的session怎样实现共享。</p><h1 id="客户端存储"><a href="#客户端存储" class="headerlink" title="客户端存储"></a>客户端存储</h1><p>在分布式环境下，一个客户端的请求可能散落在不同的服务器上，但是我们可以发现客户端是唯一，所以将session直接存储在客户端不就可以实现session的共享了吗？这的确是一个方法，一个会话一定是会在同个客户端，这样就实现了session的共享。</p><p>那该怎么存储在客户端上呢？</p><p>可以借助浏览器的cookie来进行session信息的存储。在客户端向服务端第一次发送请求的时候，服务端生成一个特定的sessionId返回给客户端，浏览器收到了session信息之后，就将该信息存储到cookie中，每次发送请求都带上session信息，服务端就能识别出该会话。在这里服务端只提供生成session和识别seesion的服务，而不存储session信息。</p><p><img src="https://img.yww52.com/2021/6/2021-6-10/img3.png" alt=""></p><div class="note warning flat"><p>缺点: session存储在cookie中，安全隐患很大,很容易被盗取。另一方面，cookie对于数据类型及数据大小存在限制。</p></div><h1 id="session复制"><a href="#session复制" class="headerlink" title="session复制"></a>session复制</h1><p>第一种情况，在客户端存储，这种方案已经思考过了，那么接下来可以该在服务端想想方法了。</p><p>于是就出现了session复制这种方案。</p><p>将服务器A的sessionA给服务器B，服务器B的sessionB给服务器A，这样不就实现了session的共享了吗。</p><p>大致思路就是这样，在特定时间，服务器就会去复制其他服务器的session信息或者将自己的信息发送给其他服务器，从而实现session信息同步。</p><p>像tomcat等一些web容器就支持session的复制，在同一个局域网内发送该容器的session信息给其他容器。</p><p><img src="https://img.yww52.com/2021/6/2021-6-10/img4.png" alt=""></p><div class="note warning flat"><p>缺点: session需要消耗一定的资源，当集群有点规模，消耗的资源也是不可小觑。</p></div><h1 id="绑定session"><a href="#绑定session" class="headerlink" title="绑定session"></a>绑定session</h1><p>第一种方法是在客户端，第二种方法是在服务端，接下来就可以考虑请求转发上想方法。</p><p>我们只要将同一个客户端的请求都转发到同一个服务器上不就好了吗。</p><p>这种想法也是可以行的通的。Nginx也提供了对应的方法。使用Nginx进行负载均衡，可以选择它的ip_hash的方案，将同一个IP的请求转发到相同的服务器，这样该次会话都只会在一个服务器上进行，宏观上就像是单体架构下使用的session。</p><p><img src="https://img.yww52.com/2021/6/2021-6-10/img5.png" alt=""></p><div class="note warning flat"><p>缺点: 降低了集群的可用性，若是单个服务器出现故障，那么存储的session的信息丢失，会话就会出现故障。</p></div><h1 id="基于外部存储"><a href="#基于外部存储" class="headerlink" title="基于外部存储"></a>基于外部存储</h1><p>前三种方法在客户端，服务端，和负载均衡下思考的方案，除了这三者之外怎么进行session共享呢？</p><p>在计算机领域，没有什么问题是加一层不能解决的。</p><p>所以我们可以考虑给系统增加一个数据存储层，然后将seesion存储在该数据存储层上就好了，从而实现session的共享。</p><div class="note info flat"><p>事实上，每一个系统都会存在数据存储层的，只是上述解决思路没有将该数据存储层表示出来。没有表示出来的原因只是因为在session的机制中没有涉及到存储层，所以没有提及。</p></div><p>这里就提供一个基于Redis的sessioon存储方案。（只要是提供存储数据的都是可以实现的）</p><p><img src="https://img.yww52.com/2021/6/2021-6-10/img6.png" alt=""></p><div class="note success flat"><p>这种方法除了降低整个系统的可用性就没有什么很明显的缺点，系统可用性也是可以通过搭建Redis集群来进行提高。其实上述的绑定session的方案和session的方案，总体也是会降低整个系统的可用性，所以就不探讨该缺点。所以这种方案也是最经常使用的方案了。</p></div>]]></content:encoded>
            
            
            <category domain="https://yww52.com/categories/%E6%88%91%E7%9A%84%E6%8A%80%E6%9C%AF%E5%AD%A6%E4%B9%A0%E7%89%A9%E8%AF%AD%E6%9E%9C%E7%84%B6%E6%9C%89%E9%97%AE%E9%A2%98/">我的技术学习物语果然有问题</category>
            
            <category domain="https://yww52.com/categories/%E6%88%91%E7%9A%84%E6%8A%80%E6%9C%AF%E5%AD%A6%E4%B9%A0%E7%89%A9%E8%AF%AD%E6%9E%9C%E7%84%B6%E6%9C%89%E9%97%AE%E9%A2%98/%E5%88%86%E5%B8%83%E5%BC%8F/">分布式</category>
            
            
            
            <comments>https://yww52.com/posts/86ff1dad/#disqus_thread</comments>
            
        </item>
        
        <item>
            <title>MySQL必知必会</title>
            <link>https://yww52.com/posts/df083c4d/</link>
            <guid>https://yww52.com/posts/df083c4d/</guid>
            <pubDate>Thu, 03 Jun 2021 16:00:00 GMT</pubDate>
            
            <description>阅读MySQL必知必会的笔记，并记录一下书中的SQL语句，用于加深一下印象。</description>
            
            
            
            <content:encoded><![CDATA[<img src="https://img.yww52.com/2021/6/2021-6-4top_img.jpg" alt="MySQL必知必会" style="max-width:100%;height:auto;margin-bottom:15px;"
  /><h1 id="了解数据库和表"><a href="#了解数据库和表" class="headerlink" title="了解数据库和表"></a>了解数据库和表</h1><h2 id="连接"><a href="#连接" class="headerlink" title="连接"></a>连接</h2><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">mysql -u root -p</span><br></pre></td></tr></table></figure><h2 id="选择数据库"><a href="#选择数据库" class="headerlink" title="选择数据库"></a>选择数据库</h2><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">USE crashcourse;</span><br></pre></td></tr></table></figure><h2 id="了解数据库和表-1"><a href="#了解数据库和表-1" class="headerlink" title="了解数据库和表"></a>了解数据库和表</h2><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"># 显示所有数据库</span><br><span class="line">SHOW DATABASES;</span><br><span class="line"># 显示一个数据库中的所有表</span><br><span class="line">SHOW TABLES;</span><br><span class="line"># 显示表中数据的列</span><br><span class="line">SHOW COLUMNS FROM customers;</span><br><span class="line">DESCRIBE customers;</span><br><span class="line">DESC cutomers;</span><br></pre></td></tr></table></figure><h2 id="其他一些语句"><a href="#其他一些语句" class="headerlink" title="其他一些语句"></a>其他一些语句</h2><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></pre></td><td class="code"><pre><span class="line"># 用于显示服务器状态信息</span><br><span class="line">SHOW STATUS;</span><br><span class="line"># 显示创建特定数据库表MySQL语句</span><br><span class="line">SHOW CREATE DATABASE crashcourse;</span><br><span class="line"># 显示创建特定表MySQL语句</span><br><span class="line">SHOW CREATE TABLE customers;</span><br><span class="line"># 用来显示授予用户的安全权限</span><br><span class="line">SHOW GRANTS;</span><br><span class="line"># 显示服务器错误或警告信息</span><br><span class="line">SHOW ERRORS;</span><br><span class="line">SHOW WARNINGS;</span><br></pre></td></tr></table></figure><h1 id="检索数据"><a href="#检索数据" class="headerlink" title="检索数据"></a>检索数据</h1><h2 id="检索单个列"><a href="#检索单个列" class="headerlink" title="检索单个列"></a>检索单个列</h2><figure class="highlight plaintext"><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"># 从表products中检索一个名为prod_name的列</span><br><span class="line">SELECT prod_name FROM products;</span><br></pre></td></tr></table></figure><h2 id="检索多个列"><a href="#检索多个列" class="headerlink" title="检索多个列"></a>检索多个列</h2><figure class="highlight plaintext"><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"># 从表products中检索名为prod_id, prod_name, prod_price的列</span><br><span class="line">SELECT prod_id, prod_name, prod_price FROM products;</span><br></pre></td></tr></table></figure><h2 id="检索所有列"><a href="#检索所有列" class="headerlink" title="检索所有列"></a>检索所有列</h2><figure class="highlight plaintext"><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">SELECT * FROM products;</span><br></pre></td></tr></table></figure><blockquote><p>最好不要使用通配符，检索不需要的列会江都检索和应用程序的性能。</p></blockquote><h2 id="检索不同的行"><a href="#检索不同的行" class="headerlink" title="检索不同的行"></a>检索不同的行</h2><figure class="highlight plaintext"><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"># 使用DISTINCT关键字，会返回不同的行</span><br><span class="line">SELECT DISTINCT vend_id FROM products;</span><br></pre></td></tr></table></figure><blockquote><p>​    DISTINCT必须放在列名的前面</p><p>​    不能部分使用DISTINCT，这个关键字应用于所有列而不仅仅是前置它的列，所以除非指定的列都相同，否则所有行都将被检索出来。</p></blockquote><h2 id="限制结果"><a href="#限制结果" class="headerlink" title="限制结果"></a>限制结果</h2><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"># 返回前5行数据</span><br><span class="line">SELECT prod_name FROM products LIMIT 5;</span><br><span class="line"># 从第5行开始返回接下来5行的数据</span><br><span class="line">SELECT prod_name FROM products LIMIT 5, 5;</span><br><span class="line">SELECT prod_name FROM products LIMIT 5 OFFSET 5;</span><br></pre></td></tr></table></figure><blockquote><p>检索出来的行是从0开始，所以LIMIT 5 表示从第0行开始接下来的五行</p></blockquote><h2 id="使用完全限定的表名"><a href="#使用完全限定的表名" class="headerlink" title="使用完全限定的表名"></a>使用完全限定的表名</h2><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">SELECT products.prod_name FROM products;</span><br></pre></td></tr></table></figure><h1 id="排序检索数据"><a href="#排序检索数据" class="headerlink" title="排序检索数据"></a>排序检索数据</h1><h2 id="排序数据"><a href="#排序数据" class="headerlink" title="排序数据"></a>排序数据</h2><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></pre></td><td class="code"><pre><span class="line"># 使用ORDER BY子句取一个或多个列名字(使用未检索的列也是合法的)，对输出进行排序</span><br><span class="line"># 以prod_name列的字母顺序排序数据</span><br><span class="line">SELECT prod_name FROM products ORDER BY prod_name;</span><br></pre></td></tr></table></figure><h2 id="按多个列排序"><a href="#按多个列排序" class="headerlink" title="按多个列排序"></a>按多个列排序</h2><figure class="highlight plaintext"><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">SELECT prod_id, prod_price, prod_name FROM products ORDER BY prod_price, prod_name;</span><br></pre></td></tr></table></figure><blockquote><p>ORDER BY 子句应该保证在FROM子句之后，LIMIT子句要在ORDER BY 子句之后。</p></blockquote><h2 id="指定排序顺序"><a href="#指定排序顺序" class="headerlink" title="指定排序顺序"></a>指定排序顺序</h2><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></pre></td><td class="code"><pre><span class="line"># 以价格降序进行排序</span><br><span class="line">SELECT prod_id, prod_price, prod_name FROM products ORDER BY prod_price DESC;</span><br><span class="line"># 以价格升序序进行排序</span><br><span class="line">SELECT prod_id, prod_price, prod_name FROM products ORDER BY prod_price ASC;</span><br><span class="line"># 先以价格降序，再以产品名排序</span><br><span class="line">SELECT prod_id, prod_price, prod_name FROM products ORDER BY prod_price DESC, prod_name;</span><br></pre></td></tr></table></figure><blockquote><p>如果想在多个列上进行降序排序，必须每一个列都要指定DESC关键字</p><p>ASC关键字其实没多大用，因为默认就是升序排序的，不指定DESC就一般是ASC排序</p></blockquote><h1 id="过滤数据"><a href="#过滤数据" class="headerlink" title="过滤数据"></a>过滤数据</h1><h2 id="使用WHERE子句"><a href="#使用WHERE子句" class="headerlink" title="使用WHERE子句"></a>使用WHERE子句</h2><figure class="highlight plaintext"><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"># 使用where子句指定搜索条件</span><br><span class="line">SELECT prod_name, prod_price FROM products WHERE prod_price = 2.5;</span><br></pre></td></tr></table></figure><blockquote><p>WHERE子句的位置应该在ORDER BY子句之前。</p></blockquote><h2 id="WHERE子句操作符"><a href="#WHERE子句操作符" class="headerlink" title="WHERE子句操作符"></a>WHERE子句操作符</h2><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">=</td><td style="text-align:center">等于</td></tr><tr><td style="text-align:center">&lt;&gt;</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">&lt;</td><td style="text-align:center">小于</td></tr><tr><td style="text-align:center">&lt;=</td><td style="text-align:center">小于等于</td></tr><tr><td style="text-align:center">&gt;</td><td style="text-align:center">大于</td></tr><tr><td style="text-align:center">&gt;=</td><td style="text-align:center">大于等于</td></tr><tr><td style="text-align:center">BETWEEN</td><td style="text-align:center">在指定两个值之间</td></tr></tbody></table></div><h3 id="检查单个值"><a href="#检查单个值" class="headerlink" title="检查单个值"></a>检查单个值</h3><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"># 执行匹配时默认不区分大小写</span><br><span class="line">SELECT prod_name, prod_price FROM products WHERE prod_name = &#x27;fuses&#x27;;</span><br><span class="line">SELECT prod_name, prod_price FROM products WHERE prod_price &lt; 10;</span><br></pre></td></tr></table></figure><h3 id="不匹配检查"><a href="#不匹配检查" class="headerlink" title="不匹配检查"></a>不匹配检查</h3><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"># 不是由供应商1003制造的产品</span><br><span class="line">SELECT vend_id, prod_name FROM products WHERE vend_id &lt;&gt; 1003;</span><br><span class="line">SELECT vend_id, prod_name FROM products WHERE vend_id != 1003;</span><br></pre></td></tr></table></figure><blockquote><p>单引号用来限定字符串。如果将值与串类型的列进行比较，则需要限定引号。用来与数值列进行比较的值不用引号。</p></blockquote><h3 id="范围值检查"><a href="#范围值检查" class="headerlink" title="范围值检查"></a>范围值检查</h3><figure class="highlight plaintext"><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"># 检索价格在5美元和10美元之间的产品</span><br><span class="line">SELECT prod_name, prod_price FROM products WHERE prod_price BETWEEN 5 AND 10;</span><br></pre></td></tr></table></figure><h3 id="空值检查"><a href="#空值检查" class="headerlink" title="空值检查"></a>空值检查</h3><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"># 查询价格是空值的产品,空值检查</span><br><span class="line">SELECT prod_name FROM products WHERE prod_price IS NULL;</span><br><span class="line"># 非空值检查</span><br><span class="line">SELECT prod_name FROM products WHERE prod_price IS NOT NULL;</span><br></pre></td></tr></table></figure><blockquote><p>NULL 无值，她与字段包括0，空字符串或仅仅包括空格不同。</p></blockquote><h1 id="数据过滤"><a href="#数据过滤" class="headerlink" title="数据过滤"></a>数据过滤</h1><h2 id="组合WHERE子句"><a href="#组合WHERE子句" class="headerlink" title="组合WHERE子句"></a>组合WHERE子句</h2><blockquote><p>用来联结或改变WHERE子句的子句的关键字，被称为逻辑操作符或是操作符。</p></blockquote><h3 id="AND操作符"><a href="#AND操作符" class="headerlink" title="AND操作符"></a>AND操作符</h3><figure class="highlight plaintext"><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"># AND操作符给WHERE子句附加条件</span><br><span class="line">SELECT prod_id, prod_price, prod_name FROM products WHERE vend_id = 1003 AND prod_price &lt;= 10;</span><br></pre></td></tr></table></figure><blockquote><p>AND    用在WHERE子句的关键字，用来指示检索满足所有给定条件的行。</p></blockquote><h3 id="OR操作符"><a href="#OR操作符" class="headerlink" title="OR操作符"></a>OR操作符</h3><figure class="highlight plaintext"><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"># OR操作符指示MySQL检索匹配任意条件的行</span><br><span class="line">SELECT prod_name, prod_price FROM products WHERE vend_id = 1002 OR vend_id = 1003;</span><br></pre></td></tr></table></figure><blockquote><p>OR    WHERE子句中使用的关键字，用来表示检索匹配任一给定条件的行。</p></blockquote><h3 id="计算次序"><a href="#计算次序" class="headerlink" title="计算次序"></a>计算次序</h3><blockquote><p>WHERE可包括AND和OR的组合子句条件，但注意的是，MYSQL会优先处理AND的条件然后在处理OR条件。</p></blockquote><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></pre></td><td class="code"><pre><span class="line"># 先处理AND在处理OR，所以表示的是vend_id等于1003并且价格大于等于10的产品或者是vend_id等于1002价格没限制的产品</span><br><span class="line">SELECT prod_name, prod_price FROM products </span><br><span class="line">WHERE vend_id = 1002 OR vend_id = 1003 AND prod_price &gt;= 10;</span><br><span class="line"># 正确顺序的话需要添加括号进行区分</span><br><span class="line">SELECT prod_name, prod_price FROM products </span><br><span class="line">WHERE (vend_id = 1002 OR vend_id = 1003) AND prod_price &gt;= 10;</span><br></pre></td></tr></table></figure><blockquote><p>任何时候使用具有AND和OR操作符的WHERE子句，都应该使用圆括号明确地分组操作符。</p></blockquote><h2 id="IN操作符"><a href="#IN操作符" class="headerlink" title="IN操作符"></a>IN操作符</h2><blockquote><p>IN操作符用来指定条件返回，范围中地每个条件都可以进行匹配。</p></blockquote><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></pre></td><td class="code"><pre><span class="line"># IN取合法值</span><br><span class="line">SELECT prod_name, prod_price FROM products WHERE vend_id IN (1002,1003)</span><br><span class="line">ORDER BY prod_name;p</span><br><span class="line"># 等价于</span><br><span class="line">SELECT prod_name, prod_price FROM products WHERE vend_id = 1002 OR vend_id = 1003</span><br><span class="line">ORDER BY prod_name;</span><br></pre></td></tr></table></figure><h2 id="NOT操作符"><a href="#NOT操作符" class="headerlink" title="NOT操作符"></a>NOT操作符</h2><blockquote><p>NOT    WHERE子句中用来否定后跟条件地关键字。</p></blockquote><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">SELECT prod_name, prod_price</span><br><span class="line">FROM products</span><br><span class="line">WHERE vend_id NOT IN (1002,1003)</span><br><span class="line">ORDER BY prod_name;</span><br></pre></td></tr></table></figure><h1 id="用通配符进行过滤"><a href="#用通配符进行过滤" class="headerlink" title="用通配符进行过滤"></a>用通配符进行过滤</h1><h2 id="LIKE操作符"><a href="#LIKE操作符" class="headerlink" title="LIKE操作符"></a>LIKE操作符</h2><h3 id="百分号通配符"><a href="#百分号通配符" class="headerlink" title="百分号通配符"></a>百分号通配符</h3><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"># %通配符表示任何字符出现任意次数</span><br><span class="line">SELECT prod_id,prod_name </span><br><span class="line">FROM products WHERE prod_name LIKE &#x27;jet%&#x27;;</span><br></pre></td></tr></table></figure><blockquote><p>WHERE prod_name LIKE ‘%’也是不能匹配NULL值得，需要注意。</p></blockquote><h3 id="下划线通配符"><a href="#下划线通配符" class="headerlink" title="下划线通配符"></a>下划线通配符</h3><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"># _下划线只匹配一个字符,不能多也不能少</span><br><span class="line">SELECT prod_id, prod_name </span><br><span class="line">FROM products</span><br><span class="line">WHERE prod_name LIKE &#x27;_ton anvil&#x27;;</span><br></pre></td></tr></table></figure>]]></content:encoded>
            
            
            <category domain="https://yww52.com/categories/%E8%AF%BB%E4%B9%A6%E7%AC%94%E8%AE%B0/">读书笔记</category>
            
            
            
            <comments>https://yww52.com/posts/df083c4d/#disqus_thread</comments>
            
        </item>
        
        <item>
            <title>漫画算法</title>
            <link>https://yww52.com/posts/998d504/</link>
            <guid>https://yww52.com/posts/998d504/</guid>
            <pubDate>Wed, 02 Jun 2021 16:00:00 GMT</pubDate>
            
            <description>记录书中面试算法题的解法，主要是书中思路。</description>
            
            
            
            <content:encoded><![CDATA[<img src="https://img.yww52.com/2021/6/2021-6-3top_img.jpg" alt="漫画算法" style="max-width:100%;height:auto;margin-bottom:15px;"
  /><h2 id="如何判断链表有环"><a href="#如何判断链表有环" class="headerlink" title="如何判断链表有环"></a>如何判断链表有环</h2><blockquote><p>给定一个链表，判断链表中是否有环。</p></blockquote><h3 id="循环判断"><a href="#循环判断" class="headerlink" title="循环判断"></a>循环判断</h3><p>从头节点开始，依次遍历单链表中的每一个节点。没遍历一个新节点，就从头检查新节点之前的所有节点，查看是否相同。若存在相同节点证明链表是有环的。</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</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="type">boolean</span> <span class="title function_">hasCycle</span><span class="params">(ListNode head)</span> &#123;</span><br><span class="line">        <span class="keyword">if</span> (head == <span class="literal">null</span> || head.next == <span class="literal">null</span>) <span class="keyword">return</span> <span class="literal">false</span>;</span><br><span class="line">        <span class="type">int</span> <span class="variable">total</span> <span class="operator">=</span> <span class="number">2</span>;</span><br><span class="line">        <span class="type">ListNode</span> <span class="variable">cur</span> <span class="operator">=</span> head.next;</span><br><span class="line">        <span class="keyword">while</span> (cur != <span class="literal">null</span>) &#123;</span><br><span class="line">            <span class="type">int</span> <span class="variable">start</span> <span class="operator">=</span> <span class="number">1</span>;</span><br><span class="line">            <span class="type">ListNode</span> <span class="variable">node</span> <span class="operator">=</span> head;</span><br><span class="line">            <span class="keyword">while</span> (node != cur) &#123;</span><br><span class="line">                node = node.next;</span><br><span class="line">                start++;</span><br><span class="line">            &#125;</span><br><span class="line">            <span class="keyword">if</span> (start != total) <span class="keyword">return</span> <span class="literal">true</span>;</span><br><span class="line">            total++;</span><br><span class="line">            cur = cur.next;</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>该算法遍历一遍的同时，内部也在从头遍历一次，所以时间复杂度为O(n^2)</p><p>没有创造新的内存空间，空间复杂度为O(1)</p><h3 id="Set集合"><a href="#Set集合" class="headerlink" title="Set集合"></a>Set集合</h3><p>创建一个Set集合，遍历链表并将节点加入Set集合，当出现重复的元素，即加入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></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</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="type">boolean</span> <span class="title function_">hasCycle</span><span class="params">(ListNode head)</span> &#123;</span><br><span class="line">        <span class="keyword">if</span> (head == <span class="literal">null</span> || head.next == <span class="literal">null</span>) <span class="keyword">return</span> <span class="literal">false</span>;</span><br><span class="line">        Set&lt;ListNode&gt; set = <span class="keyword">new</span> <span class="title class_">HashSet</span>&lt;&gt;();</span><br><span class="line">        <span class="keyword">while</span> (head != <span class="literal">null</span>) &#123;</span><br><span class="line">            <span class="keyword">if</span> (!set.add(head)) <span class="keyword">return</span> <span class="literal">true</span>;</span><br><span class="line">            head = head.next;</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>该算法是遍历一遍链表，所以时间复杂度为O(n)</p><p>将所有节点加入Set集合存储，空间复杂度也为O(n)</p><h3 id="双指针"><a href="#双指针" class="headerlink" title="双指针"></a>双指针</h3><p>使用两个指针，一个每次前进两个，一个每次前进一个，若是出现两个指针指向一个节点，代表链表有环。因为快指针提前入环，总会追上慢指针。</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</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="type">boolean</span> <span class="title function_">hasCycle</span><span class="params">(ListNode head)</span> &#123;</span><br><span class="line">        <span class="keyword">if</span> (head == <span class="literal">null</span> || head.next == <span class="literal">null</span>) <span class="keyword">return</span> <span class="literal">false</span>;</span><br><span class="line">        <span class="type">ListNode</span> <span class="variable">fast</span> <span class="operator">=</span> head;</span><br><span class="line">        <span class="type">ListNode</span> <span class="variable">slow</span> <span class="operator">=</span> head;</span><br><span class="line">        <span class="keyword">while</span> (fast != <span class="literal">null</span> &amp;&amp; fast.next != <span class="literal">null</span>) &#123;</span><br><span class="line">            fast = fast.next.next;</span><br><span class="line">            slow = slow.next;</span><br><span class="line">            <span class="keyword">if</span> (fast == slow) <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>只用循环一次链表，所以时间复杂度为O(n)</p><p>不用创建额外的空间，所以空间复杂度为O(1)</p><h3 id="求出环的长度"><a href="#求出环的长度" class="headerlink" title="求出环的长度"></a>求出环的长度</h3><p>快慢节点重合的时候，在继续前进并记录前进的次数，下次重合，就是快指针（速度是慢指针的两倍）比慢指针多走了一圈，可以理解为前进的次数就是环的长度。</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <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_">getCycleLength</span><span class="params">(ListNode head)</span> &#123;</span><br><span class="line">        <span class="keyword">if</span> (head == <span class="literal">null</span> || head.next == <span class="literal">null</span>) <span class="keyword">return</span> <span class="literal">false</span>;</span><br><span class="line">        <span class="type">ListNode</span> <span class="variable">fast</span> <span class="operator">=</span> head;</span><br><span class="line">        <span class="type">ListNode</span> <span class="variable">slow</span> <span class="operator">=</span> head;</span><br><span class="line">        <span class="keyword">while</span> (fast != <span class="literal">null</span> &amp;&amp; fast.next != <span class="literal">null</span>) &#123;</span><br><span class="line">            fast = fast.next.next;</span><br><span class="line">            slow = slow.next;</span><br><span class="line">            <span class="keyword">if</span> (fast == slow) <span class="keyword">break</span>;</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="type">int</span> <span class="variable">length</span> <span class="operator">=</span> <span class="number">1</span>;</span><br><span class="line">        fast = fast.next.next;</span><br><span class="line">        slow = slow.next;</span><br><span class="line">        <span class="keyword">while</span> (fast != slow) &#123;</span><br><span class="line">fast = fast.next.next;</span><br><span class="line">            slow = slow.next;</span><br><span class="line">            length++;</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">return</span> length;</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>假设链表头节点到入环点的距离是<code>D</code>，从入环点到两个指针首次相遇点为<code>S1</code>，从两个指针首次相遇点到入环点的距离为<code>S2</code>。</p><p>设慢指针的移动速度为一个节点，快指针的移动速度为两个节点。</p><p>故两个指针首次相遇时，两个指针所走的距离如下</p><p>慢指针行走的距离为<code>D + S1</code></p><p>快指针的速度是慢指针的两倍，所以相当于比慢指针多走了<code>N</code>圈，故行走的距离为<code>D + S1 + N * (S1 + S2)</code></p><p>因为快指针的速度是慢指针的两倍，所以快指针的行走距离应该为慢指针的两倍，所以有</p><p><code>2(D + S1) = D + S1 + N * (S1 + S2)</code></p><p>经过整理可以得到</p><p><code>D = (N - 1)(S1 + S2) + S2</code></p><p>也就是说，链表头节点到入环点的的距离相当于从首次相遇点绕环<code>N - 1</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><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><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_">Solution</span> &#123;</span><br><span class="line">    <span class="keyword">public</span> ListNode <span class="title function_">detectCycle</span><span class="params">(ListNode head)</span> &#123;</span><br><span class="line">        <span class="keyword">if</span>(head == <span class="literal">null</span> || head.next == <span class="literal">null</span>) <span class="keyword">return</span> <span class="literal">null</span>;</span><br><span class="line">        <span class="type">ListNode</span> <span class="variable">fast</span> <span class="operator">=</span> head;</span><br><span class="line">        <span class="type">ListNode</span> <span class="variable">slow</span> <span class="operator">=</span> head;</span><br><span class="line">        <span class="keyword">while</span>(fast != <span class="literal">null</span> &amp;&amp; fast.next != <span class="literal">null</span>) &#123;</span><br><span class="line">            slow = slow.next;</span><br><span class="line">            fast = fast.next.next;</span><br><span class="line">            <span class="keyword">if</span>(slow == fast) &#123;</span><br><span class="line">                fast = head;</span><br><span class="line">                <span class="keyword">while</span>(fast != slow) &#123;</span><br><span class="line">                   fast = fast.next;</span><br><span class="line">                   slow = slow.next;</span><br><span class="line">                &#125;</span><br><span class="line">                <span class="keyword">return</span> fast;</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">return</span> <span class="literal">null</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><blockquote><p>设计一个支持 <code>push</code> ，<code>pop</code> ，<code>top</code> 操作，并能在常数时间内检索到最小元素的栈。</p><ul><li><code>push(x)</code> —— 将元素 x 推入栈中。</li><li><code>pop()</code> —— 删除栈顶的元素。</li><li><code>top()</code> —— 获取栈顶元素。</li><li><code>getMin()</code> —— 检索栈中的最小元素。</li></ul></blockquote><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">class</span> <span class="title class_">MinStack</span> &#123;</span><br><span class="line">    Stack&lt;Integer&gt; min;</span><br><span class="line">    Stack&lt;Integer&gt; stack;</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">        stack = <span class="keyword">new</span> <span class="title class_">Stack</span>&lt;&gt;();</span><br><span class="line">        min = <span class="keyword">new</span> <span class="title class_">Stack</span>&lt;&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="keyword">if</span> (stack.isEmpty()) &#123;</span><br><span class="line">            stack.push(val);</span><br><span class="line">            min.push(val);</span><br><span class="line">        &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">            stack.push(val);</span><br><span class="line">            <span class="keyword">if</span> (val &lt; min.peek()) &#123;</span><br><span class="line">                min.push(val);</span><br><span class="line">            &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">                min.push(min.peek());</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_">pop</span><span class="params">()</span> &#123;</span><br><span class="line">        <span class="keyword">if</span> (stack.isEmpty()) &#123;&#125;</span><br><span class="line">        stack.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="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> stack.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">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><h2 id="如何求出最大公约数"><a href="#如何求出最大公约数" class="headerlink" title="如何求出最大公约数"></a>如何求出最大公约数</h2><blockquote><p>求出两个正整数的最大公约数。</p></blockquote><h3 id="枚举"><a href="#枚举" class="headerlink" title="枚举"></a>枚举</h3><p>从较小整数的一半开始遍历到2，首次出现了两个正整数都整除的数，则该数就是两个正整数的最大公约数。</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><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="type">int</span> <span class="title function_">getGreatestCommonDivisor</span><span class="params">(<span class="type">int</span> a, <span class="type">int</span> b)</span> &#123;</span><br><span class="line">        <span class="type">int</span> <span class="variable">max</span> <span class="operator">=</span> Math.max(a, b);</span><br><span class="line">        <span class="type">int</span> <span class="variable">min</span> <span class="operator">=</span> Math.min(a, b);</span><br><span class="line">        <span class="keyword">if</span> (max % min == <span class="number">0</span>) &#123;</span><br><span class="line">            <span class="keyword">return</span> min;</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">for</span> (<span class="type">int</span> <span class="variable">i</span> <span class="operator">=</span>  min / <span class="number">2</span>; i &gt; <span class="number">1</span>; i--) &#123;</span><br><span class="line">            <span class="keyword">if</span> (min % i == <span class="number">0</span> &amp;&amp; max % i == <span class="number">0</span>) &#123;</span><br><span class="line">                <span class="keyword">return</span> i;</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">return</span> <span class="number">1</span>;</span><br><span class="line">    &#125;</span><br></pre></td></tr></table></figure><p>时间复杂度为O(min(a, b))</p><h3 id="辗转相除法（欧几里得算法）"><a href="#辗转相除法（欧几里得算法）" class="headerlink" title="辗转相除法（欧几里得算法）"></a>辗转相除法（欧几里得算法）</h3><p>这个方法基于一个定理：</p><p>两个正整数a和b（a &gt; b），它们的最大公约数等于a除以b的余数c和b之间的最大公约数。</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><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">static</span> <span class="type">int</span> <span class="title function_">getGreatestCommonDivisor</span><span class="params">(<span class="type">int</span> a, <span class="type">int</span> b)</span> &#123;</span><br><span class="line">    <span class="type">int</span> <span class="variable">max</span> <span class="operator">=</span> Math.max(a, b);</span><br><span class="line">    <span class="type">int</span> <span class="variable">min</span> <span class="operator">=</span> Math.min(a, b);</span><br><span class="line">    <span class="keyword">if</span> (max % min == <span class="number">0</span>) &#123;</span><br><span class="line">        <span class="keyword">return</span> min;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">return</span> getGreatestCommonDivisor(max % min, min);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>时间复杂度近似为O(log(max(a, b))，当两个正整数很大的时候，a % b的计算性能很差。</p><h3 id="更相减损术"><a href="#更相减损术" class="headerlink" title="更相减损术"></a>更相减损术</h3><p>两个正整数a和b（a &gt; b），它们的最大公约数等于a - b的差值c和较小数b的最大公约数。</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><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">static</span> <span class="type">int</span> <span class="title function_">getGreatestCommonDivisor</span><span class="params">(<span class="type">int</span> a, <span class="type">int</span> b)</span> &#123;</span><br><span class="line">    <span class="keyword">if</span> (a == b) &#123;</span><br><span class="line">        <span class="keyword">return</span> a;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="type">int</span> <span class="variable">max</span> <span class="operator">=</span> Math.max(a, b);</span><br><span class="line">    <span class="type">int</span> <span class="variable">min</span> <span class="operator">=</span> Math.min(a, b);</span><br><span class="line">    <span class="keyword">return</span> getGreatestCommonDivisor(max - min, min);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>避免了大数取模的计算，但是算法性能不稳定，最坏的时间复杂度为O(max(a, b))</p><h3 id="更优算法"><a href="#更优算法" class="headerlink" title="更优算法"></a>更优算法</h3><ul><li>当a和b均为偶数时，gcd(a,b) = 2 <em> gcd(a / 2, b / 2) = 2 </em> gcd(a &gt;&gt; 1, b &gt;&gt; 1) = gcd(a &gt;&gt; 1, b &gt;&gt; 1) &lt;&lt; 1;</li><li>当a为偶数，b为奇数时，gcd(a, b) = gcd(a / 2, b) = gcd(a &gt;&gt; 1, b);</li><li>当a为奇数，b为偶数时，gcd(a, b) = gcd(a, b / 2) = gcd(a, b &gt;&gt; 1);</li><li>当a和b均为奇数时，先利用更相减损术运算一次，gcd(a, b) = gcd(b, a - b)，此时a - b必是偶数，然后进行上面情况的运算。</li></ul><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">static</span> <span class="type">int</span> <span class="title function_">getGreatestCommonDivisor</span><span class="params">(<span class="type">int</span> a, <span class="type">int</span> b)</span> &#123;</span><br><span class="line">    <span class="keyword">if</span> (a == b) &#123;</span><br><span class="line">        <span class="keyword">return</span> a;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">if</span> ((a &amp; <span class="number">1</span>) == <span class="number">0</span> &amp;&amp; (b &amp; <span class="number">1</span>) == <span class="number">0</span>) &#123;</span><br><span class="line">        <span class="keyword">return</span> getGreatestCommonDivisor(a &gt;&gt; <span class="number">1</span>, b &gt;&gt; <span class="number">1</span>) &lt;&lt; <span class="number">1</span>;</span><br><span class="line">    &#125; <span class="keyword">else</span> <span class="keyword">if</span> ((a &amp; <span class="number">1</span>) == <span class="number">0</span> &amp;&amp; (b &amp; <span class="number">1</span>) != <span class="number">0</span>) &#123;</span><br><span class="line">        <span class="keyword">return</span> getGreatestCommonDivisor(a &gt;&gt; <span class="number">1</span>, b);</span><br><span class="line">    &#125; <span class="keyword">else</span> <span class="keyword">if</span> ((a &amp; <span class="number">1</span>) != <span class="number">0</span> &amp;&amp; (b &amp; <span class="number">1</span>) == <span class="number">0</span>) &#123;</span><br><span class="line">        <span class="keyword">return</span> getGreatestCommonDivisor(a, b &gt;&gt; <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">max</span> <span class="operator">=</span> Math.max(a, b);</span><br><span class="line">        <span class="type">int</span> <span class="variable">min</span> <span class="operator">=</span> Math.min(a, b);</span><br><span class="line">        <span class="keyword">return</span> getGreatestCommonDivisor(max - min, min);</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">static</span> <span class="type">int</span> <span class="title function_">getGreatestCommonDivisor</span><span class="params">(<span class="type">int</span> a, <span class="type">int</span> b)</span> &#123;</span><br><span class="line">    <span class="keyword">if</span> (a == b) &#123;</span><br><span class="line">        <span class="keyword">return</span> a;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">if</span> ((a &amp; <span class="number">1</span>) == <span class="number">0</span> &amp;&amp; (b &amp; <span class="number">1</span>) == <span class="number">0</span>) &#123;</span><br><span class="line">        <span class="keyword">return</span> getGreatestCommonDivisor(a &gt;&gt; <span class="number">1</span>, b &gt;&gt; <span class="number">1</span>) &lt;&lt; <span class="number">1</span>;</span><br><span class="line">    &#125; <span class="keyword">else</span> <span class="keyword">if</span> ((a &amp; <span class="number">1</span>) == <span class="number">0</span>) &#123;</span><br><span class="line">        <span class="keyword">return</span> getGreatestCommonDivisor(a &gt;&gt; <span class="number">1</span>, b);</span><br><span class="line">    &#125; <span class="keyword">else</span> <span class="keyword">if</span> ((b &amp; <span class="number">1</span>) == <span class="number">0</span>) &#123;</span><br><span class="line">        <span class="keyword">return</span> getGreatestCommonDivisor(a, b &gt;&gt; <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">max</span> <span class="operator">=</span> Math.max(a, b);</span><br><span class="line">        <span class="type">int</span> <span class="variable">min</span> <span class="operator">=</span> Math.min(a, b);</span><br><span class="line">        <span class="keyword">return</span> getGreatestCommonDivisor(max - min, min);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>这是更相减损法与位运算的结合方法，算法性能稳定，而且使用了位运算，时间复杂度为O(log(max(a, b)))</p><h2 id="判断一个数是否为2的整数次幂"><a href="#判断一个数是否为2的整数次幂" class="headerlink" title="判断一个数是否为2的整数次幂"></a>判断一个数是否为2的整数次幂</h2><blockquote><p>判断一个正整数是否为2的整数次幂</p></blockquote><h3 id="枚举-1"><a href="#枚举-1" class="headerlink" title="枚举"></a>枚举</h3><p>计算出2的整数次幂，依次递增，若是出现了大于该数的2次幂（并没有等于的），就证明该数不是2的整数次幂。</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><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">class</span> <span class="title class_">Solution</span> &#123;</span><br><span class="line">    <span class="keyword">public</span> <span class="type">boolean</span> <span class="title function_">isPowerOfTwo</span><span class="params">(<span class="type">int</span> n)</span> &#123;</span><br><span class="line">        <span class="type">int</span> <span class="variable">num</span> <span class="operator">=</span> <span class="number">1</span>;</span><br><span class="line">        <span class="keyword">while</span> (num &lt;= n) &#123;</span><br><span class="line">            <span class="keyword">if</span> (num == n) <span class="keyword">return</span> <span class="literal">true</span>;</span><br><span class="line">            num = num &lt;&lt; <span class="number">1</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>算法的时间复杂度为O(logn)。</p><h3 id="n-amp-n-1-0"><a href="#n-amp-n-1-0" class="headerlink" title="n &amp; (n - 1) == 0"></a>n &amp; (n - 1) == 0</h3><p>每一个2的整数次幂转换为二进制，都是1开头后面都是0的数串。</p><p>将该整数次幂减一，就得到二进制全为1的数串。</p><p>这两个数取按位与运算，就会出现每一位数都不一样，所以结果为0。</p><p>所以不为0的就不是2的整数次幂了。</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><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">class</span> <span class="title class_">Solution</span> &#123;</span><br><span class="line">    <span class="keyword">public</span> <span class="type">boolean</span> <span class="title function_">isPowerOfTwo</span><span class="params">(<span class="type">int</span> n)</span> &#123;</span><br><span class="line">        <span class="keyword">return</span> (n &amp; n - <span class="number">1</span>) == <span class="number">0</span>;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h3 id="n-amp-n-n"><a href="#n-amp-n-n" class="headerlink" title="n &amp; -n == n"></a>n &amp; -n == n</h3><p><code>n &amp; -n</code>可以得到最低位的1的位置，即最低位为1其余全是0的数串，如果是2的整数次幂的话，那么该数串其实表示的就是本身。</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><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">class</span> <span class="title class_">Solution</span> &#123;</span><br><span class="line">    <span class="keyword">public</span> <span class="type">boolean</span> <span class="title function_">isPowerOfTwo</span><span class="params">(<span class="type">int</span> n)</span> &#123;</span><br><span class="line">        <span class="keyword">return</span> (n &amp; -n) == n;</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><blockquote><p>有一个无序整数型数组，如何求出该数组排序后的任意两个相邻元素的最大差值？</p></blockquote><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></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">static</span> <span class="type">int</span> <span class="title function_">getMaxSortedDistance</span><span class="params">(<span class="type">int</span>[] array)</span> &#123;</span><br><span class="line">    Arrays.sort(array);</span><br><span class="line">    <span class="type">int</span> <span class="variable">max</span> <span class="operator">=</span> -<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">1</span>; i &lt; array.length; i++) &#123;</span><br><span class="line">        <span class="type">int</span> <span class="variable">temp</span> <span class="operator">=</span> array[i] - array[i - <span class="number">1</span>];</span><br><span class="line">        <span class="keyword">if</span> (temp &gt; max) &#123;</span><br><span class="line">            max = temp;</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">return</span> max;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>将数组进行快速排序O(nlogn)，然后进行遍历O(n)，所以总的时间复杂度为O(nlogn)</p><p>空间复杂度就是该数组O(n)</p><h3 id="计数排序"><a href="#计数排序" class="headerlink" title="计数排序"></a>计数排序</h3><p>使用计数排序，找出新数组最多连续出现0的次数加1，就是最大相邻差了。</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></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">static</span> <span class="type">int</span> <span class="title function_">getMaxSortedDistance</span><span class="params">(<span class="type">int</span>[] array)</span> &#123;</span><br><span class="line">    <span class="comment">// 求出数组的最大值和最小值</span></span><br><span class="line">    <span class="type">int</span> <span class="variable">max</span> <span class="operator">=</span> array[<span class="number">0</span>];</span><br><span class="line">    <span class="type">int</span> <span class="variable">min</span> <span class="operator">=</span> array[<span class="number">0</span>];</span><br><span class="line">    <span class="keyword">for</span> (<span class="type">int</span> <span class="variable">i</span> <span class="operator">=</span> <span class="number">1</span>; i &lt; array.length; i++) &#123;</span><br><span class="line">        <span class="keyword">if</span> (array[i] &gt; max) &#123;</span><br><span class="line">            max = array[i];</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">if</span> (array[i] &lt; min) &#123;</span><br><span class="line">            min = array[i];</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">d</span> <span class="operator">=</span> max - min;</span><br><span class="line">    <span class="comment">// 最大值和最小值相等，证明数组的所有数相等</span></span><br><span class="line">    <span class="keyword">if</span> (d == <span class="number">0</span>) &#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 class="comment">// 计数排序</span></span><br><span class="line">    <span class="type">int</span>[] arr = <span class="keyword">new</span> <span class="title class_">int</span>[d + <span class="number">1</span>];</span><br><span class="line">    <span class="keyword">for</span> (<span class="type">int</span> i : array) &#123;</span><br><span class="line">        arr[i - min]++;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="type">int</span> <span class="variable">maxDistance</span> <span class="operator">=</span> <span class="number">0</span>;</span><br><span class="line">    </span><br><span class="line">    <span class="comment">// 寻找最长连续的0</span></span><br><span class="line">    <span class="keyword">for</span> (<span class="type">int</span> <span class="variable">i</span> <span class="operator">=</span> <span class="number">0</span>; i &lt; arr.length; i++) &#123;</span><br><span class="line">        <span class="keyword">if</span> (arr[i] == <span class="number">0</span>) &#123;</span><br><span class="line">            <span class="type">int</span> <span class="variable">temp</span> <span class="operator">=</span> i;</span><br><span class="line">            <span class="keyword">while</span> (arr[temp] == <span class="number">0</span>) &#123;</span><br><span class="line">                temp++;</span><br><span class="line">            &#125;</span><br><span class="line">            <span class="keyword">if</span> (temp - i &gt; maxDistance) &#123;</span><br><span class="line">                maxDistance = temp - i;</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">return</span> maxDistance + <span class="number">1</span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h3 id="桶排序"><a href="#桶排序" class="headerlink" title="桶排序"></a>桶排序</h3><p>记录桶内的最大和最小值，统计出每一个桶的最大值，和这个桶右侧非空桶的最小值的差，数值最大的差即为题目所求，故时间复杂度为O(n)</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><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">static</span> <span class="type">int</span> <span class="title function_">getMaxSortedDistance</span><span class="params">(<span class="type">int</span>[] array)</span> &#123;</span><br><span class="line">    <span class="comment">// 求出数组的最大值和最小值</span></span><br><span class="line">    <span class="type">int</span> <span class="variable">max</span> <span class="operator">=</span> array[<span class="number">0</span>];</span><br><span class="line">    <span class="type">int</span> <span class="variable">min</span> <span class="operator">=</span> array[<span class="number">0</span>];</span><br><span class="line">    <span class="keyword">for</span> (<span class="type">int</span> <span class="variable">i</span> <span class="operator">=</span> <span class="number">1</span>; i &lt; array.length; i++) &#123;</span><br><span class="line">        <span class="keyword">if</span> (array[i] &gt; max) &#123;</span><br><span class="line">            max = array[i];</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">if</span> (array[i] &lt; min) &#123;</span><br><span class="line">            min = array[i];</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">d</span> <span class="operator">=</span> max - min;</span><br><span class="line">    <span class="comment">// 最大值和最小值相等，证明数组的所有数相等</span></span><br><span class="line">    <span class="keyword">if</span> (d == <span class="number">0</span>) &#123;</span><br><span class="line">        <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 初始化桶</span></span><br><span class="line">    <span class="type">int</span> <span class="variable">bucketNum</span> <span class="operator">=</span> array.length;</span><br><span class="line">    Bucket[] buckets = <span class="keyword">new</span> <span class="title class_">Bucket</span>[bucketNum];</span><br><span class="line">    Arrays.fill(buckets,<span class="keyword">new</span> <span class="title class_">Bucket</span>());</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 遍历原始数组，确定每个桶的最大值和最小值</span></span><br><span class="line">    <span class="keyword">for</span> (<span class="type">int</span> i : array) &#123;</span><br><span class="line">        <span class="type">int</span> <span class="variable">index</span> <span class="operator">=</span> ((i - min) * (bucketNum - <span class="number">1</span>) / d);</span><br><span class="line">        <span class="keyword">if</span> (buckets[index].min == <span class="literal">null</span> || buckets[index].min &gt; i) &#123;</span><br><span class="line">            buckets[index].min = i;</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">if</span> (buckets[index].max == <span class="literal">null</span> || buckets[index].max &lt; i) &#123;</span><br><span class="line">            buckets[index].max = i;</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 遍历桶，找到最大差值</span></span><br><span class="line">    <span class="type">int</span> <span class="variable">leftMax</span> <span class="operator">=</span> buckets[<span class="number">0</span>].max;</span><br><span class="line">    <span class="type">int</span> <span class="variable">maxDistance</span> <span class="operator">=</span> <span class="number">0</span>;</span><br><span class="line">    <span class="keyword">for</span> (<span class="type">int</span> <span class="variable">i</span> <span class="operator">=</span> <span class="number">1</span>; i &lt; buckets.length; i++) &#123;</span><br><span class="line">        <span class="keyword">if</span> (buckets[i].min == <span class="literal">null</span>) &#123;</span><br><span class="line">            <span class="keyword">continue</span>;</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">if</span> (buckets[i].min - leftMax &gt; maxDistance) &#123;</span><br><span class="line">            maxDistance = buckets[i].min - leftMax;</span><br><span class="line">        &#125;</span><br><span class="line">        leftMax = buckets[i].max;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">return</span> maxDistance;</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> <span class="keyword">class</span> <span class="title class_">Bucket</span> &#123;</span><br><span class="line">    Integer min;</span><br><span class="line">    Integer max;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h2 id="用栈实现队列"><a href="#用栈实现队列" class="headerlink" title="用栈实现队列"></a>用栈实现队列</h2><blockquote><p>用栈来模拟一个队列，要求实现队列的两个基本操作：入队和出队。</p></blockquote><p>使用一个栈明显是不能实现队列的，所以需要一个辅助栈。设这两个栈为a和b。</p><p>a栈用来存储入队的元素，入队的元素直接压入a栈就好。</p><p>b栈用来存储出队的元素，出队的元素就直接推出b栈就好。</p><p>当b栈是空的，就将a栈的所有元素出栈压入a栈，这样出栈的顺序就相反了，即符合先进先出了。</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><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="keyword">class</span> <span class="title class_">CQueue</span> &#123;</span><br><span class="line"></span><br><span class="line">    Stack&lt;Integer&gt; stack1;</span><br><span class="line">    Stack&lt;Integer&gt; stack2;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">public</span> <span class="title function_">CQueue</span><span class="params">()</span> &#123;</span><br><span class="line">        stack1 = <span class="keyword">new</span> <span class="title class_">Stack</span>&lt;&gt;();</span><br><span class="line">        stack2 = <span class="keyword">new</span> <span class="title class_">Stack</span>&lt;&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_">appendTail</span><span class="params">(<span class="type">int</span> value)</span> &#123;</span><br><span class="line">        stack1.push(value);</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_">deleteHead</span><span class="params">()</span> &#123;</span><br><span class="line">        <span class="keyword">if</span>(!stack2.isEmpty()) &#123;</span><br><span class="line">            <span class="keyword">return</span> stack2.pop();</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">if</span>(stack1.isEmpty()) &#123;</span><br><span class="line">            <span class="keyword">return</span> -<span class="number">1</span>;</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">while</span>(!stack1.isEmpty()) &#123;</span><br><span class="line">            stack2.push(stack1.pop());</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">return</span> stack2.pop();</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"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * Your CQueue object will be instantiated and called as such:</span></span><br><span class="line"><span class="comment"> * CQueue obj = new CQueue();</span></span><br><span class="line"><span class="comment"> * obj.appendTail(value);</span></span><br><span class="line"><span class="comment"> * int param_2 = obj.deleteHead();</span></span><br><span class="line"><span class="comment"> */</span></span><br></pre></td></tr></table></figure><h2 id="删除k个数字后的最小值"><a href="#删除k个数字后的最小值" class="headerlink" title="删除k个数字后的最小值"></a>删除k个数字后的最小值</h2><blockquote><p>给出一个整数，从该整数中去掉k个数字，要求剩下的数字形成的新整数尽可能小。应该如何选取被去掉的数字？</p></blockquote><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></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">static</span> String <span class="title function_">removeKDigits</span><span class="params">(String num, <span class="type">int</span> k)</span> &#123;</span><br><span class="line">    <span class="type">int</span> <span class="variable">newLength</span> <span class="operator">=</span> num.length() - k;</span><br><span class="line">    <span class="type">char</span>[] stack = <span class="keyword">new</span> <span class="title class_">char</span>[num.length()];</span><br><span class="line">    <span class="type">int</span> <span class="variable">top</span> <span class="operator">=</span> <span class="number">0</span>;</span><br><span class="line">    <span class="keyword">for</span> (<span class="type">int</span> <span class="variable">i</span> <span class="operator">=</span> <span class="number">0</span>; i &lt; num.length(); i++) &#123;</span><br><span class="line">        <span class="type">char</span> <span class="variable">c</span> <span class="operator">=</span> num.charAt(i);</span><br><span class="line">        <span class="keyword">while</span> (top &gt; <span class="number">0</span> &amp;&amp; stack[top - <span class="number">1</span>] &gt; c &amp;&amp; k &gt; <span class="number">0</span>) &#123;</span><br><span class="line">            top -= <span class="number">1</span>;</span><br><span class="line">            k -= <span class="number">1</span>;</span><br><span class="line">        &#125;</span><br><span class="line">        stack[top++] = c;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="type">int</span> <span class="variable">offset</span> <span class="operator">=</span> <span class="number">0</span>;</span><br><span class="line">    <span class="keyword">while</span> (offset &lt; newLength &amp;&amp; stack[offset] == <span class="string">&#x27;0&#x27;</span>) &#123;</span><br><span class="line">        offset++;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">return</span> offset == newLength ? <span class="string">&quot;0&quot;</span> : <span class="keyword">new</span> <span class="title class_">String</span>(stack,offset,newLength - offset);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h2 id="如何实现大整数相加"><a href="#如何实现大整数相加" class="headerlink" title="如何实现大整数相加"></a>如何实现大整数相加</h2><blockquote><p>给出两个很大的正整数，要求实现程序求出两个整数之和。</p></blockquote><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><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">static</span> String <span class="title function_">bigNumberSum</span><span class="params">(String bigNumberA, String bigNumberB)</span> &#123;</span><br><span class="line">    <span class="type">int</span> <span class="variable">maxLen</span> <span class="operator">=</span> Math.max(bigNumberA.length(), bigNumberB.length()) + <span class="number">1</span>;</span><br><span class="line">    <span class="comment">// 将两个大数逆序存储到数组中</span></span><br><span class="line">    <span class="type">int</span>[] arrA = <span class="keyword">new</span> <span class="title class_">int</span>[maxLen]; </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; bigNumberA.length(); i++) &#123;</span><br><span class="line">        arrA[i] = bigNumberA.charAt(bigNumberA.length() - i - <span class="number">1</span>) - <span class="string">&#x27;0&#x27;</span>;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="type">int</span>[] arrB = <span class="keyword">new</span> <span class="title class_">int</span>[maxLen];</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; bigNumberB.length(); i++) &#123;</span><br><span class="line">        arrB[i] = bigNumberB.charAt(bigNumberB.length() - i - <span class="number">1</span>) - <span class="string">&#x27;0&#x27;</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>[] res = <span class="keyword">new</span> <span class="title class_">int</span>[maxLen];</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; res.length; i++) &#123;</span><br><span class="line">        <span class="type">int</span> <span class="variable">temp</span> <span class="operator">=</span> res[i];</span><br><span class="line">        temp += arrA[i] + arrB[i];</span><br><span class="line">        <span class="keyword">if</span> (temp &gt;= <span class="number">10</span>) &#123;</span><br><span class="line">            temp -= <span class="number">10</span>;</span><br><span class="line">            res[i + <span class="number">1</span>]++;</span><br><span class="line">        &#125;</span><br><span class="line">        res[i] = temp;</span><br><span 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">StringBuilder</span> <span class="variable">str</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">StringBuilder</span>();</span><br><span class="line">    <span class="type">boolean</span> <span class="variable">findFirst</span> <span class="operator">=</span> <span class="literal">false</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> res.length - <span class="number">1</span>; i &gt;= <span class="number">0</span>; i--) &#123;</span><br><span class="line">        <span class="keyword">if</span> (!findFirst) &#123;</span><br><span class="line">            <span class="keyword">if</span> (res[i] == <span class="number">0</span>) &#123;</span><br><span class="line">                <span class="keyword">continue</span>;</span><br><span class="line">            &#125;</span><br><span class="line">            findFirst = <span class="literal">true</span>;</span><br><span class="line">        &#125;</span><br><span class="line">        str.append(res[i]);</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">return</span> str.toString();</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h2 id="金矿问题"><a href="#金矿问题" class="headerlink" title="金矿问题"></a>金矿问题</h2><blockquote><p>​    很久很久以前，有一位国王拥有5座金矿，每座金矿的黄金储量不同，需要参与挖掘的工人人数也不同。例如又得金矿储量是500kg黄金，需要5个工人来挖掘；有的金矿储量是200kg黄金，需要3个工人来挖掘……</p><p>​    如果参与挖矿的工人的总数是10.每座金矿要么全挖，要么不挖，不能派出一半人挖取一半的金矿。要用程序求出，要想得到尽可能多的黄金，应该选取挖取那几座金矿？</p></blockquote><p>这题就是一道背包问题，根据题意可知这是01背包的问题，有两种状态转移的情况，选或者是不选。</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><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="comment">/**</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@param</span> w 工人数量</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@param</span> p 金矿开采所需的工人数量</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@param</span> g 金矿储量</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">int</span> <span class="title function_">getBestGoldMining</span><span class="params">(<span class="type">int</span> w, <span class="type">int</span>[] p, <span class="type">int</span>[] g)</span> &#123;</span><br><span class="line">    <span class="type">int</span>[] res = <span class="keyword">new</span> <span class="title class_">int</span>[w + <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">1</span>; i &lt;= g.length; i++) &#123;</span><br><span class="line">        <span class="keyword">for</span> (<span class="type">int</span> <span class="variable">j</span> <span class="operator">=</span> w; j &gt;= <span class="number">1</span>; j--) &#123;</span><br><span class="line">            <span class="keyword">if</span> (j &gt;= p[i - <span class="number">1</span>]) &#123;</span><br><span class="line">                res[j] = Math.max(res[j],res[j - p[i - <span class="number">1</span>]] + g[i - <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">    <span class="keyword">return</span> res[w];</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h2 id="寻找缺少的整数"><a href="#寻找缺少的整数" class="headerlink" title="寻找缺少的整数"></a>寻找缺少的整数</h2><blockquote><p>在一个无序数组里有99个不重复的正整数，范围从1到100，唯独缺少1个1~100中的整数。如何找出整个缺少的整数。</p></blockquote><h3 id="哈希表"><a href="#哈希表" class="headerlink" title="哈希表"></a>哈希表</h3><p>创建一个哈希表，将1到100这100个数全部存入key中，遍历整个数组，将存在的key都删掉，剩下的key就是缺少的整数。</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><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="type">int</span> <span class="title function_">getBestGoldMining</span><span class="params">(<span class="type">int</span>[] arr)</span> &#123;</span><br><span class="line">    Map&lt;Integer, Integer&gt; map = <span class="keyword">new</span> <span class="title class_">HashMap</span>&lt;&gt;(<span class="number">100</span>);</span><br><span class="line">    <span class="keyword">for</span> (<span class="type">int</span> <span class="variable">i</span> <span class="operator">=</span> <span class="number">1</span>; i &lt;= <span class="number">100</span>; i++) &#123;</span><br><span class="line">        map.put(i,i);</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">for</span> (<span class="type">int</span> i : arr) &#123;</span><br><span class="line">        map.remove(i);</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="type">int</span> <span class="variable">res</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> key : map.keySet()) &#123;</span><br><span class="line">      res = key;  </span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">return</span> res;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h3 id="先排序后寻找"><a href="#先排序后寻找" class="headerlink" title="先排序后寻找"></a>先排序后寻找</h3><p>将数组进行排序，然后逐个寻找。</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">static</span> <span class="type">int</span> <span class="title function_">getBestGoldMining</span><span class="params">(<span class="type">int</span>[] arr)</span> &#123;</span><br><span class="line">    Arrays.sort(arr);</span><br><span class="line">    <span class="keyword">if</span> (arr[<span class="number">0</span>] != <span class="number">1</span>) &#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 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; arr.length - <span class="number">1</span>; i++) &#123;</span><br><span class="line">        <span class="keyword">if</span> (arr[i] + <span class="number">1</span> != arr[i + <span class="number">1</span>]) &#123;</span><br><span class="line">            <span class="keyword">return</span> arr[i] + <span class="number">1</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="number">100</span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h3 id="求和"><a href="#求和" class="headerlink" title="求和"></a>求和</h3><p>计算出1~100的和，然后减去数组中元素的和，剩下的就是所缺少的整数了。</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><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">static</span> <span class="type">int</span> <span class="title function_">getBestGoldMining</span><span class="params">(<span class="type">int</span>[] arr)</span> &#123;</span><br><span class="line">    <span class="type">int</span> <span class="variable">res</span> <span class="operator">=</span> <span class="number">5050</span>;</span><br><span class="line">    <span class="keyword">for</span> (<span class="type">int</span> i : arr) &#123;</span><br><span class="line">        res -= i;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">return</span> res;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h3 id="求只出现一次的数"><a href="#求只出现一次的数" class="headerlink" title="求只出现一次的数"></a>求只出现一次的数</h3><blockquote><p>​    一个无序数组里有若干个正整数，范围是1~100，其中99个整数都出现了偶数次，只有一个整数出现了奇书次，如何找到这个出现奇数次的整数？</p></blockquote><p>可以将数与数出现的次数当成Key和Value放入哈希表，然后寻找出现次数为1的，和上述哈希表的做法差不多，就不深入了。</p><p>这里可以使用异或运算，先了解异或运算的规律。</p><ol><li>两个相同的数异或，结果等于0</li><li>任何数与0异或，结果等于本身</li><li>异或运算符合交换律</li></ol><p>故将数组中的数进行异或，偶数次的数异或会变成0，奇数次的数就会留着。</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><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">static</span> <span class="type">int</span> <span class="title function_">getBestGoldMining</span><span class="params">(<span class="type">int</span>[] arr)</span> &#123;</span><br><span class="line">    <span class="type">int</span> <span class="variable">res</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> i : arr) &#123;</span><br><span class="line">        res ^= i;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">return</span> res;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h2 id="LRU（最近最少使用算法）"><a href="#LRU（最近最少使用算法）" class="headerlink" title="LRU（最近最少使用算法）"></a>LRU（最近最少使用算法）</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><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></pre></td><td class="code"><pre><span class="line"><span class="keyword">package</span> com.yww;</span><br><span class="line"></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 class="doctag">@ClassName</span> LRUCache</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@author</span> yww</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_">LRUCache</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">private</span> Node head;</span><br><span class="line">    <span class="comment">/**</span></span><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> Node end;</span><br><span class="line">    <span class="comment">/**</span></span><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> <span class="type">int</span> limit;</span><br><span class="line">    <span class="comment">/**</span></span><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> HashMap&lt;String, Node&gt; map;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">class</span> <span class="title class_">Node</span> &#123;</span><br><span class="line">        <span class="keyword">public</span> Node pre;</span><br><span class="line">        <span class="keyword">public</span> Node next;</span><br><span class="line">        <span class="keyword">public</span> String key;</span><br><span class="line">        <span class="keyword">public</span> String value;</span><br><span class="line">        Node(String key, String value) &#123;</span><br><span class="line">            <span class="built_in">this</span>.key = key;</span><br><span class="line">            <span class="built_in">this</span>.value = value;</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">public</span> <span class="title function_">LRUCache</span><span class="params">(<span class="type">int</span> limit)</span> &#123;</span><br><span class="line">        <span class="built_in">this</span>.limit = limit;</span><br><span class="line">        map = <span class="keyword">new</span> <span class="title class_">HashMap</span>&lt;&gt;();</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     * 通过key获取缓存节点</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_">get</span><span class="params">(String key)</span> &#123;</span><br><span class="line">        <span class="type">Node</span> <span class="variable">node</span> <span class="operator">=</span> map.get(key);</span><br><span class="line">        <span class="keyword">if</span> (node == <span class="literal">null</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">        refreshNode(node);</span><br><span class="line">        <span class="keyword">return</span> node.value;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     * 放入缓存，若key存在，更新value值</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_">put</span><span class="params">(String key, String value)</span> &#123;</span><br><span class="line">        <span class="type">Node</span> <span class="variable">node</span> <span class="operator">=</span> map.get(key);</span><br><span class="line">        <span class="keyword">if</span> (node == <span class="literal">null</span>) &#123;</span><br><span class="line">            <span class="keyword">if</span> (map.size() &gt;= limit) &#123;</span><br><span class="line">                <span class="type">String</span> <span class="variable">oldKey</span> <span class="operator">=</span> removeNode(head);</span><br><span class="line">                map.remove(oldKey);</span><br><span class="line">            &#125;</span><br><span class="line">            node = <span class="keyword">new</span> <span class="title class_">Node</span>(key, value);</span><br><span class="line">            addNode(node);</span><br><span class="line">            map.put(key,node);</span><br><span class="line">        &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">            node.value = value;</span><br><span class="line">            refreshNode(node);</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">     * 通过key删除缓存</span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">remove</span><span class="params">(String key)</span> &#123;</span><br><span class="line">        <span class="type">Node</span> <span class="variable">node</span> <span class="operator">=</span> map.get(key);</span><br><span class="line">        removeNode(node);</span><br><span class="line">        map.remove(key);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     * 刷新被访问的节点</span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">void</span> <span class="title function_">refreshNode</span><span class="params">(Node node)</span> &#123;</span><br><span class="line">        <span class="keyword">if</span> (node == end) &#123;</span><br><span class="line">            <span class="keyword">return</span>;</span><br><span class="line">        &#125;</span><br><span class="line">        removeNode(node);</span><br><span class="line">        addNode(node);</span><br><span 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">private</span> String <span class="title function_">removeNode</span><span class="params">(Node node)</span> &#123;</span><br><span class="line">        <span class="keyword">if</span> (node == head &amp;&amp; node == end) &#123;</span><br><span class="line">            head = <span class="literal">null</span>;</span><br><span class="line">            end = <span class="literal">null</span>;</span><br><span class="line">        &#125; <span class="keyword">else</span> <span class="keyword">if</span> (node == end) &#123;</span><br><span class="line">            end = end.pre;</span><br><span class="line">            end.next = <span class="literal">null</span>;</span><br><span class="line">        &#125; <span class="keyword">else</span> <span class="keyword">if</span> (node == head) &#123;</span><br><span class="line">            head = head.next;</span><br><span class="line">            head.pre = <span class="literal">null</span>;</span><br><span class="line">        &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">            node.pre.next = node.next;</span><br><span class="line">            node.next.pre = node.pre;</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">return</span> node.key;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     * 尾部插入节点</span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">void</span> <span class="title function_">addNode</span><span class="params">(Node node)</span> &#123;</span><br><span class="line">        <span class="keyword">if</span> (end != <span class="literal">null</span>) &#123;</span><br><span class="line">            end.next = node;</span><br><span class="line">            node.pre = end;</span><br><span class="line">            node.next = <span class="literal">null</span>;</span><br><span class="line">        &#125;</span><br><span class="line">        end = node;</span><br><span class="line">        <span class="keyword">if</span> (head == <span class="literal">null</span>) &#123;</span><br><span class="line">            head = node;</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>]]></content:encoded>
            
            
            <category domain="https://yww52.com/categories/%E8%AF%BB%E4%B9%A6%E7%AC%94%E8%AE%B0/">读书笔记</category>
            
            
            
            <comments>https://yww52.com/posts/998d504/#disqus_thread</comments>
            
        </item>
        
        <item>
            <title>码出高效Java开发手册</title>
            <link>https://yww52.com/posts/b93b977e/</link>
            <guid>https://yww52.com/posts/b93b977e/</guid>
            <pubDate>Sat, 29 May 2021 16:00:00 GMT</pubDate>
            
            <description>记录码出高效Java开发手册的读书笔记，主要是书中一些我觉得很有收获的观点，所以大多是原书内容，小部分是自己的总结。</description>
            
            
            
            <content:encoded><![CDATA[<img src="https://img.yww52.com/2021/5/2021-5-30top_img.jpg" alt="码出高效Java开发手册" style="max-width:100%;height:auto;margin-bottom:15px;"
  /><h2 id="面向对象的理念-OOP"><a href="#面向对象的理念-OOP" class="headerlink" title="面向对象的理念(OOP)"></a>面向对象的理念(OOP)</h2><blockquote><p>面向过程编程让计算机有步骤地顺序地做一件事情，是一种过程化地叙事思维。但是在大型软件开发过程中，使用面向过程语言开发，软件维护，软件复用存在着巨大的困难。</p></blockquote><p>面向过程是用来解决一个问题的最好的思想，面向过程最主要的是过程，好的过程就会让一个问题快速正确的获得正确的答案。</p><p>但在软件开发方面，会出现很多很多的功能，即出现很多很多的问题需要解决，对于面向对象思想无疑会产生很多问题。</p><ol><li>无法复用代码，在不同问题中出现相同的子问题，就需要又去复用之前出现的代码，无疑是很麻烦的。</li><li>耦合度高。面向过程的代码不断穿插，就会让软件的耦合度过高，这样只要一个小问题出现了错误，就有可能导致整个大的问题发生错误，这是我们不想看到的。</li></ol><p>面向对象思想的想法就是为了解决这样的问题。</p><h2 id="面向对象的特性"><a href="#面向对象的特性" class="headerlink" title="面向对象的特性"></a>面向对象的特性</h2><blockquote><p>传统意义上，面向对象有三大特性：封装，继承，多态。本书明确将“抽象”作为面向对象的特性之一，支持面向对象“四大特性”的说法。</p><p>Java之父Gosling设计的Object类，是任何类的默认父类，是对万事万物的抽象，是在哲学方向上的延伸思考，高度概括了事务的自然行为和社会行为。Object还对哲学的三个经典问题进行了隐约的解答。</p><ol><li>我是谁？ getClass()说明本质上是谁，而toString()是当前我的名片。</li><li>我从哪里来？Object()构造方法是生产对象的基本方式，clone()是繁殖对象的另一种方式。</li><li>我到哪里去？finalize()是在对象销毁时触发的方法。</li></ol><p>Object还映射了社会科学领域的一些问题。</p><ol><li>世界是否与你而不同？hashCode()和equals()就是判断与其他元素是否相同的一组方法。</li><li>与他人如何协调？wait()和notify()是对象间通信与协作的一组方法。</li></ol></blockquote><p>Object的类是抽象的核心体现。他将万物抽象成一个Object，之后每一个类都继承于Object，将Object类当成一个模板，去将事物抽象成一个类。</p><ol><li><p>封装。封装是将一个类中的行为和属性进行的信息控制，决定是否要公开对类行为和属性的控制。外界只要知道调用这个行为方法，就可以实现一些功能，而不用知道如何去内部是怎样实现的。书中有句话说得很好，模块之间的协作只需要忠于接口，忠于功能实现即可。</p></li><li><p>继承。类将一些复用的行为和属性赋予子类，也就是子类继承了父类，从而复用了父类的代码，也为多态打下基础。</p><blockquote><p>继承的成本很低，一个关键字就可以使用别人的方法，所以继承像抗生素一样容易被滥用，我们传递的理念是谨慎使用继承，认清继承滥用的危险性，即方法污染和方法爆炸。</p><p>方法污染是指父类具备的行为，通过继承传递给子类，子类并不具备执行此行为的能力，这就是方法污染。（因为父类有些方法不希望子类使用）</p><p>方法爆炸是指继承树不断扩大，底层类拥有的方法虽然都能执行，但是由于方法众多，其中部分方法并非于当前类功能定位相关，多次继承后达到上百个方法，造成了方法爆炸。</p></blockquote></li><li><p>多态。根据运行时不同对象的类型，同一个方法产生不同的运行结果，使同一个行为具有不同的表现形式。</p></li></ol><h2 id="接口与抽象类"><a href="#接口与抽象类" class="headerlink" title="接口与抽象类"></a>接口与抽象类</h2><blockquote><p>抽象类是模板式设计，而接口是契约式设计。</p></blockquote><p>抽象类和接口是对一个事物更高级的抽象，抽象类通常包括抽象方法，实体方法和属性变量，而接口一般只是一组行为，没有具体实现和属性。</p><p>抽象类是模板，意义是给子类提供一个抽象的模板，接口是一个契约，继承该接口就要实现里面的所有方法，即遵循这个契约。</p><h2 id="覆写"><a href="#覆写" class="headerlink" title="覆写"></a>覆写</h2><blockquote><p>多态中的override，本书翻译成覆写。如果翻译成重写，那么与重构意思过于接近；如果翻译成覆盖，那么少了“写”这个核心动词。如果父类定义的方法达不到子类的预期，那么子类可以重新实现方法覆盖父类的实现。（实现多态的方式）父类引用执行子类方法时需要注意</p><ol><li>无法调用到子类中存在而父类本身不存在的方法。</li><li>可以调用到子类覆写了父类的方法，这是一种多态的实现。</li></ol></blockquote><p>覆写父类的方法，需要满足以下四个条件。</p><ol><li>访问权限不能变小 。</li><li>返回类型能够向上转型成父类的返回类型。这里的向上转型必须是严格的继承关系，数据类型基本不存在通过继承向上转型的问题。比如<code>int</code>和<code>Integer</code>,不会自动给装箱，是非兼容返回类型。</li><li>异常也要能向上转型成为父类的异常。</li><li>方法名，参数类型及个数必须严格一致。</li></ol><p>每一个覆写方法最好加上<code>@Override</code>注解，编译器会自动检查覆写方法签名是否一致。</p><h2 id="重载"><a href="#重载" class="headerlink" title="重载"></a>重载</h2><blockquote><p>在同一个类中，如果多个方法有相同的方法名称，不同的参数类型，参数个数，参数顺序，即为重载。（方法返回值不在考虑范围）</p></blockquote><p>JVM在重载方法中，选择合适的目标方法的顺序如下：</p><ol><li>精确匹配。</li><li>如果是基本数据类型，自动转换成更大表示范围的基本类型。</li><li>通过自动拆箱与装箱。</li><li>通过子类向上转型继承路线依次匹配。</li><li>通过可变参数匹配。</li></ol><blockquote><p>父类的公有实例方法与子类的公有实例方法可以存在重载关系。不管继承关系如何复杂，重载在编译时可以根据规则知道调用哪种目标方法。所以重载又称为静态绑定。</p></blockquote><h2 id="基本数据类型"><a href="#基本数据类型" class="headerlink" title="基本数据类型"></a>基本数据类型</h2><blockquote><p>虽然Java是面向对象编程语言，一切皆是对象，但是为了兼容人类根深蒂固的数据处理习惯，加快常规数据的处理速度，提供了9种基本数据类型，它们都不具备对象的特性，没有属性和行为。</p><p>9种数据类型包括：boolean,byte,char,short,int,long,float,double,refvar。</p><p>refvar是面向对象世界中的引用变量，也叫引用句柄。本书认为它也是一种基本数据类型。</p></blockquote><p>其中的boolean是一个比较特殊的存在。它的值为false或者是true。在计算机的世界，0表示false,1表示true。所以常规思想会认为booealn的大小为1字节或1位。</p><p>Java虚拟机描述，虽然定义了Boolean数据类型，但是对它的支持有限，所以在编译时用<code>int</code>数据类型来代替，所以会占用4个字节。但时Boolean数组在编译时会被编译成<code>byte</code>数组，所以数组中Boolean占用1个字节。</p><p>refvar无论指向什么，都是占用4个字节的空间。</p><h2 id="代码风格"><a href="#代码风格" class="headerlink" title="代码风格"></a>代码风格</h2><p>书的第三章一章书都是在讲代码风格。</p><p>对于我这种有一点代码洁癖的人来说，还是很建议去学习一套规范的代码风格的。</p><p>这里讲的不是很详细，详细的可以去看一下《阿里巴巴Java开发手册》那本书，一本小册子写了记录了很多的代码规范。</p><p>IDEA也有一个插件，用阿里巴巴的代码规范来规范你的代码，多写些规范的代码，利于别人也利于自己。</p><h2 id="字节码"><a href="#字节码" class="headerlink" title="字节码"></a>字节码</h2><blockquote><p>​    如果某个程序因为不同的硬件平台需要编写多套代码，这是十分令人崩溃的。Java的使命就是一次编写，到处执行。在不同的操作系统，不同的硬件平台上，均可以不用修改代码即可顺畅地执行，如何实现跨平台？有一个声音在天空中回响：计算机工程领域的任何问题都可以通过一个中间层来解决。因此，中间码应运而生，即“字节码”（Bytecode）。</p></blockquote><p>Java之所以会这么流行，离不开它的一次编写，到处执行的跨平台的这个特性。不同平台的Java文件（.java），都会编译产生字节码文件（.class）,从字节码文件这一中间层屏蔽了上层操作系统（平台）的差异性，只要安装了Java的环境，都能运行出Java文件，能解决很多平台之间的运行问题。</p><h2 id="Java内存结构"><a href="#Java内存结构" class="headerlink" title="Java内存结构"></a>Java内存结构</h2><h3 id="堆"><a href="#堆" class="headerlink" title="堆"></a>堆</h3><p>堆分成了两大块，新生代和老年代（它们之间的占比为1:2）。新生代又分为三个区域Eden区和两个Survivor区（它们占比为8:1:1）。</p><p>首先新创建的对象会放在Eden区上，等Eden区满了放不下新创建的对象了，就会进行YGC，将Eden区中和其中一个Survivor区（from区）活着的对象放入另一个一个Survivor区（to区）。以此循环。</p><p>每一个对象都会有一个计数器，经历了一次YGC之后，对象的计数器就会加一，当到达了一定阈值，就会进入老年代（Java默认为15）。</p><p>新生代进入老年代的两种方法：</p><ol><li>对象的计数器到达了阈值，进入老年代。</li><li>Eden区放不下新创建的对象，进行了YGC之后也放不下，就会提前将该对象晋升为老年代（过早老化）。</li></ol><h3 id="方法区"><a href="#方法区" class="headerlink" title="方法区"></a>方法区</h3><p>方法区是JVM规定的一个区域，不是一个实在的区域，只是一个定义。</p><p>Java8之前是用永久代来实现了方法区，Java8之后是用元空间来实现方法区。</p><p>永久代包括了运行时常量池（比如String）和一些类元信息（类加载的文件，静态变量等）。</p><p>元空间只保存了类元信息，运行时常量池已经放入堆中进行存储。</p><p>不同与永久代，元空间在本地内存中分配。</p><h3 id="虚拟机栈和本地方法栈"><a href="#虚拟机栈和本地方法栈" class="headerlink" title="虚拟机栈和本地方法栈"></a>虚拟机栈和本地方法栈</h3><blockquote><p>​    虚拟机栈是描述Java方法执行的内存区域，它是线程私有的。栈中的元素用与支持虚拟机进行方法调用，每个方法从开始调用到执行完成的过程，就是栈帧从入栈到出栈的过程。</p></blockquote><p>可以说，栈帧就是一个方法执行过程的具体体现。</p><p>栈帧主要包括四个部分。</p><ol><li>局部变量表。存放方法参数和局部变量的区域。</li><li>操作栈。操作栈是一个初始状态为空的桶式结构栈。在方法执行过程中，会有各种指令往栈中写入和提取信息。</li><li>动态连接。每个栈中中包含一个在常量池中对当前方法的引用，目的是支持方法调用过程的动态连接。</li><li>方法返回地址。方法执行有两种退出情况。一，正常退出。二，异常退出。无论何种退出情况，都将返回至方法被调用的位置。</li></ol><blockquote><p>虚拟机栈主内，本地方法栈主外。这个内外是针对JVM来说的，本地方法栈为Native方法服务的。</p></blockquote><h3 id="程序计数寄存器"><a href="#程序计数寄存器" class="headerlink" title="程序计数寄存器"></a>程序计数寄存器</h3><blockquote><p>​    每个线程在创建后，都会产生自己的程序计数器和栈帧，程序计数器用来存放执行指令的偏移量和行号指示器等，线程执行或恢复都要依赖程序计数器。程序计数器在各个线程之间互不影响，此区域也不会发生内存溢出的异常。</p></blockquote><h2 id="try-catch-finally"><a href="#try-catch-finally" class="headerlink" title="try-catch-finally"></a>try-catch-finally</h2><blockquote><ol><li>try代码块<br>监视代码执行过程，一旦发现异常则直接跳转到catch，如果没有catch，就直接跳转到finally。</li><li><p>catch代码块<br>可选执行的代码块，如果没有任何异常发生则不会执行；如果发现异常则进行处理或向上抛出。</p></li><li><p>finally代码块<br>必选执行代码块，不管是否有异常产生，即使发售鞥OutOfMemoryError也会执行，通常用于处理善后清理工作。如果finally代码块没有执行，则会有三种情况。</p><ul><li>没有进入try代码块</li><li>进入try代码块，但是代码运行中出现了死循环或死锁状态</li><li>进入try代码块，但是执行了System.exit()操作</li></ul></li></ol></blockquote><h2 id="fail-fast机制"><a href="#fail-fast机制" class="headerlink" title="fail-fast机制"></a>fail-fast机制</h2><p>fail-fast机制是几何世界中比较常见的错误检测机制，通常出现在遍历集合元素的过程中。</p><p>java.util下的所有集合类都是fail-fase机制的。</p><p>比如在这些集合中，使用foreach遍历元素时进行删除，就会出现异常。</p><h2 id="ConcurrentHashMap"><a href="#ConcurrentHashMap" class="headerlink" title="ConcurrentHashMap"></a>ConcurrentHashMap</h2><blockquote><p>​    需要注意的时，无论是JDK7还是JDK8，ConcurrentHashMap的size()方法都只能返回一个大概数量，无法做到100%精确，因为已经统计过的槽在size()返回最终结果前可能又出现了变化，导致返回大小与实际大小存在些许差异。在多个槽的设计下，如果仅仅是为了统计元素数量而停下所有操作，又会显得因噎废食。因此，ConcurrentHashMap在涉及元素总数的相关更新和计算时，会最大限度地减少锁的使用，以减少线程间地竞争与互相等待。在这个设计思路上，JDK8的ConcurrentHashMap对元素总数的计算又做了进一步的优化，具体表现：在put()，remove()和size()方法中，涉及元素总数的更新和计算，都彻底避免了锁的使用，取而代之的是众多的CAS操作。</p></blockquote><h2 id="线程安全"><a href="#线程安全" class="headerlink" title="线程安全"></a>线程安全</h2><blockquote><p>线程安全问题只在多线程环境下才出现，单线程串行执行不存在此问题。保证高并发场景下的线程安全，可以从以下四个纬度考量</p><ol><li>数据单线程内可见。</li><li>只读对象</li><li>线程安全类</li><li>同步与锁机制</li></ol><p>线程安全的核心理念就是“要么只读，要么加锁”。</p></blockquote><p>该注意的是线程安全的主体是对象，而不是线程。</p><p>一般我们说线程安全的主语都是对象。</p><blockquote><p>Java并发编程实践中对线程安全的定义</p><p>​    当多个线程访问一个对象时，如果不用考虑这些线程在运行时环境下的调度和交替执行，也不需要进行额外的同步，或者在调用方进行任何其他的协调操作，调用这个对象的行为都可以获得正确的结果，那这个对象就是线程安全的。</p></blockquote><h2 id="volatile"><a href="#volatile" class="headerlink" title="volatile"></a>volatile</h2><blockquote><p>​    volatile的英文本已是“挥发，不稳定的”，延申意义为敏感的。当使用volatile修饰变量时，意味着任何对此变量的操作都会在内存中进行，不会产生副本，以保证共享变量的可见性，局部阻止了指令重排的发生。</p><p>​    “volatile是轻量级的同步方式”这种说法是错误的。它只是轻量级的线程操作可见方式，并非同步方式，如果是多写场景，一定会产生线程安全问题。</p></blockquote><p>volatile关键字的特性</p><ol><li>保证可见性</li><li>不保证原子性</li><li>禁止指令重排</li></ol><h2 id="读书感谢"><a href="#读书感谢" class="headerlink" title="读书感谢"></a>读书感谢</h2><p>这本书的前面半部分讲的通俗易懂，讲的很好，但是到后面有几章通过源码来进行讲解部分类与方法，这里就开始接触到部分工具的底层，如果没有一定的基础去看的话会很累，而且也会很繁琐。这些知识点很重要，作者想尽量写全，但是书的篇幅是有限的，所以就会感觉后面讲源码的部分看的体验就不是很好，有些地方也没有更深入的去讲。总的来说还是一本很好的书，适合学习Java的人观看，但是后面的一些知识点需要更加了解的话，就还需要通过其他途径，作者只是给各位读者开个头，了解一下，也是查缺补漏。最后孤尽老师还写了段话送给大家。</p><blockquote><p>​    最后，做一个有技术情怀的人。技术情怀总结成两个关键字：热爱，卓越。热爱是一种源动力，卓越是一种境界。兴趣是最好的老师，也是最好的动力。而热爱是一种信念，即使痛苦，也不会让你背离这份事业和内心的执着。对技术的热爱，让人用于归根究底，勇于坐冷板凳，勇于回馈别人。极致与卓越，似乎是一个意思，即出类拔萃，超出期望。技术情怀提倡我们追求极致式的卓越，把卓越在往前提升。不管一个人如何卓越与优秀，都要学会自我驱动，持续进步，追求个人内心的极致。因为卓越，所以经典，只有这样百尺竿头，才能更进一步。仰望星空的同时，是脚踏实地，这样才能不断地学习和打磨自己。</p></blockquote>]]></content:encoded>
            
            
            <category domain="https://yww52.com/categories/%E8%AF%BB%E4%B9%A6%E7%AC%94%E8%AE%B0/">读书笔记</category>
            
            
            
            <comments>https://yww52.com/posts/b93b977e/#disqus_thread</comments>
            
        </item>
        
        <item>
            <title>结果类封装</title>
            <link>https://yww52.com/posts/a3d4b987/</link>
            <guid>https://yww52.com/posts/a3d4b987/</guid>
            <pubDate>Fri, 21 May 2021 16:00:00 GMT</pubDate>
            
            <description>记录一下项目中常用的返回信息的封装类。</description>
            
            
            
            <content:encoded><![CDATA[<img src="https://img.yww52.com/2021/5/2021-5-22top_img.jpg" alt="结果类封装" style="max-width:100%;height:auto;margin-bottom:15px;"
  /><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></pre></td><td class="code"><pre><span class="line"><span class="keyword">package</span> com.yww.demo.util;</span><br><span class="line"></span><br><span class="line"><span class="comment">/**</span></span><br><span 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> yww</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_">ResultCode</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">    SUCCESS(<span class="number">200</span>, <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><span class="line"><span class="comment">     */</span></span><br><span class="line">    FAILED(<span class="number">500</span>, <span class="string">&quot;服务器发生错误&quot;</span>);</span><br><span class="line"></span><br><span class="line">    <span class="keyword">private</span> Integer status;</span><br><span class="line">    <span class="keyword">private</span> String message;</span><br><span class="line"></span><br><span class="line">    ResultCode(Integer status, String message) &#123;</span><br><span class="line">        <span class="built_in">this</span>.status = status;</span><br><span class="line">        <span class="built_in">this</span>.message = message;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">public</span> Integer <span class="title function_">getStatus</span><span class="params">()</span> &#123;</span><br><span class="line">        <span class="keyword">return</span> <span class="built_in">this</span>.status;</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_">getMessage</span><span class="params">()</span> &#123;</span><br><span class="line">        <span class="keyword">return</span> <span class="built_in">this</span>.message;</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><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><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></pre></td><td class="code"><pre><span class="line"><span class="keyword">package</span> com.yww.demo.util;</span><br><span class="line"></span><br><span class="line"><span class="keyword">import</span> lombok.Data;</span><br><span class="line"></span><br><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@ClassName</span> Result</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@Descriprtion</span> 结果类封装</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@Author</span> yww</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@Date</span> 2021/5/21 23:22</span></span><br><span class="line"><span class="comment"> **/</span></span><br><span class="line"><span class="meta">@Data</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">Result</span>&lt;T&gt; &#123;</span><br><span class="line"></span><br><span class="line">    <span class="comment">/**</span></span><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> Integer code;</span><br><span class="line"></span><br><span class="line">    <span class="comment">/**</span></span><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> String message;</span><br><span class="line"></span><br><span class="line">    <span class="comment">/**</span></span><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> T data;</span><br><span class="line"></span><br><span class="line">    <span class="comment">/**</span></span><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> <span class="title function_">Result</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="comment">     * <span class="doctag">@param</span> code    状态码</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 class="doctag">@param</span> data    返回数据</span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    <span class="keyword">public</span> <span class="title function_">Result</span><span class="params">(Integer code, String message, T data)</span> &#123;</span><br><span class="line">        <span class="built_in">this</span>.code = code;</span><br><span class="line">        <span class="built_in">this</span>.message = message;</span><br><span class="line">        <span class="built_in">this</span>.data = data;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     * 返回一个响应成功的信息，不带数据</span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">static</span> &lt;T&gt; Result&lt;T&gt; <span class="title function_">success</span><span class="params">()</span> &#123;</span><br><span class="line">        <span class="keyword">return</span> <span class="keyword">new</span> <span class="title class_">Result</span>&lt;&gt;(ResultCode.SUCCESS.getStatus(), ResultCode.SUCCESS.getMessage(), <span class="literal">null</span>);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     * 返回一个响应成功的信息，带数据</span></span><br><span class="line"><span class="comment">     *</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@param</span> data    返回数据</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> &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">        <span class="keyword">return</span> <span class="keyword">new</span> <span class="title class_">Result</span>&lt;&gt;(ResultCode.SUCCESS.getStatus(), ResultCode.SUCCESS.getMessage(), data);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     *  返回一个响应错误的信息</span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">static</span> &lt;T&gt; Result&lt;T&gt; <span class="title function_">failure</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>&lt;&gt;(ResultCode.FAILED.getStatus(), ResultCode.FAILED.getMessage(),<span class="literal">null</span>);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     *  返回一个响应错误的信息，可以自定义错误信息</span></span><br><span class="line"><span class="comment">     *  <span 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">static</span> &lt;T&gt; Result&lt;T&gt; <span class="title function_">failure</span><span class="params">(String message)</span> &#123;</span><br><span class="line">        <span class="keyword">return</span> <span class="keyword">new</span> <span class="title class_">Result</span>&lt;&gt;(ResultCode.FAILED.getStatus(), message,<span class="literal">null</span>);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     *  根据结果状态码，返回一个响应错误的信息</span></span><br><span class="line"><span class="comment">     *  <span class="doctag">@param</span> resultCode  状态码</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> &lt;T&gt; Result&lt;T&gt; <span class="title function_">failure</span><span class="params">(ResultCode resultCode)</span> &#123;</span><br><span class="line">        <span class="keyword">return</span> <span class="keyword">new</span> <span class="title class_">Result</span>&lt;&gt;(resultCode.getStatus(), resultCode.getMessage(),<span class="literal">null</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>]]></content:encoded>
            
            
            <category domain="https://yww52.com/categories/%E6%88%91%E7%9A%84%E6%8A%80%E6%9C%AF%E5%AD%A6%E4%B9%A0%E7%89%A9%E8%AF%AD%E6%9E%9C%E7%84%B6%E6%9C%89%E9%97%AE%E9%A2%98/">我的技术学习物语果然有问题</category>
            
            <category domain="https://yww52.com/categories/%E6%88%91%E7%9A%84%E6%8A%80%E6%9C%AF%E5%AD%A6%E4%B9%A0%E7%89%A9%E8%AF%AD%E6%9E%9C%E7%84%B6%E6%9C%89%E9%97%AE%E9%A2%98/%E5%BC%80%E5%8F%91%E6%8A%80%E5%B7%A7/">开发技巧</category>
            
            
            
            <comments>https://yww52.com/posts/a3d4b987/#disqus_thread</comments>
            
        </item>
        
        <item>
            <title>背包专题</title>
            <link>https://yww52.com/posts/4f2ce235/</link>
            <guid>https://yww52.com/posts/4f2ce235/</guid>
            <pubDate>Sun, 18 Apr 2021 16:00:00 GMT</pubDate>
            
            <description>记录一下学习背包问题时的思路和解法</description>
            
            
            
            <content:encoded><![CDATA[<img src="https://img.yww52.com/2021/4/2021-4-19top_img.jpg" alt="背包专题" style="max-width:100%;height:auto;margin-bottom:15px;"
  /><h1 id="01背包"><a href="#01背包" class="headerlink" title="01背包"></a>01背包</h1><blockquote><p>有 N 件物品和一个容量是 V 的背包。每件物品只能使用一次。</p><p>第 i 件物品的体积是 v[i]，价值是 w[i]。</p><p>求解将哪些物品装入背包，可使这些物品的总体积不超过背包容量，且总价值最大。<br>输出最大价值。</p></blockquote><p>01背部的特征是每个物品都只有一件，要么选择装进背包要么选择不装进背部。</p><p>首先是状态表示，一般都能想到的是二维数组来表示状态<code>dp[i][j]</code>，这个应该表示的是从前<code>i</code>个物品中选取，总体积是<code>j</code>的情况下，总价值最大是多少。</p><p>那么可以知道这道题的答案就是<code>dp[N][V]</code>了。</p><p>然后是状态转移了。</p><p>从题目就可以看出选择的方案，或者说是状态转移的方案。</p><ol><li><p>不选择第<code>i</code>件物品，即可以理解为从第<code>i - 1</code>件物品中选择，不选择第<code>i</code>件物品了，背包容量不变。</p><p><code>dp[i][j] = dp[i - 1][j]</code></p></li><li><p>选择第<code>i</code>件物品，即可以理解选择了第<code>i - 1</code>件物品之后，从第<code>i - 1</code>件物品中选择，背包容量变为原容量减去第<code>i</code>件物品的体积，这个方案的前提是当前背包容量大于物品的体积。</p><p><code>dp[i][j] = dp[i - 1][j - v[i]] + w[i]</code></p></li></ol><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></pre></td><td class="code"><pre><span class="line"><span class="keyword">import</span> java.util.Scanner;</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 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">Scanner</span> <span class="variable">sc</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">Scanner</span>(System.in);</span><br><span class="line">        <span class="type">int</span> <span class="variable">N</span> <span class="operator">=</span> sc.nextInt();</span><br><span class="line">        <span class="type">int</span> <span class="variable">V</span> <span class="operator">=</span> sc.nextInt();</span><br><span class="line">        <span class="type">int</span>[] v = <span class="keyword">new</span> <span class="title class_">int</span>[N + <span class="number">1</span>];</span><br><span class="line">        <span class="type">int</span>[] w = <span class="keyword">new</span> <span class="title class_">int</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">1</span>; i &lt;= N; i++) &#123;</span><br><span class="line">            v[i] = sc.nextInt();</span><br><span class="line">            w[i] = sc.nextInt();</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>[][] dp = <span class="keyword">new</span> <span class="title class_">int</span>[N + <span class="number">1</span>][V + <span class="number">1</span>];</span><br><span class="line">        <span class="comment">// 物品是需要全部遍历的，所以是从第一件物品遍历到第N件物品</span></span><br><span class="line">        <span class="keyword">for</span> (<span class="type">int</span> <span class="variable">i</span> <span class="operator">=</span> <span class="number">1</span>; i &lt;= N; i++) &#123;</span><br><span class="line">            <span class="comment">// 体积是需要从0遍历到背包的体积</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;= V; j++) &#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="keyword">if</span>(j &gt;= v[i])&#123;</span><br><span class="line">                    dp[i][j] = Math.max(dp[i - <span class="number">1</span>][j], dp[i - <span class="number">1</span>][j - v[i]] + w[i]);</span><br><span class="line">                &#125;<span class="keyword">else</span>&#123;</span><br><span class="line">                    dp[i][j] = dp[i - <span class="number">1</span>][j];</span><br><span class="line">                &#125;</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;</span><br><span class="line">        System.out.println(dp[N][V]);</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>优化dp数组。</p><p>这里使用的是二维的dp数组表示状态，但其实可以优化成一维的数组。</p><p>仔细观察这两个方程的状态转移方程。</p><ol><li><code>dp[i][j] = dp[i - 1][j]</code></li><li><code>dp[i][j] = dp[i - 1][j - v[i]] + w[i]</code></li></ol><p>第一个很好理解，将第<code>i - 1</code>的层<code>j</code>状态转移到第<code>i</code>层，但因为总体积是不变的，即<code>j</code>是不变的，所以这个转移相当于没有意义，所以遍历了<code>i</code>层这个转移就自动成立了。</p><p>第二个就会有点麻烦。当我们想要更新当前的状态，那么就需要第<code>i -1</code>层的<code>[j - v[i]] + w[i]</code>数据来更新，如果我们直接使用<code>d[j] = dp[j - v[i]] + w[i]</code>是不对的。</p><p>这个式子其实等价于<code>dp[i][j] = dp[i][j - v[i]] + w[i]</code>，与之前的状态转移是不一样的，所以是不对的，要想这个式子与之前的状态转移方程等价，将体积从<code>V</code>到0遍历就可以了。</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><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">for</span> (<span class="type">int</span> <span class="variable">i</span> <span class="operator">=</span> <span class="number">1</span>; i &lt;= N; i++) &#123;</span><br><span class="line">           <span class="keyword">for</span> (<span class="type">int</span> <span class="variable">j</span> <span class="operator">=</span> V; j &gt;= <span class="number">0</span>; j--) &#123;</span><br><span class="line">               <span class="comment">// 这个条件放到for循环上</span></span><br><span class="line">               <span class="keyword">if</span>(j &gt;= v[i])&#123;</span><br><span class="line">                   dp[j] = Math.max(dp[j], dp[j-v[i]] + w[i]);</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">                   dp[j] = dp[j];</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></pre></td><td class="code"><pre><span class="line"><span class="keyword">import</span> java.util.Scanner;</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 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">Scanner</span> <span class="variable">sc</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">Scanner</span>(System.in);</span><br><span class="line">        <span class="type">int</span> <span class="variable">N</span> <span class="operator">=</span> sc.nextInt();</span><br><span class="line">        <span class="type">int</span> <span class="variable">V</span> <span class="operator">=</span> sc.nextInt();</span><br><span class="line">        <span class="type">int</span>[] v = <span class="keyword">new</span> <span class="title class_">int</span>[N + <span class="number">1</span>];</span><br><span class="line">        <span class="type">int</span>[] w = <span class="keyword">new</span> <span class="title class_">int</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">1</span>; i &lt;= N; i++) &#123;</span><br><span class="line">            v[i] = sc.nextInt();</span><br><span class="line">            w[i] = sc.nextInt();</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="type">int</span>[] dp = <span class="keyword">new</span> <span class="title class_">int</span>[V + <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">1</span>; i &lt;= N; i++) &#123;</span><br><span class="line">            <span class="keyword">for</span> (<span class="type">int</span> <span class="variable">j</span> <span class="operator">=</span> V; j &gt;= v[i]; j--) &#123;</span><br><span class="line">                    dp[j] = Math.max(dp[j], dp[j-v[i]] + w[i]);</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;</span><br><span class="line">        System.out.println(dp[V]);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>为什么逆序遍历可以让上述两个式子等价呢？</p><p>遍历第<code>i</code>层的时候，<code>dp[j-v[i]]</code>会用到之前的状态，这个式子其实等价于<code>dp[i][j-v[i]]</code>，在同一层里，用到了之前的状态，而不是上一层<code>i - 1</code>的状态，这就会导致<code>i</code>这个物品是被用到了第二次的，而01背包是每个物品只能用一次的，所以这个式子是不对的，逆序遍历体积容量的话就不会用到被<code>污染</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><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><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">import</span> java.util.Scanner;</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 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">Scanner</span> <span class="variable">sc</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">Scanner</span>(System.in);</span><br><span class="line">        <span class="type">int</span> <span class="variable">N</span> <span class="operator">=</span> sc.nextInt();</span><br><span class="line">        <span class="type">int</span> <span class="variable">V</span> <span class="operator">=</span> sc.nextInt();</span><br><span class="line">        <span class="type">int</span>[] dp = <span class="keyword">new</span> <span class="title class_">int</span>[V + <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">1</span>; i &lt;= N; i++) &#123;</span><br><span class="line">            <span class="type">int</span> <span class="variable">vi</span> <span class="operator">=</span> sc.nextInt();</span><br><span class="line">            <span class="type">int</span> <span class="variable">wi</span> <span class="operator">=</span> sc.nextInt();</span><br><span class="line">            <span class="keyword">for</span> (<span class="type">int</span> <span class="variable">j</span> <span class="operator">=</span> V; j &gt;= vi; j--) &#123;</span><br><span class="line">                    dp[j] = Math.max(dp[j], dp[j-vi] + wi);</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;</span><br><span class="line">        System.out.println(dp[V]);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h1 id="完全背包"><a href="#完全背包" class="headerlink" title="完全背包"></a>完全背包</h1><blockquote><p>有 N 种物品和一个容量是 V 的背包，每种物品都有无限件可用。</p><p>第 i 种物品的体积是 vi，价值是 wi。</p><p>求解将哪些物品装入背包，可使这些物品的总体积不超过背包容量，且总价值最大。<br>输出最大价值。</p></blockquote><p>完全背包与01背包不一样的是完全背包是可以选无限件的。</p><p>所以完全背包与01背包分析不一样的就是状态的转移方案。</p><p>01背包问题里，是有两种方案的，选一个或者是不选，完全背包就会有很多种方案。</p><ol><li><p>不选第<code>i</code>件物品</p><p><code>dp[i][j] = dp[i - 1][j]</code></p></li><li><p>选择<code>k</code>件第<code>i</code>个物品，容量要大于体积。</p><p><code>dp[i][j] = dp[i - 1][j - v[i]] + w[i]</code></p></li></ol><p>跟01背包差不多，直接遍历所有第<code>i</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="keyword">import</span> java.util.Scanner;</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 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">Scanner</span> <span class="variable">sc</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">Scanner</span>(System.in);</span><br><span class="line">        <span class="type">int</span> <span class="variable">N</span> <span class="operator">=</span> sc.nextInt();</span><br><span class="line">        <span class="type">int</span> <span class="variable">V</span> <span class="operator">=</span> sc.nextInt();</span><br><span class="line">        <span class="type">int</span>[] v = <span class="keyword">new</span> <span class="title class_">int</span>[N + <span class="number">1</span>];</span><br><span class="line">        <span class="type">int</span>[] w = <span class="keyword">new</span> <span class="title class_">int</span>[N + <span class="number">1</span>];</span><br><span class="line">        <span class="type">int</span>[][] dp = <span class="keyword">new</span> <span class="title class_">int</span>[N + <span class="number">1</span>][V + <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">1</span>; i &lt;= N; i++) &#123;</span><br><span class="line">            v[i] = sc.nextInt();</span><br><span class="line">            w[i] = sc.nextInt();</span><br><span class="line">        &#125;</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;= N; i++) &#123;</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;= V; j++) &#123;</span><br><span class="line">                <span class="comment">// 遍历选择第i件物品的选项</span></span><br><span class="line">                <span class="keyword">for</span>(<span class="type">int</span> <span class="variable">k</span> <span class="operator">=</span> <span class="number">0</span> ; k * v[i] &lt;= j ; k++) &#123;</span><br><span class="line">                    dp[i][j] = Math.max(dp[i][j], dp[i - <span class="number">1</span>][j - k * v[i]] + k * w[i]);</span><br><span class="line">                &#125;</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;</span><br><span class="line">        System.out.println(dp[N][V]);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>这里用到了三重循环，这样的话时间复杂度会很高，所以应该要考虑进行优化。</p><p>仔细观察一下这个状态转移方程。</p><p><code>dp[i][j] = max(dp[i - 1][j], dp[i - 1][j - v] + w, dp[i - 1][j - 2v] + 2w....)</code></p><p>我们求<code>dp[i][j]</code>的最大值也就是求后面这些方案的最大值，我们可以注意到</p><p><code>dp[i][j - v] = max(dp[i - 1][j - v], dp[i - 1][j - 2v] + w)....</code></p><p>所以通过二式我们可以求出一式后面的除了第一项的最大值。</p><p>故进行优化之后的状态状态方程为</p><p><code>dp[i][j] = max(dp[i - 1][j], dp[i][j - v] + w)</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><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><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">import</span> java.util.Scanner;</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 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">Scanner</span> <span class="variable">sc</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">Scanner</span>(System.in);</span><br><span class="line">        <span class="type">int</span> <span class="variable">N</span> <span class="operator">=</span> sc.nextInt();</span><br><span class="line">        <span class="type">int</span> <span class="variable">V</span> <span class="operator">=</span> sc.nextInt();</span><br><span class="line">        <span class="type">int</span>[] v = <span class="keyword">new</span> <span class="title class_">int</span>[N + <span class="number">1</span>];</span><br><span class="line">        <span class="type">int</span>[] w = <span class="keyword">new</span> <span class="title class_">int</span>[N + <span class="number">1</span>];</span><br><span class="line">        <span class="type">int</span>[][] dp = <span class="keyword">new</span> <span class="title class_">int</span>[N + <span class="number">1</span>][V + <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">1</span>; i &lt;= N; i++) &#123;</span><br><span class="line">            v[i] = sc.nextInt();</span><br><span class="line">            w[i] = sc.nextInt();</span><br><span class="line">        &#125;</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;= N; i++) &#123;</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;= V; j++) &#123;</span><br><span class="line">                dp[i][j] = dp[i - <span class="number">1</span>][j];</span><br><span class="line">                <span class="keyword">if</span> (j &gt;= v[i]) &#123;</span><br><span class="line">                    dp[i][j] = Math.max(dp[i][j], dp[i][j - v[i]] + w[i]);</span><br><span class="line">                &#125;</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;</span><br><span class="line">        System.out.println(dp[N][V]);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>变成这样会发现和01背包十分相似，所以也可以根据01背包的优化思路进行优化，先将该状态数组优化为一维。</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><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">import</span> java.util.Scanner;</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 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">Scanner</span> <span class="variable">sc</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">Scanner</span>(System.in);</span><br><span class="line">        <span class="type">int</span> <span class="variable">N</span> <span class="operator">=</span> sc.nextInt();</span><br><span class="line">        <span class="type">int</span> <span class="variable">V</span> <span class="operator">=</span> sc.nextInt();</span><br><span class="line">        <span class="type">int</span>[] v = <span class="keyword">new</span> <span class="title class_">int</span>[N + <span class="number">1</span>];</span><br><span class="line">        <span class="type">int</span>[] w = <span class="keyword">new</span> <span class="title class_">int</span>[N + <span class="number">1</span>];</span><br><span class="line">        <span class="type">int</span>[] dp = <span class="keyword">new</span> <span class="title class_">int</span>[V + <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">1</span>; i &lt;= N; i++) &#123;</span><br><span class="line">            v[i] = sc.nextInt();</span><br><span class="line">            w[i] = sc.nextInt();</span><br><span class="line">        &#125;</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;= N; i++) &#123;</span><br><span class="line">            <span class="comment">// 这里用正序遍历是可以实现的，01背包需要逆序</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> v[i]; j &lt;= V; j++) &#123;</span><br><span class="line">                dp[j] = Math.max(dp[j], dp[j - v[i]] + w[i]);</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;</span><br><span class="line">        System.out.println(dp[V]);</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></pre></td><td class="code"><pre><span class="line"><span class="keyword">import</span> java.util.Scanner;</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 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">Scanner</span> <span class="variable">sc</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">Scanner</span>(System.in);</span><br><span class="line">        <span class="type">int</span> <span class="variable">N</span> <span class="operator">=</span> sc.nextInt();</span><br><span class="line">        <span class="type">int</span> <span class="variable">V</span> <span class="operator">=</span> sc.nextInt();</span><br><span class="line">        <span class="type">int</span>[] dp = <span class="keyword">new</span> <span class="title class_">int</span>[V + <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">1</span>; i &lt;= N; i++) &#123;</span><br><span class="line">            <span class="type">int</span> <span class="variable">v</span> <span class="operator">=</span> sc.nextInt();</span><br><span class="line">            <span class="type">int</span> <span class="variable">w</span> <span class="operator">=</span> sc.nextInt();</span><br><span class="line">            <span class="keyword">for</span> (<span class="type">int</span> <span class="variable">j</span> <span class="operator">=</span> v; j &lt;= V; j++) &#123;</span><br><span class="line">                dp[j] = Math.max(dp[j],dp[j - v] + w);</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;</span><br><span class="line">        System.out.println(dp[V]);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h1 id="多重背包"><a href="#多重背包" class="headerlink" title="多重背包"></a>多重背包</h1><blockquote><p>有 N 种物品和一个容量是 V 的背包。</p><p>第 i 种物品最多有 si 件，每件体积是 vi，价值是 wi。</p><p>求解将哪些物品装入背包，可使物品体积总和不超过背包容量，且价值总和最大。<br>输出最大价值。</p></blockquote><p>多重背包跟完全背包不一样的点是多重背包的每件物品是有个数限制的，不过大体是和完全背包差不多的解题思路。</p><h2 id="暴力三重循环"><a href="#暴力三重循环" class="headerlink" title="暴力三重循环"></a>暴力三重循环</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><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">import</span> java.util.Scanner;</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 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">Scanner</span> <span class="variable">sc</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">Scanner</span>(System.in);</span><br><span class="line">        <span class="type">int</span> <span class="variable">N</span> <span class="operator">=</span> sc.nextInt();</span><br><span class="line">        <span class="type">int</span> <span class="variable">V</span> <span class="operator">=</span> sc.nextInt();</span><br><span class="line">        <span class="type">int</span>[] v = <span class="keyword">new</span> <span class="title class_">int</span>[N + <span class="number">1</span>];</span><br><span class="line">        <span class="type">int</span>[] w = <span class="keyword">new</span> <span class="title class_">int</span>[N + <span class="number">1</span>];</span><br><span class="line">        <span class="type">int</span>[] s = <span class="keyword">new</span> <span class="title class_">int</span>[N + <span class="number">1</span>];</span><br><span class="line">        <span class="type">int</span>[][] dp = <span class="keyword">new</span> <span class="title class_">int</span>[N + <span class="number">1</span>][V + <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">1</span>; i &lt;= N; i++) &#123;</span><br><span class="line">            v[i] = sc.nextInt();</span><br><span class="line">            w[i] = sc.nextInt();</span><br><span class="line">            s[i] = sc.nextInt();</span><br><span class="line">        &#125;</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;= N; i++) &#123;</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;= V; j++) &#123;</span><br><span class="line">                <span class="comment">// 加多一个条件，个数小于或等于限制的条件</span></span><br><span class="line">                <span class="keyword">for</span>(<span class="type">int</span> <span class="variable">k</span> <span class="operator">=</span> <span class="number">0</span> ; k * v[i] &lt;= j &amp;&amp; k &lt;= s[i] ; k++) &#123;</span><br><span class="line">                    dp[i][j] = Math.max(dp[i][j], dp[i - <span class="number">1</span>][j - k * v[i]] + k * w[i]);</span><br><span class="line">                &#125;</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;</span><br><span class="line">        System.out.println(dp[N][V]);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>三重循环的时间复杂度为O(n^3)，很大概率会超时，所以要进行优化才行。</p><h2 id="二进制优化"><a href="#二进制优化" class="headerlink" title="二进制优化"></a>二进制优化</h2><p>其实多重背包可以转换成01背包的问题，只要将背包的个数拆分开来就可以了。</p><p>最容易想到的是将<code>n</code>件商品拆分成<code>n</code>个商品，这样将所有商品个数大于1的商品全部拆分，就可以理解为01背包的问题了，选或者是不选。</p><p>但是这种方案是行不通的。因为将<code>n</code>件商品拆分成<code>n</code>个，时间复杂度依旧很大，依然很容易超时，所以我们得考虑出另一种拆分方法，怎样拆分能用拆分后得数字表示出<code>0 ~ n</code>呢？</p><p>这就用到了一种二进制的拆分方法。</p><p>将数字转换成类似二进制的表示方法。</p><blockquote><p>比如要拆分9这个数字</p><p>我们可以拆分为 1 + 2 + 4 + 2</p><p>最后一个数小于2的n次方就使用剩余的数</p><p>这样多重背包的一个9件的商品问题就可以拆分为01背包的四个不同的商品的问题</p><p>使用这四个数选或者不选两种方案就能遍历出0 ~ 9十种方案</p></blockquote><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">import</span> java.util.*;</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 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">Scanner</span> <span class="variable">sc</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">Scanner</span>(System.in);</span><br><span class="line">        <span class="type">int</span> <span class="variable">N</span> <span class="operator">=</span> sc.nextInt();</span><br><span class="line">        <span class="type">int</span> <span class="variable">V</span> <span class="operator">=</span> sc.nextInt();</span><br><span class="line">        List&lt;Integer&gt; vi = <span class="keyword">new</span> <span class="title class_">ArrayList</span>&lt;&gt;();</span><br><span class="line">        List&lt;Integer&gt; wi = <span class="keyword">new</span> <span class="title class_">ArrayList</span>&lt;&gt;();</span><br><span class="line">        <span class="comment">// 将多重背包拆分成01背包</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">            <span class="type">int</span> <span class="variable">v</span> <span class="operator">=</span> sc.nextInt();</span><br><span class="line">            <span class="type">int</span> <span class="variable">w</span> <span class="operator">=</span> sc.nextInt();</span><br><span class="line">            <span class="type">int</span> <span class="variable">s</span> <span class="operator">=</span> sc.nextInt();</span><br><span class="line">            <span class="keyword">for</span> (<span class="type">int</span> <span class="variable">k</span> <span class="operator">=</span> <span class="number">1</span>; k &lt; s; k *= <span class="number">2</span>) &#123;</span><br><span class="line">                vi.add(k * v);</span><br><span class="line">                wi.add(k * w);</span><br><span class="line">                s = s - k;</span><br><span class="line">            &#125;</span><br><span class="line">            <span class="keyword">if</span> (s &gt; <span class="number">0</span>) &#123;</span><br><span class="line">                vi.add(s * v);</span><br><span class="line">                wi.add(s * w);</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="comment">// 接下来是01背包解法</span></span><br><span class="line">        <span class="type">int</span>[] dp = <span class="keyword">new</span> <span class="title class_">int</span>[V + <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; vi.size(); i++) &#123;</span><br><span class="line">            <span class="keyword">for</span> (<span class="type">int</span> <span class="variable">j</span> <span class="operator">=</span> V; j &gt;= vi.get(i); j--) &#123;</span><br><span class="line">                dp[j] = Math.max(dp[j],dp[j - vi.get(i)] + wi.get(i));</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;</span><br><span class="line">        System.out.println(dp[V]);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h1 id="参考链接"><a href="#参考链接" class="headerlink" title="参考链接"></a>参考链接</h1><ul><li><a href="https://github.com/tianyicui/pack">https://github.com/tianyicui/pack</a></li><li><a href="https://www.acwing.com/problem/">https://www.acwing.com/problem/</a></li></ul>]]></content:encoded>
            
            
            <category domain="https://yww52.com/categories/%E6%88%91%E7%9A%84%E6%8A%80%E6%9C%AF%E5%AD%A6%E4%B9%A0%E7%89%A9%E8%AF%AD%E6%9E%9C%E7%84%B6%E6%9C%89%E9%97%AE%E9%A2%98/">我的技术学习物语果然有问题</category>
            
            <category domain="https://yww52.com/categories/%E6%88%91%E7%9A%84%E6%8A%80%E6%9C%AF%E5%AD%A6%E4%B9%A0%E7%89%A9%E8%AF%AD%E6%9E%9C%E7%84%B6%E6%9C%89%E9%97%AE%E9%A2%98/%E7%AE%97%E6%B3%95/">算法</category>
            
            
            
            <comments>https://yww52.com/posts/4f2ce235/#disqus_thread</comments>
            
        </item>
        
        <item>
            <title>整合Redis</title>
            <link>https://yww52.com/posts/3cd0c9b6/</link>
            <guid>https://yww52.com/posts/3cd0c9b6/</guid>
            <pubDate>Thu, 01 Apr 2021 16:00:00 GMT</pubDate>
            
            <description>SpringBoot整合Redis的Demo</description>
            
            
            
            <content:encoded><![CDATA[<img src="https://img.yww52.com/2021/4/2021-4-2top_img.jpg" alt="整合Redis" style="max-width:100%;height:auto;margin-bottom:15px;"
  /><h1 id="Jedis"><a href="#Jedis" class="headerlink" title="Jedis"></a>Jedis</h1><p>Jedis是官网推荐的Java连接工具，类似就是在Java和Redis中增加一层辅助层，辅助你用Java使用Redis。</p><p>简单的测试一下使用。</p><p>首先创建一个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><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;!-- Jedis --&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>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">version</span>&gt;</span>3.5.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>连接使用Redis</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">package</span> com.yww;</span><br><span class="line"><span class="keyword">import</span> redis.clients.jedis.Jedis;</span><br><span class="line"></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">config</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">// 创建Jedis对象，连接Redis,jedis对象相当于客户端</span></span><br><span class="line">        <span class="comment">// Redis命令就是使用该对象的方法</span></span><br><span class="line">        <span class="type">Jedis</span> <span class="variable">jedis</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">Jedis</span>(<span class="string">&quot;127.0.0.1&quot;</span>, <span class="number">6379</span>);</span><br><span class="line">        <span class="comment">// 测试连接</span></span><br><span class="line">        System.out.println(jedis.ping());</span><br><span class="line">        <span class="comment">// 关闭连接</span></span><br><span class="line">        jedis.close();</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><span class="line">26</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">package</span> com.yww;</span><br><span class="line"><span class="keyword">import</span> redis.clients.jedis.Jedis;</span><br><span class="line"><span class="keyword">import</span> redis.clients.jedis.Transaction;</span><br><span class="line"></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">config</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">Jedis</span> <span class="variable">jedis</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">Jedis</span>(<span class="string">&quot;127.0.0.1&quot;</span>, <span class="number">6379</span>);</span><br><span class="line"></span><br><span class="line">        <span class="comment">// 创建事务</span></span><br><span class="line">        <span class="type">Transaction</span> <span class="variable">multi</span> <span class="operator">=</span> jedis.multi();</span><br><span class="line">        <span class="keyword">try</span>&#123;</span><br><span class="line"></span><br><span class="line">            <span class="comment">// 组成事务的命令</span></span><br><span class="line"></span><br><span class="line">            <span class="comment">// 执行事务</span></span><br><span class="line">            multi.exec();</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">            multi.discard();</span><br><span class="line">            e.printStackTrace();</span><br><span class="line">        &#125; <span class="keyword">finally</span> &#123;</span><br><span class="line">            <span class="comment">// 关闭连接</span></span><br><span class="line">            jedis.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><h1 id="Lettuce"><a href="#Lettuce" class="headerlink" title="Lettuce"></a>Lettuce</h1><p>整合就先创建一个SpringBoot的项目，导入SpringData项目的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>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">version</span>&gt;</span>2.4.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><div class="note info flat"><p>在比较新的SpringBoot版本中，放弃了Jedis对redis的直接连接，因为多个线程会出现线程不安全的情况，所以官方已经替换成了lettuce。<br>lettuce采用netty，实例可以在多个线程中进行共享，减少线程数量，不存在线程不安全的情况。</p></div><p>在配置文件中，配置Redis的连接。</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></pre></td><td class="code"><pre><span class="line"><span class="comment"># 配置Redis</span></span><br><span class="line"><span class="attr">spring.redis.host</span>=<span class="string">127.0.0.1</span></span><br><span class="line"><span class="attr">spring.redis.port</span>=<span class="string">6379</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><span class="line">34</span><br><span class="line">35</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">package</span> com.yww;</span><br><span class="line"></span><br><span class="line"><span class="keyword">import</span> org.junit.jupiter.api.Test;</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.boot.test.context.SpringBootTest;</span><br><span class="line"><span class="keyword">import</span> org.springframework.data.redis.core.RedisTemplate;</span><br><span class="line"></span><br><span class="line"><span class="meta">@SpringBootTest</span></span><br><span class="line"><span class="keyword">class</span> <span class="title class_">RedisTestApplicationTests</span> &#123;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Autowired</span></span><br><span class="line">    <span class="keyword">private</span> RedisTemplate redisTemplate;</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">        <span class="comment">/*</span></span><br><span class="line"><span class="comment">        *       数据类型的操作</span></span><br><span class="line"><span class="comment">        *   1. opsForValue  操作字符串</span></span><br><span class="line"><span class="comment">        *   2. opsForList   操作list</span></span><br><span class="line"><span class="comment">        *   3. opsForSet    操作set</span></span><br><span class="line"><span class="comment">        *   4. opsForHash   操作hash</span></span><br><span class="line"><span class="comment">        *   5. opsForZSet   操作Zest</span></span><br><span class="line"><span class="comment">        *   6. opsForGeo    操作geospatial</span></span><br><span class="line"><span class="comment">        *   7. opsForHyperLogLog    操作Hyperloglog</span></span><br><span class="line"><span class="comment">        *</span></span><br><span class="line"><span class="comment">        */</span></span><br><span class="line">        redisTemplate.opsForValue().set(<span class="string">&quot;name&quot;</span>,<span class="string">&quot;yww&quot;</span>);</span><br><span class="line">        System.out.println(redisTemplate.opsForValue().get(<span class="string">&quot;name&quot;</span>));</span><br><span class="line"></span><br><span class="line">        <span class="comment">// 获取Redis的连接对象</span></span><br><span class="line">        <span class="type">RedisConnection</span> <span class="variable">connection</span> <span class="operator">=</span> redisTemplate.getConnectionFactory().getConnection();</span><br><span class="line">        connection.flushDb();</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>先简单的创建一个对象来测试。</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><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.yww;</span><br><span class="line"></span><br><span class="line"><span class="keyword">import</span> lombok.AllArgsConstructor;</span><br><span class="line"><span class="keyword">import</span> lombok.Data;</span><br><span class="line"><span class="keyword">import</span> lombok.NoArgsConstructor;</span><br><span class="line"><span class="keyword">import</span> 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">@AllArgsConstructor</span></span><br><span class="line"><span class="meta">@NoArgsConstructor</span></span><br><span class="line"><span class="meta">@Data</span></span><br><span class="line"><span class="comment">// 实现Serializable序列化</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 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">&#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><span class="line">21</span><br><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.yww;</span><br><span class="line"></span><br><span class="line"><span class="keyword">import</span> com.fasterxml.jackson.core.JsonProcessingException;</span><br><span class="line"><span class="keyword">import</span> com.fasterxml.jackson.databind.ObjectMapper;</span><br><span class="line"><span class="keyword">import</span> org.junit.jupiter.api.Test;</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.boot.test.context.SpringBootTest;</span><br><span class="line"><span class="keyword">import</span> org.springframework.data.redis.connection.RedisConnection;</span><br><span class="line"><span class="keyword">import</span> org.springframework.data.redis.core.RedisTemplate;</span><br><span class="line"></span><br><span class="line"><span class="meta">@SpringBootTest</span></span><br><span class="line"><span class="keyword">class</span> <span class="title class_">RedisTestApplicationTests</span> &#123;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Autowired</span></span><br><span class="line">    <span class="keyword">private</span> RedisTemplate redisTemplate;</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> <span class="keyword">throws</span> JsonProcessingException &#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;yww&quot;</span>, <span class="number">20</span>);</span><br><span class="line">        <span class="comment">//  转换为JSON字符串，传输的对象要经过序列化，不然会报错</span></span><br><span class="line">        <span class="type">String</span> <span class="variable">json</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">ObjectMapper</span>().writeValueAsString(user);</span><br><span class="line">        redisTemplate.opsForValue().set(<span class="string">&quot;user&quot;</span>,json);</span><br><span class="line">        System.out.println(redisTemplate.opsForValue().get(<span class="string">&quot;user&quot;</span>));</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><div class="note info flat"><p>默认是会使用jdk的序列化方式，在后期使用会出现一些麻烦，所以开发中一般会自己定义RedisTemplate的序列化方式，所以就需要自己定义一个RedisTemplate来使用。</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><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * &lt;p&gt;</span></span><br><span class="line"><span class="comment"> *     Redis的配置类</span></span><br><span class="line"><span class="comment"> * &lt;/p&gt;</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@ClassName</span> RedisConfig</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@Author</span> yww</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="meta">@Configuration</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">RedisConfig</span> <span class="keyword">extends</span> <span class="title class_">CachingConfigurationSelector</span> &#123;</span><br><span class="line"></span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     * 自定义RedisTemplate</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@return</span> RedisTemplate</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="meta">@SuppressWarnings(value = &#123; &quot;unchecked&quot;, &quot;rawtypes&quot;, &quot;deprecation&quot; &#125;)</span></span><br><span class="line">    <span class="keyword">public</span> RedisTemplate&lt;Object, Object&gt; <span class="title function_">redisTemplate</span><span class="params">(RedisConnectionFactory redisConnectionFactory)</span> &#123;</span><br><span class="line">        RedisTemplate&lt;Object, Object&gt; redisTemplate = <span class="keyword">new</span> <span class="title class_">RedisTemplate</span>();</span><br><span class="line">        redisTemplate.setConnectionFactory(redisConnectionFactory);</span><br><span class="line">        <span class="type">Jackson2JsonRedisSerializer</span> <span class="variable">jackson2JsonRedisSerializer</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">Jackson2JsonRedisSerializer</span>(Object.class);</span><br><span class="line">        <span class="type">ObjectMapper</span> <span class="variable">mapper</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">ObjectMapper</span>();</span><br><span class="line">        mapper.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);</span><br><span class="line">        mapper.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);</span><br><span class="line">        jackson2JsonRedisSerializer.setObjectMapper(mapper);</span><br><span class="line">        <span class="type">StringRedisSerializer</span> <span class="variable">stringRedisSerializer</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">StringRedisSerializer</span>();</span><br><span class="line">        <span class="comment">// key采用String的序列化方式</span></span><br><span class="line">        redisTemplate.setKeySerializer(stringRedisSerializer);</span><br><span class="line">        <span class="comment">// hash的key也采用String的序列化方式</span></span><br><span class="line">        redisTemplate.setHashKeySerializer(stringRedisSerializer);</span><br><span class="line">        <span class="comment">// value序列化方式采用jackson</span></span><br><span class="line">        redisTemplate.setValueSerializer(jackson2JsonRedisSerializer);</span><br><span class="line">        <span class="comment">// hash的value序列化方式采用jackson</span></span><br><span class="line">        redisTemplate.setHashValueSerializer(jackson2JsonRedisSerializer);</span><br><span class="line">        redisTemplate.afterPropertiesSet();</span><br><span class="line">        <span class="keyword">return</span> redisTemplate;</span><br><span 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> factory Redis连接工厂</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@return</span> CacheManager</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="meta">@SuppressWarnings(value = &#123; &quot;unchecked&quot;, &quot;rawtypes&quot; &#125;)</span></span><br><span class="line">    <span class="keyword">public</span> CacheManager <span class="title function_">cacheManager</span><span class="params">(RedisConnectionFactory factory)</span> &#123;</span><br><span class="line">        RedisSerializer&lt;String&gt; redisSerializer = <span class="keyword">new</span> <span class="title class_">StringRedisSerializer</span>();</span><br><span class="line">        <span class="type">Jackson2JsonRedisSerializer</span> <span class="variable">jackson2JsonRedisSerializer</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">Jackson2JsonRedisSerializer</span>(Object.class);</span><br><span class="line">        <span class="comment">//解决查询缓存转换异常的问题</span></span><br><span class="line">        <span class="type">ObjectMapper</span> <span class="variable">om</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">ObjectMapper</span>();</span><br><span class="line">        om.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);</span><br><span class="line">        om.activateDefaultTyping(LaissezFaireSubTypeValidator.instance ,</span><br><span class="line">                ObjectMapper.DefaultTyping.NON_FINAL, JsonTypeInfo.As.PROPERTY);</span><br><span class="line">        jackson2JsonRedisSerializer.setObjectMapper(om);</span><br><span class="line">        <span class="comment">// 配置序列化（解决乱码的问题）,过期时间600秒</span></span><br><span class="line">        <span class="type">RedisCacheConfiguration</span> <span class="variable">config</span> <span class="operator">=</span></span><br><span class="line">                RedisCacheConfiguration.defaultCacheConfig()</span><br><span class="line">                        .entryTtl(Duration.ofSeconds(<span class="number">600</span>))</span><br><span class="line">                        .serializeKeysWith(RedisSerializationContext.SerializationPair.fromSerializer(redisSerializer))</span><br><span class="line">                        .serializeValuesWith(RedisSerializationContext.SerializationPair.fromSerializer(jackson2JsonRedisSerializer))</span><br><span class="line">                        .disableCachingNullValues();</span><br><span class="line">        <span class="keyword">return</span> RedisCacheManager.builder(factory).cacheDefaults(config).build();</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><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><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">@SpringBootTest</span></span><br><span class="line"><span class="keyword">class</span> <span class="title class_">RedisTestApplicationTests</span> &#123;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 注意要注入的对象是配置类的对象，要是使用了原生的就配置无效了,或者使用resource</span></span><br><span class="line">    <span class="meta">@Autowired</span></span><br><span class="line">    <span class="meta">@Qualifier(&quot;redisTemplate&quot;)</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">void</span> <span class="title function_">contextLoads</span><span class="params">()</span> <span class="keyword">throws</span> JsonProcessingException &#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;yww&quot;</span>, <span class="number">20</span>);</span><br><span class="line">        redisTemplate.opsForValue().set(<span class="string">&quot;user&quot;</span>,json);</span><br><span class="line">        System.out.println(redisTemplate.opsForValue().get(<span class="string">&quot;user&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><h1 id="使用注解"><a href="#使用注解" class="headerlink" title="使用注解"></a>使用注解</h1><p>注解的使用会更加方便的为接口返回的数据添加到缓存里。</p><h2 id="Cacheable"><a href="#Cacheable" class="headerlink" title="Cacheable"></a>Cacheable</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><span class="line">21</span><br><span class="line">22</span><br><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">@Target(&#123;ElementType.TYPE, ElementType.METHOD&#125;)</span></span><br><span class="line"><span class="meta">@Retention(RetentionPolicy.RUNTIME)</span></span><br><span class="line"><span class="meta">@Inherited</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> Cacheable &#123;</span><br><span class="line">    <span class="comment">// 缓存名（注意这个不是键，类似一个文件夹名）</span></span><br><span class="line"><span class="meta">@AliasFor(&quot;cacheNames&quot;)</span></span><br><span class="line">String[] value() <span class="keyword">default</span> &#123;&#125;;</span><br><span class="line"><span class="meta">@AliasFor(&quot;value&quot;)</span></span><br><span class="line">String[] cacheNames() <span class="keyword">default</span> &#123;&#125;;</span><br><span class="line"><span class="comment">// 缓存的自定义键</span></span><br><span class="line">String <span class="title function_">key</span><span class="params">()</span> <span class="keyword">default</span> <span class="string">&quot;&quot;</span>;</span><br><span class="line"></span><br><span class="line">String <span class="title function_">keyGenerator</span><span class="params">()</span> <span class="keyword">default</span> <span class="string">&quot;&quot;</span>;</span><br><span class="line"></span><br><span class="line">String <span class="title function_">cacheManager</span><span class="params">()</span> <span class="keyword">default</span> <span class="string">&quot;&quot;</span>;</span><br><span class="line"></span><br><span class="line">String <span class="title function_">cacheResolver</span><span class="params">()</span> <span class="keyword">default</span> <span class="string">&quot;&quot;</span>;</span><br><span class="line"><span class="comment">// 缓存的条件</span></span><br><span class="line">String <span class="title function_">condition</span><span class="params">()</span> <span class="keyword">default</span> <span class="string">&quot;&quot;</span>;</span><br><span class="line"></span><br><span class="line">String <span class="title function_">unless</span><span class="params">()</span> <span class="keyword">default</span> <span class="string">&quot;&quot;</span>;</span><br><span class="line"></span><br><span class="line"><span class="type">boolean</span> <span class="title function_">sync</span><span class="params">()</span> <span class="keyword">default</span> <span class="literal">false</span>;</span><br><span class="line"></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>首先在这个注解中，<code>value</code>或者是<code>cacheNames</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">@Cacheable(&quot;yww&quot;)</span><span class="comment">// 这样就是value的属性</span></span><br><span class="line"><span class="meta">@GetMapping(&quot;/&quot;)</span></span><br><span class="line"><span class="keyword">public</span> Result <span class="title function_">getInfo</span><span class="params">()</span> &#123;</span><br><span class="line">    List&lt;User&gt; list = service.list();</span><br><span class="line">    <span class="keyword">return</span> Result.ok().data(<span class="string">&quot;list&quot;</span>,list);</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">@Cacheable(value = &quot;yww&quot;,key = &quot;111&quot;)</span></span><br><span class="line"><span class="meta">@GetMapping(&quot;/&quot;)</span></span><br><span class="line"><span class="keyword">public</span> Result <span class="title function_">getInfo</span><span class="params">()</span> &#123;</span><br><span class="line">    List&lt;User&gt; list = service.list();</span><br><span class="line">    <span class="keyword">return</span> Result.ok().data(<span class="string">&quot;list&quot;</span>,list);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p><code>condition</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">// 这样表示id大于50的时候取出的数据才会放入缓存</span></span><br><span class="line"><span class="meta">@Cacheable(value = &quot;yww&quot;,condition = &quot;#id &gt; 50&quot;)</span></span><br><span class="line">   <span class="meta">@GetMapping(&quot;/&#123;id&#125;&quot;)</span></span><br><span class="line">   <span class="keyword">public</span> Result <span class="title function_">getInfo</span><span class="params">(<span class="meta">@PathVariable(&quot;id&quot;)</span> <span class="type">int</span> id)</span> &#123;</span><br><span class="line">   &#125;</span><br></pre></td></tr></table></figure><h2 id="CachePut"><a href="#CachePut" class="headerlink" title="CachePut"></a>CachePut</h2><p>这个注解和<code>Cacheable</code>作用是一样的，参数也是一样的，不一样的是这个注解是只负责将返回的结果存入缓存，再次查询的时候，是不走缓存的。</p><h2 id="CacheEvict"><a href="#CacheEvict" class="headerlink" title="CacheEvict"></a>CacheEvict</h2><p>这个注解主要是用来清除缓存的。</p><p>主要使用的参数是<code>allEntries</code>，让它为true会将缓存清空</p><figure class="highlight java"><table><tr><td class="gutter"><pre><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">// value,key,condition和Cacheable一样</span></span><br><span class="line"><span class="meta">@Cacheable(value = &quot;yww&quot;,key = &quot;111&quot;,condition = &quot;#id &gt; 50&quot;,allEntries = true)</span></span><br><span class="line">   <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">clear</span><span class="params">()</span> &#123;</span><br><span class="line">   &#125;</span><br></pre></td></tr></table></figure><h1 id="比较标准的一个配置"><a href="#比较标准的一个配置" class="headerlink" title="比较标准的一个配置"></a>比较标准的一个配置</h1><h2 id="依赖"><a href="#依赖" class="headerlink" title="依赖"></a>依赖</h2><ol><li>redis的官方starter</li><li>fastjson2，用于配置文件中的序列化，当然用其他的也可能可以</li><li>log4j，用于记录日志，当然不用也可以</li><li>hutool，这里用于提供sha256Hex加密</li></ol><figure class="highlight xml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><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">&lt;!--  redis  --&gt;</span></span><br><span class="line"><span class="tag">&lt;<span class="name">dependency</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">groupId</span>&gt;</span>org.springframework.boot<span class="tag">&lt;/<span class="name">groupId</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">artifactId</span>&gt;</span>spring-boot-starter-data-redis<span class="tag">&lt;/<span class="name">artifactId</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">version</span>&gt;</span>$&#123;redis.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="comment">&lt;!--  搭配redis的对象池依赖  --&gt;</span></span><br><span class="line"><span class="tag">&lt;<span class="name">dependency</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">groupId</span>&gt;</span>org.apache.commons<span class="tag">&lt;/<span class="name">groupId</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">artifactId</span>&gt;</span>commons-pool2<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;commons-pool2.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="comment">&lt;!--  fastjson2  --&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.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>$&#123;fastjson2.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="comment">&lt;!--  log4j日志  --&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.logging.log4j<span class="tag">&lt;/<span class="name">groupId</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">artifactId</span>&gt;</span>log4j-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;log4j.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="comment">&lt;!--  hutool  --&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>cn.hutool<span class="tag">&lt;/<span class="name">groupId</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">artifactId</span>&gt;</span>hutool-all<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;hutool.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></pre></td></tr></table></figure><h2 id="application-yaml"><a href="#application-yaml" class="headerlink" title="application.yaml"></a>application.yaml</h2><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="comment"># redis配置</span></span><br><span class="line">  <span class="attr">redis:</span></span><br><span class="line">    <span class="attr">database:</span> <span class="number">3</span></span><br><span class="line">    <span class="attr">host:</span> <span class="number">127.0</span><span class="number">.0</span><span class="number">.1</span></span><br><span class="line">    <span class="attr">port:</span> <span class="number">6379</span></span><br><span class="line">    <span class="comment">#连接超时时间（毫秒）</span></span><br><span class="line">    <span class="attr">timeout:</span> <span class="number">5000</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="comment">#连接池最大连接数（使用负值表示没有限制）</span></span><br><span class="line">        <span class="attr">max-active:</span> <span class="number">8</span></span><br><span class="line">        <span class="comment">#连接池最大阻塞等待时间（使用负值表示没有限制）</span></span><br><span class="line">        <span class="attr">max-wait:</span> <span class="number">3000</span></span><br><span class="line">        <span class="comment">#连接池中的最大空闲连接</span></span><br><span class="line">        <span class="attr">max-idle:</span> <span class="number">10</span></span><br><span class="line">        <span class="comment">#连接池中的最小空闲连接</span></span><br><span class="line">        <span class="attr">min-idle:</span> <span class="number">2</span></span><br></pre></td></tr></table></figure><h2 id="Redis-config"><a href="#Redis-config" class="headerlink" title="Redis.config"></a>Redis.config</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><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><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></pre></td><td class="code"><pre><span class="line"><span class="keyword">import</span> cn.hutool.core.lang.Assert;</span><br><span class="line"><span class="keyword">import</span> cn.hutool.crypto.digest.DigestUtil;</span><br><span class="line"><span class="keyword">import</span> com.alibaba.fastjson2.JSON;</span><br><span class="line"><span class="keyword">import</span> com.alibaba.fastjson2.JSONReader;</span><br><span class="line"><span class="keyword">import</span> com.alibaba.fastjson2.JSONWriter;</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.commons.lang3.SerializationException;</span><br><span class="line"><span class="keyword">import</span> org.apache.commons.lang3.StringUtils;</span><br><span class="line"><span class="keyword">import</span> org.springframework.boot.autoconfigure.AutoConfigureBefore;</span><br><span class="line"><span class="keyword">import</span> org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;</span><br><span class="line"><span class="keyword">import</span> org.springframework.boot.autoconfigure.data.redis.RedisAutoConfiguration;</span><br><span class="line"><span class="keyword">import</span> org.springframework.cache.Cache;</span><br><span class="line"><span class="keyword">import</span> org.springframework.cache.annotation.CachingConfigurerSupport;</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.cache.interceptor.CacheErrorHandler;</span><br><span class="line"><span class="keyword">import</span> org.springframework.cache.interceptor.KeyGenerator;</span><br><span class="line"><span class="keyword">import</span> org.springframework.context.annotation.Bean;</span><br><span class="line"><span class="keyword">import</span> org.springframework.context.annotation.Configuration;</span><br><span class="line"><span class="keyword">import</span> org.springframework.data.redis.cache.RedisCacheConfiguration;</span><br><span class="line"><span class="keyword">import</span> org.springframework.data.redis.connection.RedisConnectionFactory;</span><br><span class="line"><span class="keyword">import</span> org.springframework.data.redis.core.RedisTemplate;</span><br><span class="line"><span class="keyword">import</span> org.springframework.data.redis.serializer.RedisSerializationContext;</span><br><span class="line"><span class="keyword">import</span> org.springframework.data.redis.serializer.RedisSerializer;</span><br><span class="line"><span class="keyword">import</span> reactor.util.annotation.Nullable;</span><br><span class="line"></span><br><span class="line"><span class="keyword">import</span> java.nio.charset.Charset;</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.time.Duration;</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><br><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * &lt;p&gt;</span></span><br><span class="line"><span class="comment"> *      Redis配置类</span></span><br><span class="line"><span class="comment"> * &lt;/p&gt;</span></span><br><span class="line"><span class="comment"> *</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@ClassName</span> RedisConfig</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@Author</span> yww</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@Date</span> 2022/10/12 21:10</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="meta">@Slf4j</span></span><br><span class="line"><span class="meta">@Configuration</span></span><br><span class="line"><span class="meta">@EnableCaching</span></span><br><span class="line"><span class="meta">@AutoConfigureBefore(RedisAutoConfiguration.class)</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">RedisConfig</span> <span class="keyword">extends</span> <span class="title class_">CachingConfigurerSupport</span> &#123;</span><br><span class="line"></span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     *  RedisTemplate的配置</span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    <span class="meta">@Bean(name = &quot;redisTemplate&quot;)</span></span><br><span class="line">    <span class="meta">@ConditionalOnMissingBean(name = &quot;redisTemplate&quot;)</span></span><br><span class="line">    <span class="meta">@SuppressWarnings(value = &#123; &quot;unchecked&quot;, &quot;rawtypes&quot; &#125;)</span></span><br><span class="line">    <span class="keyword">public</span> RedisTemplate&lt;String, Object&gt; <span class="title function_">redisTemplate</span><span class="params">(RedisConnectionFactory connectionFactory)</span> &#123;</span><br><span class="line">        RedisTemplate&lt;String, Object&gt; template = <span class="keyword">new</span> <span class="title class_">RedisTemplate</span>&lt;&gt;();</span><br><span class="line">        template.setConnectionFactory(connectionFactory);</span><br><span class="line"></span><br><span class="line">        <span class="comment">// 使用FastJson2重写序列化</span></span><br><span class="line">        <span class="type">FastJson2JsonRedisSerializer</span> <span class="variable">serializer</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">FastJson2JsonRedisSerializer</span>(Object.class);</span><br><span class="line"></span><br><span class="line">        <span class="comment">// value值的序列化采用FastJson2重写的序列化</span></span><br><span class="line">        template.setValueSerializer(serializer);</span><br><span class="line">        template.setHashValueSerializer(serializer);</span><br><span class="line"></span><br><span class="line">        <span class="comment">// key的序列化采用StringRedisSerializer</span></span><br><span class="line">        template.setKeySerializer(<span class="keyword">new</span> <span class="title class_">StringRedisSerializer</span>());</span><br><span class="line">        template.setHashKeySerializer(<span class="keyword">new</span> <span class="title class_">StringRedisSerializer</span>());</span><br><span class="line"></span><br><span class="line">        template.afterPropertiesSet();</span><br><span class="line">        <span class="keyword">return</span> template;</span><br><span 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">     * 设置 redis 数据默认过期时间，默认3小时</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> RedisCacheConfiguration <span class="title function_">redisCacheConfiguration</span><span class="params">()</span> &#123;</span><br><span class="line">        FastJson2JsonRedisSerializer&lt;Object&gt; fastJsonRedisSerializer = <span class="keyword">new</span> <span class="title class_">FastJson2JsonRedisSerializer</span>&lt;&gt;(Object.class);</span><br><span class="line">        <span class="type">RedisCacheConfiguration</span> <span class="variable">configuration</span> <span class="operator">=</span> RedisCacheConfiguration.defaultCacheConfig();</span><br><span class="line">        configuration = configuration.serializeValuesWith(RedisSerializationContext.</span><br><span class="line">                SerializationPair.fromSerializer(fastJsonRedisSerializer)).entryTtl(Duration.ofHours(<span class="number">3</span>));</span><br><span class="line">        <span class="keyword">return</span> configuration;</span><br><span 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">     * 自定义缓存key生成策略，默认将使用该策略</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="meta">@Override</span></span><br><span class="line">    <span class="keyword">public</span> KeyGenerator <span class="title function_">keyGenerator</span><span class="params">()</span> &#123;</span><br><span class="line">        <span class="keyword">return</span> (target, method, params) -&gt; &#123;</span><br><span class="line">            Map&lt;String, Object&gt; container = <span class="keyword">new</span> <span class="title class_">HashMap</span>&lt;&gt;(<span class="number">4</span>);</span><br><span class="line">            Class&lt;?&gt; targetClassClass = target.getClass();</span><br><span class="line">            <span class="comment">// 类地址</span></span><br><span class="line">            container.put(<span class="string">&quot;class&quot;</span>, targetClassClass.toGenericString());</span><br><span class="line">            <span class="comment">// 方法名称</span></span><br><span class="line">            container.put(<span class="string">&quot;methodName&quot;</span>, method.getName());</span><br><span class="line">            <span class="comment">// 包名称</span></span><br><span class="line">            container.put(<span class="string">&quot;package&quot;</span>, targetClassClass.getPackage());</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; params.length; i++) &#123;</span><br><span class="line">                container.put(String.valueOf(i), params[i]);</span><br><span class="line">            &#125;</span><br><span class="line">            <span class="comment">// 转为JSON字符串</span></span><br><span class="line">            <span class="type">String</span> <span class="variable">jsonString</span> <span class="operator">=</span> JSON.toJSONString(container);</span><br><span class="line">            <span class="comment">// 做SHA256 Hash计算，得到一个SHA256摘要作为Key</span></span><br><span class="line">            <span class="keyword">return</span> DigestUtil.sha256Hex(jsonString);</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">     *  Redis的异常处理，当Redis发生异常时，打印日志</span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    <span class="meta">@Bean</span></span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="meta">@SuppressWarnings(value = &#123;&quot;all&quot;&#125;)</span></span><br><span class="line">    <span class="keyword">public</span> CacheErrorHandler <span class="title function_">errorHandler</span><span class="params">()</span> &#123;</span><br><span class="line">        log.info(<span class="string">&quot;初始化 -&gt; [&#123;&#125;]&quot;</span>, <span class="string">&quot;Redis CacheErrorHandler&quot;</span>);</span><br><span class="line">        <span class="keyword">return</span> <span class="keyword">new</span> <span class="title class_">CacheErrorHandler</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_">handleCacheGetError</span><span class="params">(RuntimeException e, Cache cache, Object key)</span> &#123;</span><br><span class="line">                log.error(<span class="string">&quot;Redis occur handleCacheGetError：key -&gt; [&#123;&#125;]&quot;</span>, key, e);</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_">handleCachePutError</span><span class="params">(RuntimeException e, Cache cache, Object key, Object value)</span> &#123;</span><br><span class="line">                log.error(<span class="string">&quot;Redis occur handleCachePutError：key -&gt; [&#123;&#125;]；value -&gt; [&#123;&#125;]&quot;</span>, key, value, e);</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_">handleCacheEvictError</span><span class="params">(RuntimeException e, Cache cache, Object key)</span> &#123;</span><br><span class="line">                log.error(<span class="string">&quot;Redis occur handleCacheEvictError：key -&gt; [&#123;&#125;]&quot;</span>, key, e);</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_">handleCacheClearError</span><span class="params">(RuntimeException e, Cache cache)</span> &#123;</span><br><span class="line">                log.error(<span class="string">&quot;Redis occur handleCacheClearError：&quot;</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"></span><br><span 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"> * Value 序列化</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="keyword">class</span> <span class="title class_">FastJson2JsonRedisSerializer</span>&lt;T&gt; <span class="keyword">implements</span> <span class="title class_">RedisSerializer</span>&lt;T&gt; &#123;</span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">final</span> <span class="type">Charset</span> <span class="variable">DEFAULT_CHARSET</span> <span class="operator">=</span> StandardCharsets.UTF_8;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">final</span> Class&lt;T&gt; clazz;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">public</span> <span class="title function_">FastJson2JsonRedisSerializer</span><span class="params">(Class&lt;T&gt; clazz)</span> &#123;</span><br><span class="line">        <span class="built_in">super</span>();</span><br><span class="line">        <span class="built_in">this</span>.clazz = clazz;</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">byte</span>[] serialize(T t) <span class="keyword">throws</span> SerializationException &#123;</span><br><span class="line">        <span class="keyword">if</span> (t == <span class="literal">null</span>) &#123;</span><br><span class="line">            <span class="keyword">return</span> <span class="keyword">new</span> <span class="title class_">byte</span>[<span class="number">0</span>];</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">return</span> JSON.toJSONString(t, JSONWriter.Feature.WriteClassName).getBytes(DEFAULT_CHARSET);</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> T <span class="title function_">deserialize</span><span class="params">(<span class="type">byte</span>[] bytes)</span> <span class="keyword">throws</span> SerializationException &#123;</span><br><span class="line">        <span class="keyword">if</span> (bytes == <span class="literal">null</span> || bytes.length == <span class="number">0</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 class="type">String</span> <span class="variable">str</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">String</span>(bytes, DEFAULT_CHARSET);</span><br><span class="line"></span><br><span class="line">        <span class="keyword">return</span> JSON.parseObject(str, clazz, JSONReader.Feature.SupportAutoType);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * 重写 String序列化器</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="keyword">class</span> <span class="title class_">StringRedisSerializer</span> <span class="keyword">implements</span> <span class="title class_">RedisSerializer</span>&lt;Object&gt; &#123;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">final</span> Charset charset;</span><br><span class="line"></span><br><span class="line">    StringRedisSerializer() &#123;</span><br><span class="line">        <span class="built_in">this</span>(StandardCharsets.UTF_8);</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_">StringRedisSerializer</span><span class="params">(Charset charset)</span> &#123;</span><br><span class="line">        Assert.notNull(charset, <span class="string">&quot;Charset must not be null!&quot;</span>);</span><br><span class="line">        <span class="built_in">this</span>.charset = charset;</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_">deserialize</span><span class="params">(<span class="type">byte</span>[] bytes)</span> &#123;</span><br><span class="line">        <span class="keyword">return</span> (bytes == <span class="literal">null</span> ? <span class="literal">null</span> : <span class="keyword">new</span> <span class="title class_">String</span>(bytes, charset));</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="meta">@Nullable</span></span><br><span class="line">    <span class="type">byte</span>[] serialize(Object object) &#123;</span><br><span class="line">        <span class="type">String</span> <span class="variable">string</span> <span class="operator">=</span> JSON.toJSONString(object);</span><br><span class="line">        <span class="keyword">if</span> (StringUtils.isBlank(string)) &#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">        string = string.replace(<span class="string">&quot;\&quot;&quot;</span>, <span class="string">&quot;&quot;</span>);</span><br><span class="line">        <span class="keyword">return</span> string.getBytes(charset);</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="Redis工具类"><a href="#Redis工具类" class="headerlink" title="Redis工具类"></a>Redis工具类</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><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><span class="line">376</span><br><span class="line">377</span><br><span class="line">378</span><br><span class="line">379</span><br><span class="line">380</span><br><span class="line">381</span><br><span class="line">382</span><br><span class="line">383</span><br><span class="line">384</span><br><span class="line">385</span><br><span class="line">386</span><br><span class="line">387</span><br><span class="line">388</span><br><span class="line">389</span><br><span class="line">390</span><br><span class="line">391</span><br><span class="line">392</span><br><span class="line">393</span><br><span class="line">394</span><br><span class="line">395</span><br><span class="line">396</span><br><span class="line">397</span><br><span class="line">398</span><br><span class="line">399</span><br><span class="line">400</span><br><span class="line">401</span><br><span class="line">402</span><br><span class="line">403</span><br><span class="line">404</span><br><span class="line">405</span><br><span class="line">406</span><br><span class="line">407</span><br><span class="line">408</span><br><span class="line">409</span><br><span class="line">410</span><br><span class="line">411</span><br><span class="line">412</span><br><span class="line">413</span><br><span class="line">414</span><br><span class="line">415</span><br><span class="line">416</span><br><span class="line">417</span><br><span class="line">418</span><br><span class="line">419</span><br><span class="line">420</span><br><span class="line">421</span><br><span class="line">422</span><br><span class="line">423</span><br><span class="line">424</span><br><span class="line">425</span><br><span class="line">426</span><br><span class="line">427</span><br><span class="line">428</span><br><span class="line">429</span><br><span class="line">430</span><br><span class="line">431</span><br><span class="line">432</span><br><span class="line">433</span><br><span class="line">434</span><br><span class="line">435</span><br><span class="line">436</span><br><span class="line">437</span><br><span class="line">438</span><br><span class="line">439</span><br><span class="line">440</span><br><span class="line">441</span><br><span class="line">442</span><br><span class="line">443</span><br><span class="line">444</span><br><span class="line">445</span><br><span class="line">446</span><br><span class="line">447</span><br><span class="line">448</span><br><span class="line">449</span><br><span class="line">450</span><br><span class="line">451</span><br><span class="line">452</span><br><span class="line">453</span><br><span class="line">454</span><br><span class="line">455</span><br><span class="line">456</span><br><span class="line">457</span><br><span class="line">458</span><br><span class="line">459</span><br><span class="line">460</span><br><span class="line">461</span><br><span class="line">462</span><br><span class="line">463</span><br><span class="line">464</span><br><span class="line">465</span><br><span class="line">466</span><br><span class="line">467</span><br><span class="line">468</span><br><span class="line">469</span><br><span class="line">470</span><br><span class="line">471</span><br><span class="line">472</span><br><span class="line">473</span><br><span class="line">474</span><br><span class="line">475</span><br><span class="line">476</span><br><span class="line">477</span><br><span class="line">478</span><br><span class="line">479</span><br><span class="line">480</span><br><span class="line">481</span><br><span class="line">482</span><br><span class="line">483</span><br><span class="line">484</span><br><span class="line">485</span><br><span class="line">486</span><br><span class="line">487</span><br><span class="line">488</span><br><span class="line">489</span><br><span class="line">490</span><br><span class="line">491</span><br><span class="line">492</span><br><span class="line">493</span><br><span class="line">494</span><br><span class="line">495</span><br><span class="line">496</span><br><span class="line">497</span><br><span class="line">498</span><br><span class="line">499</span><br><span class="line">500</span><br><span class="line">501</span><br><span class="line">502</span><br><span class="line">503</span><br><span class="line">504</span><br><span class="line">505</span><br><span class="line">506</span><br><span class="line">507</span><br><span class="line">508</span><br><span class="line">509</span><br><span class="line">510</span><br><span class="line">511</span><br><span class="line">512</span><br><span class="line">513</span><br><span class="line">514</span><br><span class="line">515</span><br><span class="line">516</span><br><span class="line">517</span><br><span class="line">518</span><br><span class="line">519</span><br><span class="line">520</span><br><span class="line">521</span><br><span class="line">522</span><br><span class="line">523</span><br><span class="line">524</span><br><span class="line">525</span><br><span class="line">526</span><br><span class="line">527</span><br><span class="line">528</span><br><span class="line">529</span><br><span class="line">530</span><br><span class="line">531</span><br><span class="line">532</span><br><span class="line">533</span><br><span class="line">534</span><br><span class="line">535</span><br><span class="line">536</span><br><span class="line">537</span><br><span class="line">538</span><br><span class="line">539</span><br><span class="line">540</span><br><span class="line">541</span><br><span class="line">542</span><br><span class="line">543</span><br><span class="line">544</span><br><span class="line">545</span><br><span class="line">546</span><br><span class="line">547</span><br><span class="line">548</span><br><span class="line">549</span><br><span class="line">550</span><br><span class="line">551</span><br><span class="line">552</span><br><span class="line">553</span><br><span class="line">554</span><br><span class="line">555</span><br><span class="line">556</span><br><span class="line">557</span><br><span class="line">558</span><br><span class="line">559</span><br><span class="line">560</span><br><span class="line">561</span><br><span class="line">562</span><br><span class="line">563</span><br><span class="line">564</span><br><span class="line">565</span><br><span class="line">566</span><br><span class="line">567</span><br><span class="line">568</span><br><span class="line">569</span><br><span class="line">570</span><br><span class="line">571</span><br><span class="line">572</span><br><span class="line">573</span><br><span class="line">574</span><br><span class="line">575</span><br><span class="line">576</span><br><span class="line">577</span><br><span class="line">578</span><br><span class="line">579</span><br><span class="line">580</span><br><span class="line">581</span><br><span class="line">582</span><br><span class="line">583</span><br><span class="line">584</span><br><span class="line">585</span><br><span class="line">586</span><br><span class="line">587</span><br><span class="line">588</span><br><span class="line">589</span><br><span class="line">590</span><br><span class="line">591</span><br><span class="line">592</span><br><span class="line">593</span><br><span class="line">594</span><br><span class="line">595</span><br><span class="line">596</span><br><span class="line">597</span><br><span class="line">598</span><br><span class="line">599</span><br><span class="line">600</span><br><span class="line">601</span><br><span class="line">602</span><br><span class="line">603</span><br><span class="line">604</span><br><span class="line">605</span><br><span class="line">606</span><br><span class="line">607</span><br><span class="line">608</span><br><span class="line">609</span><br><span class="line">610</span><br><span class="line">611</span><br><span class="line">612</span><br><span class="line">613</span><br><span class="line">614</span><br><span class="line">615</span><br><span class="line">616</span><br><span class="line">617</span><br><span class="line">618</span><br><span class="line">619</span><br><span class="line">620</span><br><span class="line">621</span><br><span class="line">622</span><br><span class="line">623</span><br><span class="line">624</span><br><span class="line">625</span><br><span class="line">626</span><br><span class="line">627</span><br><span class="line">628</span><br><span class="line">629</span><br><span class="line">630</span><br><span class="line">631</span><br><span class="line">632</span><br><span class="line">633</span><br><span class="line">634</span><br><span class="line">635</span><br><span class="line">636</span><br><span class="line">637</span><br><span class="line">638</span><br><span class="line">639</span><br><span class="line">640</span><br><span class="line">641</span><br><span class="line">642</span><br><span class="line">643</span><br><span class="line">644</span><br><span class="line">645</span><br><span class="line">646</span><br><span class="line">647</span><br><span class="line">648</span><br><span class="line">649</span><br><span class="line">650</span><br><span class="line">651</span><br><span class="line">652</span><br><span class="line">653</span><br><span class="line">654</span><br><span class="line">655</span><br><span class="line">656</span><br><span class="line">657</span><br><span class="line">658</span><br><span class="line">659</span><br><span class="line">660</span><br><span class="line">661</span><br><span class="line">662</span><br><span class="line">663</span><br><span class="line">664</span><br><span class="line">665</span><br><span class="line">666</span><br><span class="line">667</span><br><span class="line">668</span><br><span class="line">669</span><br><span class="line">670</span><br><span class="line">671</span><br><span class="line">672</span><br><span class="line">673</span><br><span class="line">674</span><br><span class="line">675</span><br><span class="line">676</span><br><span class="line">677</span><br><span class="line">678</span><br><span class="line">679</span><br><span class="line">680</span><br><span class="line">681</span><br><span class="line">682</span><br><span class="line">683</span><br><span class="line">684</span><br><span class="line">685</span><br><span class="line">686</span><br><span class="line">687</span><br><span class="line">688</span><br><span class="line">689</span><br><span class="line">690</span><br><span class="line">691</span><br><span class="line">692</span><br><span class="line">693</span><br><span class="line">694</span><br><span class="line">695</span><br><span class="line">696</span><br><span class="line">697</span><br><span class="line">698</span><br><span class="line">699</span><br><span class="line">700</span><br><span class="line">701</span><br><span class="line">702</span><br><span class="line">703</span><br><span class="line">704</span><br><span class="line">705</span><br><span class="line">706</span><br><span class="line">707</span><br><span class="line">708</span><br><span class="line">709</span><br><span class="line">710</span><br><span class="line">711</span><br><span class="line">712</span><br><span class="line">713</span><br><span class="line">714</span><br><span class="line">715</span><br><span class="line">716</span><br><span class="line">717</span><br><span class="line">718</span><br><span class="line">719</span><br><span class="line">720</span><br><span class="line">721</span><br><span class="line">722</span><br><span class="line">723</span><br><span class="line">724</span><br><span class="line">725</span><br><span class="line">726</span><br><span class="line">727</span><br><span class="line">728</span><br><span class="line">729</span><br><span class="line">730</span><br><span class="line">731</span><br><span class="line">732</span><br><span class="line">733</span><br><span class="line">734</span><br><span class="line">735</span><br><span class="line">736</span><br><span class="line">737</span><br><span class="line">738</span><br><span class="line">739</span><br><span class="line">740</span><br><span class="line">741</span><br><span class="line">742</span><br><span class="line">743</span><br><span class="line">744</span><br><span class="line">745</span><br><span class="line">746</span><br><span class="line">747</span><br><span class="line">748</span><br><span class="line">749</span><br><span class="line">750</span><br><span class="line">751</span><br><span class="line">752</span><br><span class="line">753</span><br><span class="line">754</span><br><span class="line">755</span><br><span class="line">756</span><br><span class="line">757</span><br><span class="line">758</span><br><span class="line">759</span><br><span class="line">760</span><br><span class="line">761</span><br><span class="line">762</span><br><span class="line">763</span><br><span class="line">764</span><br><span class="line">765</span><br><span class="line">766</span><br><span class="line">767</span><br><span class="line">768</span><br><span class="line">769</span><br><span class="line">770</span><br><span class="line">771</span><br><span class="line">772</span><br><span class="line">773</span><br><span class="line">774</span><br><span class="line">775</span><br><span class="line">776</span><br><span class="line">777</span><br><span class="line">778</span><br><span class="line">779</span><br><span class="line">780</span><br><span class="line">781</span><br><span class="line">782</span><br><span class="line">783</span><br><span class="line">784</span><br><span class="line">785</span><br><span class="line">786</span><br><span class="line">787</span><br><span class="line">788</span><br><span class="line">789</span><br><span class="line">790</span><br><span class="line">791</span><br><span class="line">792</span><br><span class="line">793</span><br><span class="line">794</span><br><span class="line">795</span><br><span class="line">796</span><br><span class="line">797</span><br><span class="line">798</span><br><span class="line">799</span><br><span class="line">800</span><br><span class="line">801</span><br><span class="line">802</span><br><span class="line">803</span><br><span class="line">804</span><br><span class="line">805</span><br><span class="line">806</span><br><span class="line">807</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">import</span> com.alibaba.fastjson2.JSON;</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.beans.factory.annotation.Autowired;</span><br><span class="line"><span class="keyword">import</span> org.springframework.data.redis.connection.RedisConnection;</span><br><span class="line"><span class="keyword">import</span> org.springframework.data.redis.connection.RedisConnectionFactory;</span><br><span class="line"><span class="keyword">import</span> org.springframework.data.redis.core.*;</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="keyword">import</span> java.util.*;</span><br><span class="line"><span class="keyword">import</span> java.util.concurrent.TimeUnit;</span><br><span class="line"></span><br><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * &lt;p&gt;</span></span><br><span class="line"><span class="comment"> *      Redis的工具类</span></span><br><span class="line"><span class="comment"> * &lt;/p&gt;</span></span><br><span class="line"><span class="comment"> *</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@ClassName</span> RedisUtil</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@Author</span> yww</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@Date</span> 2022/10/15 10:40</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="meta">@Slf4j</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_">RedisUtil</span> &#123;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">final</span> StringRedisTemplate stringRedisTemplate;</span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">final</span> RedisTemplate&lt;String, Object&gt; redisTemplate;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Autowired</span></span><br><span class="line">    <span class="keyword">public</span> <span class="title function_">RedisUtil</span><span class="params">(StringRedisTemplate stringRedisTemplate, RedisTemplate&lt;String, Object&gt; redisTemplate)</span> &#123;</span><br><span class="line">        <span class="built_in">this</span>.stringRedisTemplate = stringRedisTemplate;</span><br><span class="line">        <span class="built_in">this</span>.redisTemplate = redisTemplate;</span><br><span 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> key 键</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_">setStr</span><span class="params">(String key, String value)</span> &#123;</span><br><span class="line">        stringRedisTemplate.opsForValue().set(key, value);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     * 普通缓存</span></span><br><span class="line"><span class="comment">     *</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@param</span> key 键</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_">setStr</span><span class="params">(String key, String value, <span class="type">long</span> time)</span> &#123;</span><br><span class="line">        stringRedisTemplate.opsForValue().set(key, value, time, TimeUnit.MINUTES);</span><br><span 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> key 键</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_">setStr</span><span class="params">(String key, String value, <span class="type">long</span> time, TimeUnit timeUnit)</span> &#123;</span><br><span class="line">        stringRedisTemplate.opsForValue().set(key, value, time, timeUnit);</span><br><span 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> key 键</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_">getStr</span><span class="params">(String key)</span> &#123;</span><br><span class="line">        <span class="keyword">return</span> key == <span class="literal">null</span> ? <span class="literal">null</span> : stringRedisTemplate.opsForValue().get(key);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     * 普通缓存获取</span></span><br><span class="line"><span class="comment">     *</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@param</span> key 键</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> &lt;T&gt; T <span class="title function_">getStr</span><span class="params">(String key, Class&lt;T&gt; clazz)</span> &#123;</span><br><span class="line">        <span class="keyword">return</span> key == <span class="literal">null</span> ? <span class="literal">null</span> : JSON.parseObject(stringRedisTemplate.opsForValue().get(key), clazz);</span><br><span 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> key 键</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_">deleteStr</span><span class="params">(String key)</span> &#123;</span><br><span class="line">        stringRedisTemplate.delete(key);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     * Object缓存</span></span><br><span class="line"><span class="comment">     *</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@param</span> key 键</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_">setObj</span><span class="params">(String key, Object value)</span> &#123;</span><br><span class="line">        redisTemplate.opsForValue().set(key, value);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     * Object缓存</span></span><br><span class="line"><span class="comment">     *</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@param</span> key 键</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_">setObj</span><span class="params">(String key, Object value, <span class="type">long</span> time)</span> &#123;</span><br><span class="line">        redisTemplate.opsForValue().set(key, value, time, TimeUnit.MINUTES);</span><br><span 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">     * Object缓存</span></span><br><span class="line"><span class="comment">     *</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@param</span> key 键</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_">setObj</span><span class="params">(String key, Object value, <span class="type">long</span> time, TimeUnit timeUnit)</span> &#123;</span><br><span class="line">        redisTemplate.opsForValue().set(key, value, time, timeUnit);</span><br><span 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">     * Object缓存获取</span></span><br><span class="line"><span class="comment">     *</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@param</span> key 键</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_">getObj</span><span class="params">(String key)</span> &#123;</span><br><span class="line">        <span class="keyword">return</span> key == <span class="literal">null</span> ? <span class="literal">null</span> : redisTemplate.opsForValue().get(key);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     * Object缓存获取</span></span><br><span class="line"><span class="comment">     *</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@param</span> key 键</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> &lt;T&gt; T <span class="title function_">getObj</span><span class="params">(String key, Class&lt;T&gt; clazz)</span> &#123;</span><br><span class="line">        <span class="keyword">return</span> key == <span class="literal">null</span> ? <span class="literal">null</span> : (T) redisTemplate.opsForValue().get(key);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     * Object缓存删除</span></span><br><span class="line"><span class="comment">     *</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@param</span> key 键</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">void</span> <span class="title function_">deleteObj</span><span class="params">(String key)</span> &#123;</span><br><span class="line">        redisTemplate.delete(key);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     * 查找匹配key</span></span><br><span class="line"><span class="comment">     *</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@param</span> pattern key</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;String&gt; <span class="title function_">scan</span><span class="params">(String pattern)</span> &#123;</span><br><span class="line">        <span class="type">ScanOptions</span> <span class="variable">options</span> <span class="operator">=</span> ScanOptions.scanOptions().match(pattern).build();</span><br><span class="line">        <span class="type">RedisConnectionFactory</span> <span class="variable">factory</span> <span class="operator">=</span> redisTemplate.getConnectionFactory();</span><br><span class="line">        <span class="type">RedisConnection</span> <span class="variable">rc</span> <span class="operator">=</span> Objects.requireNonNull(factory).getConnection();</span><br><span class="line">        Cursor&lt;<span class="type">byte</span>[]&gt; cursor = rc.scan(options);</span><br><span class="line">        List&lt;String&gt; result = <span class="keyword">new</span> <span class="title class_">ArrayList</span>&lt;&gt;();</span><br><span class="line">        <span class="keyword">while</span> (cursor.hasNext()) &#123;</span><br><span class="line">            result.add(<span class="keyword">new</span> <span class="title class_">String</span>(cursor.next()));</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">try</span> &#123;</span><br><span class="line">            RedisConnectionUtils.releaseConnection(rc, factory);</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> result;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     * 指定缓存失效时间</span></span><br><span class="line"><span class="comment">     *</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@param</span> key  键</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@param</span> time 时间(秒)</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_">expire</span><span class="params">(String key, <span class="type">long</span> time)</span> &#123;</span><br><span class="line">        <span class="keyword">try</span> &#123;</span><br><span class="line">            <span class="keyword">if</span> (time &gt; <span class="number">0</span>) &#123;</span><br><span class="line">                redisTemplate.expire(key, time, TimeUnit.SECONDS);</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">            <span class="keyword">return</span> <span class="literal">false</span>;</span><br><span class="line">        &#125;</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">/**</span></span><br><span 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> key      键</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@param</span> time     时间(秒)</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@param</span> timeUnit 单位</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_">expire</span><span class="params">(String key, <span class="type">long</span> time, TimeUnit timeUnit)</span> &#123;</span><br><span class="line">        <span class="keyword">try</span> &#123;</span><br><span class="line">            <span class="keyword">if</span> (time &gt; <span class="number">0</span>) &#123;</span><br><span class="line">                redisTemplate.expire(key, time, timeUnit);</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">            <span class="keyword">return</span> <span class="literal">false</span>;</span><br><span class="line">        &#125;</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">/**</span></span><br><span class="line"><span class="comment">     * 根据 key 获取过期时间</span></span><br><span class="line"><span class="comment">     *</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@param</span> key 键 不能为null</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@return</span> 时间(秒) 返回0代表为永久有效</span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    <span class="keyword">public</span> <span class="type">long</span> <span class="title function_">getExpire</span><span class="params">(String key)</span> &#123;</span><br><span class="line">        <span class="keyword">return</span> redisTemplate.getExpire(key, TimeUnit.SECONDS);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     * 分页查询 key</span></span><br><span class="line"><span class="comment">     *</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@param</span> patternKey key</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> size       每页数目</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;String&gt; <span class="title function_">findKeysForPage</span><span class="params">(String patternKey, <span class="type">int</span> page, <span class="type">int</span> size)</span> &#123;</span><br><span class="line">        <span class="type">ScanOptions</span> <span class="variable">options</span> <span class="operator">=</span> ScanOptions.scanOptions().match(patternKey).build();</span><br><span class="line">        <span class="type">RedisConnectionFactory</span> <span class="variable">factory</span> <span class="operator">=</span> redisTemplate.getConnectionFactory();</span><br><span class="line">        <span class="type">RedisConnection</span> <span class="variable">rc</span> <span class="operator">=</span> Objects.requireNonNull(factory).getConnection();</span><br><span class="line">        Cursor&lt;<span class="type">byte</span>[]&gt; cursor = rc.scan(options);</span><br><span class="line">        List&lt;String&gt; result = <span class="keyword">new</span> <span class="title class_">ArrayList</span>&lt;&gt;(size);</span><br><span class="line">        <span class="type">int</span> <span class="variable">tmpIndex</span> <span class="operator">=</span> <span class="number">0</span>;</span><br><span class="line">        <span class="type">int</span> <span class="variable">fromIndex</span> <span class="operator">=</span> page * size;</span><br><span class="line">        <span class="type">int</span> <span class="variable">toIndex</span> <span class="operator">=</span> page * size + size;</span><br><span class="line">        <span class="keyword">while</span> (cursor.hasNext()) &#123;</span><br><span class="line">            <span class="keyword">if</span> (tmpIndex &gt;= fromIndex &amp;&amp; tmpIndex &lt; toIndex) &#123;</span><br><span class="line">                result.add(<span class="keyword">new</span> <span class="title class_">String</span>(cursor.next()));</span><br><span class="line">                tmpIndex++;</span><br><span class="line">                <span class="keyword">continue</span>;</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> (tmpIndex &gt;= toIndex) &#123;</span><br><span class="line">                <span class="keyword">break</span>;</span><br><span class="line">            &#125;</span><br><span class="line">            tmpIndex++;</span><br><span class="line">            cursor.next();</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">try</span> &#123;</span><br><span class="line">            RedisConnectionUtils.releaseConnection(rc, factory);</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> result;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     * 判断key是否存在</span></span><br><span class="line"><span class="comment">     *</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@param</span> key 键</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@return</span> true 存在 false不存在</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_">hasKey</span><span class="params">(String key)</span> &#123;</span><br><span class="line">        <span class="keyword">try</span> &#123;</span><br><span class="line">            <span class="keyword">return</span> redisTemplate.hasKey(key);</span><br><span class="line">        &#125; <span class="keyword">catch</span> (Exception e) &#123;</span><br><span class="line">            e.printStackTrace();</span><br><span class="line">            <span class="keyword">return</span> <span class="literal">false</span>;</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     * 删除缓存</span></span><br><span class="line"><span class="comment">     *</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@param</span> keys 可以传一个值 或多个</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_">del</span><span class="params">(String... keys)</span> &#123;</span><br><span class="line">        <span class="keyword">if</span> (keys != <span class="literal">null</span> &amp;&amp; keys.length &gt; <span class="number">0</span>) &#123;</span><br><span class="line">            <span class="keyword">if</span> (keys.length == <span class="number">1</span>) &#123;</span><br><span class="line">                <span class="type">boolean</span> <span class="variable">result</span> <span class="operator">=</span> redisTemplate.delete(keys[<span class="number">0</span>]);</span><br><span class="line">                log.debug(<span class="string">&quot;--------------------------------------------&quot;</span>);</span><br><span class="line">                log.debug(<span class="string">&quot;删除缓存：&#123;&#125;，结果：&#123;&#125;&quot;</span>, keys[<span class="number">0</span>], result);</span><br><span class="line">                log.debug(<span class="string">&quot;--------------------------------------------&quot;</span>);</span><br><span class="line">            &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">                Set&lt;String&gt; keySet = <span class="keyword">new</span> <span class="title class_">HashSet</span>&lt;&gt;();</span><br><span class="line">                <span class="keyword">for</span> (String key : keys) &#123;</span><br><span class="line">                    keySet.addAll(redisTemplate.keys(key));</span><br><span class="line">                &#125;</span><br><span class="line">                <span class="type">long</span> <span class="variable">count</span> <span class="operator">=</span> redisTemplate.delete(keySet);</span><br><span class="line">                log.debug(<span class="string">&quot;--------------------------------------------&quot;</span>);</span><br><span class="line">                log.debug(<span class="string">&quot;成功删除缓存：&#123;&#125;&quot;</span>, keySet.toString());</span><br><span class="line">                log.debug(<span class="string">&quot;缓存删除数量：&#123;&#125;个&quot;</span>, count);</span><br><span class="line">                log.debug(<span class="string">&quot;--------------------------------------------&quot;</span>);</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// ============================String=============================</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> key 键</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_">get</span><span class="params">(String key)</span> &#123;</span><br><span class="line">        <span class="keyword">return</span> key == <span class="literal">null</span> ? <span class="literal">null</span> : redisTemplate.opsForValue().get(key);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     * 批量获取</span></span><br><span class="line"><span class="comment">     *</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@param</span> keys</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;Object&gt; <span class="title function_">multiGet</span><span class="params">(List&lt;String&gt; keys)</span> &#123;</span><br><span class="line">        <span class="keyword">return</span> redisTemplate.opsForValue().multiGet(keys);</span><br><span 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> key   键</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@param</span> value 值</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@return</span> true成功 false失败</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_">set</span><span class="params">(String key, Object value)</span> &#123;</span><br><span class="line">        <span class="keyword">try</span> &#123;</span><br><span class="line">            redisTemplate.opsForValue().set(key, value);</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 e) &#123;</span><br><span class="line">            e.printStackTrace();</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"></span><br><span class="line">    <span class="comment">/**</span></span><br><span 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> key   键</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@param</span> value 值</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@param</span> time  时间(秒) time要大于0 如果time小于等于0 将设置无限期</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@return</span> true成功 false 失败</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_">set</span><span class="params">(String key, Object value, <span class="type">long</span> time)</span> &#123;</span><br><span class="line">        <span class="keyword">try</span> &#123;</span><br><span class="line">            <span class="keyword">if</span> (time &gt; <span class="number">0</span>) &#123;</span><br><span class="line">                redisTemplate.opsForValue().set(key, value, time, TimeUnit.SECONDS);</span><br><span class="line">            &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">                set(key, value);</span><br><span class="line">            &#125;</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 e) &#123;</span><br><span class="line">            e.printStackTrace();</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"></span><br><span class="line">    <span class="comment">/**</span></span><br><span 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> key      键</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@param</span> value    值</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@param</span> time     时间</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@param</span> timeUnit 类型</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@return</span> true成功 false 失败</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_">set</span><span class="params">(String key, Object value, <span class="type">long</span> time, TimeUnit timeUnit)</span> &#123;</span><br><span class="line">        <span class="keyword">try</span> &#123;</span><br><span class="line">            <span class="keyword">if</span> (time &gt; <span class="number">0</span>) &#123;</span><br><span class="line">                redisTemplate.opsForValue().set(key, value, time, timeUnit);</span><br><span class="line">            &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">                set(key, value);</span><br><span class="line">            &#125;</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 e) &#123;</span><br><span class="line">            e.printStackTrace();</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"></span><br><span class="line">    <span class="comment">// ================================Map=================================</span></span><br><span class="line"></span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     * HashGet</span></span><br><span class="line"><span class="comment">     *</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@param</span> key  键 不能为null</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@param</span> item 项 不能为null</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_">hget</span><span class="params">(String key, String item)</span> &#123;</span><br><span class="line">        <span class="keyword">return</span> redisTemplate.opsForHash().get(key, item);</span><br><span 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">     * 获取hashKey对应的所有键值</span></span><br><span class="line"><span class="comment">     *</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@param</span> key 键</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> Map&lt;Object, Object&gt; <span class="title function_">hmget</span><span class="params">(String key)</span> &#123;</span><br><span class="line">        <span class="keyword">return</span> redisTemplate.opsForHash().entries(key);</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">     * HashSet</span></span><br><span class="line"><span class="comment">     *</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@param</span> key 键</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> true 成功 false 失败</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_">hmset</span><span class="params">(String key, Map&lt;String, Object&gt; map)</span> &#123;</span><br><span class="line">        <span class="keyword">try</span> &#123;</span><br><span class="line">            redisTemplate.opsForHash().putAll(key, map);</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 e) &#123;</span><br><span class="line">            e.printStackTrace();</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"></span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     * HashSet 并设置时间</span></span><br><span class="line"><span class="comment">     *</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@param</span> key  键</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">@param</span> time 时间(秒)</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@return</span> true成功 false失败</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_">hmset</span><span class="params">(String key, Map&lt;String, Object&gt; map, <span class="type">long</span> time)</span> &#123;</span><br><span class="line">        <span class="keyword">try</span> &#123;</span><br><span class="line">            redisTemplate.opsForHash().putAll(key, map);</span><br><span class="line">            <span class="keyword">if</span> (time &gt; <span class="number">0</span>) &#123;</span><br><span class="line">                expire(key, time);</span><br><span class="line">            &#125;</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 e) &#123;</span><br><span class="line">            e.printStackTrace();</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"></span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     * 向一张hash表中放入数据,如果不存在将创建</span></span><br><span class="line"><span class="comment">     *</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@param</span> key   键</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@param</span> item  项</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@param</span> value 值</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@return</span> true 成功 false失败</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_">hset</span><span class="params">(String key, String item, Object value)</span> &#123;</span><br><span class="line">        <span class="keyword">try</span> &#123;</span><br><span class="line">            redisTemplate.opsForHash().put(key, item, value);</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 e) &#123;</span><br><span class="line">            e.printStackTrace();</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"></span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     * 向一张hash表中放入数据,如果不存在将创建</span></span><br><span class="line"><span class="comment">     *</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@param</span> key   键</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@param</span> item  项</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@param</span> value 值</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@param</span> time  时间(秒) 注意:如果已存在的hash表有时间,这里将会替换原有的时间</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@return</span> true 成功 false失败</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_">hset</span><span class="params">(String key, String item, Object value, <span class="type">long</span> time)</span> &#123;</span><br><span class="line">        <span class="keyword">try</span> &#123;</span><br><span class="line">            redisTemplate.opsForHash().put(key, item, value);</span><br><span class="line">            <span class="keyword">if</span> (time &gt; <span class="number">0</span>) &#123;</span><br><span class="line">                expire(key, time);</span><br><span class="line">            &#125;</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 e) &#123;</span><br><span class="line">            e.printStackTrace();</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"></span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     * 删除hash表中的值</span></span><br><span class="line"><span class="comment">     *</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@param</span> key  键 不能为null</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@param</span> item 项 可以使多个 不能为null</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_">hdel</span><span class="params">(String key, Object... item)</span> &#123;</span><br><span class="line">        redisTemplate.opsForHash().delete(key, item);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     * 判断hash表中是否有该项的值</span></span><br><span class="line"><span class="comment">     *</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@param</span> key  键 不能为null</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@param</span> item 项 不能为null</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@return</span> true 存在 false不存在</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_">hHasKey</span><span class="params">(String key, String item)</span> &#123;</span><br><span class="line">        <span class="keyword">return</span> redisTemplate.opsForHash().hasKey(key, item);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     * hash递增 如果不存在,就会创建一个 并把新增后的值返回</span></span><br><span class="line"><span class="comment">     *</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@param</span> key  键</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@param</span> item 项</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@param</span> by   要增加几(大于0)</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">double</span> <span class="title function_">hincr</span><span class="params">(String key, String item, <span class="type">double</span> by)</span> &#123;</span><br><span class="line">        <span class="keyword">return</span> redisTemplate.opsForHash().increment(key, item, by);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     * hash递减</span></span><br><span class="line"><span class="comment">     *</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@param</span> key  键</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@param</span> item 项</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@param</span> by   要减少记(小于0)</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">double</span> <span class="title function_">hdecr</span><span class="params">(String key, String item, <span class="type">double</span> by)</span> &#123;</span><br><span class="line">        <span class="keyword">return</span> redisTemplate.opsForHash().increment(key, item, -by);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// ============================set=============================</span></span><br><span class="line"></span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     * 根据key获取Set中的所有值</span></span><br><span class="line"><span class="comment">     *</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@param</span> key 键</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> Set&lt;Object&gt; <span class="title function_">sGet</span><span class="params">(String key)</span> &#123;</span><br><span class="line">        <span class="keyword">try</span> &#123;</span><br><span class="line">            <span class="keyword">return</span> redisTemplate.opsForSet().members(key);</span><br><span class="line">        &#125; <span class="keyword">catch</span> (Exception e) &#123;</span><br><span class="line">            e.printStackTrace();</span><br><span class="line">            <span class="keyword">return</span> <span class="literal">null</span>;</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     * 根据value从一个set中查询,是否存在</span></span><br><span class="line"><span class="comment">     *</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@param</span> key   键</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@param</span> value 值</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@return</span> true 存在 false不存在</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_">sHasKey</span><span class="params">(String key, Object value)</span> &#123;</span><br><span class="line">        <span class="keyword">try</span> &#123;</span><br><span class="line">            <span class="keyword">return</span> redisTemplate.opsForSet().isMember(key, value);</span><br><span class="line">        &#125; <span class="keyword">catch</span> (Exception e) &#123;</span><br><span class="line">            e.printStackTrace();</span><br><span class="line">            <span class="keyword">return</span> <span class="literal">false</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">     * 将数据放入set缓存</span></span><br><span class="line"><span class="comment">     *</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@param</span> key    键</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@param</span> values 值 可以是多个</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">long</span> <span class="title function_">sSet</span><span class="params">(String key, Object... values)</span> &#123;</span><br><span class="line">        <span class="keyword">try</span> &#123;</span><br><span class="line">            <span class="keyword">return</span> redisTemplate.opsForSet().add(key, values);</span><br><span class="line">        &#125; <span class="keyword">catch</span> (Exception e) &#123;</span><br><span class="line">            e.printStackTrace();</span><br><span class="line">            <span class="keyword">return</span> <span class="number">0</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">     * 将set数据放入缓存</span></span><br><span class="line"><span class="comment">     *</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@param</span> key    键</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@param</span> time   时间(秒)</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@param</span> values 值 可以是多个</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">long</span> <span class="title function_">sSetAndTime</span><span class="params">(String key, <span class="type">long</span> time, Object... values)</span> &#123;</span><br><span class="line">        <span class="keyword">try</span> &#123;</span><br><span class="line">            <span class="type">Long</span> <span class="variable">count</span> <span class="operator">=</span> redisTemplate.opsForSet().add(key, values);</span><br><span class="line">            <span class="keyword">if</span> (time &gt; <span class="number">0</span>) &#123;</span><br><span class="line">                expire(key, time);</span><br><span class="line">            &#125;</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">            e.printStackTrace();</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">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     * 获取set缓存的长度</span></span><br><span class="line"><span class="comment">     *</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@param</span> key 键</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">long</span> <span class="title function_">sGetSetSize</span><span class="params">(String key)</span> &#123;</span><br><span class="line">        <span class="keyword">try</span> &#123;</span><br><span class="line">            <span class="keyword">return</span> redisTemplate.opsForSet().size(key);</span><br><span class="line">        &#125; <span class="keyword">catch</span> (Exception e) &#123;</span><br><span class="line">            e.printStackTrace();</span><br><span class="line">            <span class="keyword">return</span> <span class="number">0</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">     * 移除值为value的</span></span><br><span class="line"><span class="comment">     *</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@param</span> key    键</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@param</span> values 值 可以是多个</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">long</span> <span class="title function_">setRemove</span><span class="params">(String key, Object... values)</span> &#123;</span><br><span class="line">        <span class="keyword">try</span> &#123;</span><br><span class="line">            <span class="type">Long</span> <span class="variable">count</span> <span class="operator">=</span> redisTemplate.opsForSet().remove(key, values);</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">            e.printStackTrace();</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">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// ===============================list=================================</span></span><br><span class="line"></span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     * 获取list缓存的内容</span></span><br><span class="line"><span class="comment">     *</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@param</span> key   键</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@param</span> start 开始</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@param</span> end   结束 0 到 -1代表所有值</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;Object&gt; <span class="title function_">lGet</span><span class="params">(String key, <span class="type">long</span> start, <span class="type">long</span> end)</span> &#123;</span><br><span class="line">        <span class="keyword">try</span> &#123;</span><br><span class="line">            <span class="keyword">return</span> redisTemplate.opsForList().range(key, start, end);</span><br><span class="line">        &#125; <span class="keyword">catch</span> (Exception e) &#123;</span><br><span class="line">            e.printStackTrace();</span><br><span class="line">            <span class="keyword">return</span> <span class="literal">null</span>;</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     * 获取list缓存的长度</span></span><br><span class="line"><span class="comment">     *</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@param</span> key 键</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">long</span> <span class="title function_">lGetListSize</span><span class="params">(String key)</span> &#123;</span><br><span class="line">        <span class="keyword">try</span> &#123;</span><br><span class="line">            <span class="keyword">return</span> redisTemplate.opsForList().size(key);</span><br><span class="line">        &#125; <span class="keyword">catch</span> (Exception e) &#123;</span><br><span class="line">            e.printStackTrace();</span><br><span class="line">            <span class="keyword">return</span> <span class="number">0</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">     * 通过索引 获取list中的值</span></span><br><span class="line"><span class="comment">     *</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@param</span> key   键</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@param</span> index 索引 index&gt;=0时， 0 表头，1 第二个元素，依次类推；index&lt;0时，-1，表尾，-2倒数第二个元素，依次类推</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_">lGetIndex</span><span class="params">(String key, <span class="type">long</span> index)</span> &#123;</span><br><span class="line">        <span class="keyword">try</span> &#123;</span><br><span class="line">            <span class="keyword">return</span> redisTemplate.opsForList().index(key, index);</span><br><span class="line">        &#125; <span class="keyword">catch</span> (Exception e) &#123;</span><br><span class="line">            e.printStackTrace();</span><br><span class="line">            <span class="keyword">return</span> <span class="literal">null</span>;</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     * 将list放入缓存</span></span><br><span class="line"><span class="comment">     *</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@param</span> key   键</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@param</span> value 值</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">boolean</span> <span class="title function_">lSet</span><span class="params">(String key, Object value)</span> &#123;</span><br><span class="line">        <span class="keyword">try</span> &#123;</span><br><span class="line">            redisTemplate.opsForList().rightPush(key, value);</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 e) &#123;</span><br><span class="line">            e.printStackTrace();</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"></span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     * 将list放入缓存</span></span><br><span class="line"><span class="comment">     *</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@param</span> key   键</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@param</span> value 值</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@param</span> time  时间(秒)</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">boolean</span> <span class="title function_">lSet</span><span class="params">(String key, Object value, <span class="type">long</span> time)</span> &#123;</span><br><span class="line">        <span class="keyword">try</span> &#123;</span><br><span class="line">            redisTemplate.opsForList().rightPush(key, value);</span><br><span class="line">            <span class="keyword">if</span> (time &gt; <span class="number">0</span>) &#123;</span><br><span class="line">                expire(key, time);</span><br><span class="line">            &#125;</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 e) &#123;</span><br><span class="line">            e.printStackTrace();</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"></span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     * 将list放入缓存</span></span><br><span class="line"><span class="comment">     *</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@param</span> key   键</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@param</span> value 值</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">boolean</span> <span class="title function_">lSet</span><span class="params">(String key, List&lt;Object&gt; value)</span> &#123;</span><br><span class="line">        <span class="keyword">try</span> &#123;</span><br><span class="line">            redisTemplate.opsForList().rightPushAll(key, value);</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 e) &#123;</span><br><span class="line">            e.printStackTrace();</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"></span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     * 将list放入缓存</span></span><br><span class="line"><span class="comment">     *</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@param</span> key   键</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@param</span> value 值</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@param</span> time  时间(秒)</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">boolean</span> <span class="title function_">lSet</span><span class="params">(String key, List&lt;Object&gt; value, <span class="type">long</span> time)</span> &#123;</span><br><span class="line">        <span class="keyword">try</span> &#123;</span><br><span class="line">            redisTemplate.opsForList().rightPushAll(key, value);</span><br><span class="line">            <span class="keyword">if</span> (time &gt; <span class="number">0</span>) &#123;</span><br><span class="line">                expire(key, time);</span><br><span class="line">            &#125;</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 e) &#123;</span><br><span class="line">            e.printStackTrace();</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"></span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     * 根据索引修改list中的某条数据</span></span><br><span class="line"><span class="comment">     *</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@param</span> key   键</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@param</span> index 索引</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@param</span> value 值</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">boolean</span> <span class="title function_">lUpdateIndex</span><span class="params">(String key, <span class="type">long</span> index, Object value)</span> &#123;</span><br><span class="line">        <span class="keyword">try</span> &#123;</span><br><span class="line">            redisTemplate.opsForList().set(key, index, value);</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 e) &#123;</span><br><span class="line">            e.printStackTrace();</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"></span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     * 移除N个值为value</span></span><br><span class="line"><span class="comment">     *</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@param</span> key   键</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@param</span> count 移除多少个</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@param</span> value 值</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">long</span> <span class="title function_">lRemove</span><span class="params">(String key, <span class="type">long</span> count, Object value)</span> &#123;</span><br><span class="line">        <span class="keyword">try</span> &#123;</span><br><span class="line">            <span class="keyword">return</span> redisTemplate.opsForList().remove(key, count, value);</span><br><span class="line">        &#125; <span class="keyword">catch</span> (Exception e) &#123;</span><br><span class="line">            e.printStackTrace();</span><br><span class="line">            <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@param</span> prefix 前缀</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@param</span> ids    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_">delByKeys</span><span class="params">(String prefix, Set&lt;Long&gt; ids)</span> &#123;</span><br><span class="line">        Set&lt;String&gt; keys = <span class="keyword">new</span> <span class="title class_">HashSet</span>&lt;&gt;();</span><br><span class="line">        <span class="keyword">for</span> (Long id : ids) &#123;</span><br><span class="line">            keys.addAll(redisTemplate.keys(<span class="keyword">new</span> <span class="title class_">StringBuffer</span>(prefix).append(id).toString()));</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="type">long</span> <span class="variable">count</span> <span class="operator">=</span> redisTemplate.delete(keys);</span><br><span class="line">        <span class="comment">// 此处提示可自行删除</span></span><br><span class="line">        log.debug(<span class="string">&quot;--------------------------------------------&quot;</span>);</span><br><span class="line">        log.debug(<span class="string">&quot;成功删除缓存：&#123;&#125;&quot;</span>, keys.toString());</span><br><span class="line">        log.debug(<span class="string">&quot;缓存删除数量：&#123;&#125;个&quot;</span>, count);</span><br><span class="line">        log.debug(<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>]]></content:encoded>
            
            
            <category domain="https://yww52.com/categories/%E6%88%91%E7%9A%84%E6%8A%80%E6%9C%AF%E5%AD%A6%E4%B9%A0%E7%89%A9%E8%AF%AD%E6%9E%9C%E7%84%B6%E6%9C%89%E9%97%AE%E9%A2%98/">我的技术学习物语果然有问题</category>
            
            <category domain="https://yww52.com/categories/%E6%88%91%E7%9A%84%E6%8A%80%E6%9C%AF%E5%AD%A6%E4%B9%A0%E7%89%A9%E8%AF%AD%E6%9E%9C%E7%84%B6%E6%9C%89%E9%97%AE%E9%A2%98/Spring/">Spring</category>
            
            
            
            <comments>https://yww52.com/posts/3cd0c9b6/#disqus_thread</comments>
            
        </item>
        
        <item>
            <title>剑指Offer</title>
            <link>https://yww52.com/posts/dd921d98/</link>
            <guid>https://yww52.com/posts/dd921d98/</guid>
            <pubDate>Thu, 18 Mar 2021 16:00:00 GMT</pubDate>
            
            <description>剑指Offer上面的题目，记录一下解题的过程</description>
            
            
            
            <content:encoded><![CDATA[<img src="https://img.yww52.com/2021/3/2021-3-19top_img.jpg" alt="剑指Offer" style="max-width:100%;height:auto;margin-bottom:15px;"
  /><h1 id="3-数组中重复的数字"><a href="#3-数组中重复的数字" class="headerlink" title="3.数组中重复的数字"></a>3.数组中重复的数字</h1><h2 id="排序遍历"><a href="#排序遍历" class="headerlink" title="排序遍历"></a>排序遍历</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></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 class="keyword">public</span> <span class="type">int</span> <span class="title function_">findRepeatNumber</span><span class="params">(<span class="type">int</span>[] nums)</span> &#123;</span><br><span class="line">        Arrays.sort(nums);</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; nums.length; i++) &#123;</span><br><span class="line">            <span class="keyword">if</span> (nums[i] == nums[i - <span class="number">1</span>]) &#123;</span><br><span class="line">                <span class="keyword">return</span> nums[i];</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h2 id="Set去重"><a href="#Set去重" class="headerlink" title="Set去重"></a>Set去重</h2><p>可以将该数组丢到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></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 class="keyword">public</span> <span class="type">int</span> <span class="title function_">findRepeatNumber</span><span class="params">(<span class="type">int</span>[] nums)</span> &#123;</span><br><span class="line">        Set&lt;Integer&gt; set = <span class="keyword">new</span> <span class="title class_">HashSet</span>&lt;&gt;();</span><br><span class="line">        <span class="keyword">for</span> (<span class="type">int</span> i : nums) &#123;</span><br><span class="line">            <span class="keyword">if</span> (!set.add(i)) &#123;</span><br><span class="line">                <span class="keyword">return</span> i;</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">return</span> <span class="number">0</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>跟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></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 class="keyword">public</span> <span class="type">int</span> <span class="title function_">findRepeatNumber</span><span class="params">(<span class="type">int</span>[] nums)</span> &#123;</span><br><span class="line">        HashMap&lt;Integer,Integer&gt; map = <span class="keyword">new</span> <span class="title class_">HashMap</span>&lt;&gt;();</span><br><span class="line">        <span class="keyword">for</span> (<span class="type">int</span> i : nums) &#123;</span><br><span class="line">            <span class="keyword">if</span> (map.containsKey(i)) &#123;</span><br><span class="line">                <span class="keyword">return</span> i;</span><br><span class="line">            &#125;</span><br><span class="line">            map.put(i,i);</span><br><span class="line">        &#125;</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">&#125;</span><br></pre></td></tr></table></figure><h2 id="原地置换（类似桶排序）"><a href="#原地置换（类似桶排序）" class="headerlink" title="原地置换（类似桶排序）"></a>原地置换（类似桶排序）</h2><p>遍历数组，将每一个数放在与它等值的下标下，因为题目说了数字都在1~n - 1，所以当该下标出现了与它一样的数字，代表这个数字是重复的。</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br></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 class="keyword">public</span> <span class="type">int</span> <span class="title function_">findRepeatNumber</span><span class="params">(<span class="type">int</span>[] nums)</span> &#123;</span><br><span class="line">        <span class="keyword">for</span> (<span class="type">int</span> <span class="variable">i</span> <span class="operator">=</span> <span class="number">0</span>; i &lt; nums.length; i++) &#123;</span><br><span class="line">           <span class="keyword">while</span>(nums[i] != i) &#123;</span><br><span class="line">                <span class="keyword">if</span>(nums[i] == nums[nums[i]]) <span class="keyword">return</span> nums[i];</span><br><span class="line">                <span class="type">int</span> <span class="variable">temp</span> <span class="operator">=</span> nums[i];</span><br><span class="line">                nums[i] = nums[temp];</span><br><span class="line">                nums[temp] = temp;</span><br><span class="line">           &#125;</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">return</span> <span class="number">0</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>题目详情可以参考<a href="https://www.acwing.com/problem/content/description/15/">https://www.acwing.com/problem/content/description/15/</a>。</p><p>这里可以使用上述的哈希和Set集合来进行查找，或者在创建一个数组，然后使用桶排序找出重复的元素。</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></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 class="keyword">public</span> <span class="type">int</span> <span class="title function_">duplicateInArray</span><span class="params">(<span class="type">int</span>[] nums)</span> &#123;</span><br><span class="line">        <span class="comment">// 快慢指针寻找相遇点</span></span><br><span class="line">        <span class="type">int</span> <span class="variable">fast</span> <span class="operator">=</span> <span class="number">0</span>；</span><br><span class="line">        <span class="type">int</span> <span class="variable">slow</span> <span class="operator">=</span> <span class="number">0</span>;</span><br><span class="line">        <span class="keyword">while</span> (fast == <span class="number">0</span> || fast != slow) &#123;</span><br><span class="line">            fast = nums[nums[fast]];</span><br><span class="line">            slow = nums[slow];</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="comment">// 寻找入环点</span></span><br><span class="line">        fast = <span class="number">0</span>;</span><br><span class="line">        <span class="keyword">while</span> (fast != slow) &#123;</span><br><span class="line">            fast = nums[fast];</span><br><span class="line">            slow = nums[slow];</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">return</span> slow;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><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></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 class="keyword">public</span> <span class="type">int</span> <span class="title function_">duplicateInArray</span><span class="params">(<span class="type">int</span>[] nums)</span> &#123;</span><br><span class="line">        <span class="type">int</span> <span class="variable">l</span> <span class="operator">=</span> <span class="number">1</span>;</span><br><span class="line">        <span class="type">int</span> <span class="variable">r</span> <span class="operator">=</span> nums.length - <span class="number">1</span>;</span><br><span class="line">        <span class="keyword">while</span> (l &lt; r) &#123;</span><br><span class="line">            <span class="type">int</span> <span class="variable">mid</span> <span class="operator">=</span> l + r &gt;&gt; <span class="number">1</span>;    </span><br><span class="line">            <span class="comment">// 记录小于中间值的数的个数</span></span><br><span class="line">            <span class="type">int</span> <span class="variable">count</span> <span class="operator">=</span> <span class="number">0</span>;  </span><br><span class="line">            <span class="keyword">for</span>(<span class="type">int</span> i : nums) &#123;</span><br><span class="line">                <span class="keyword">if</span>(l &lt;= i &amp;&amp; i &lt;= mid) count++;</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>(count &gt; mid - l + <span class="number">1</span>) r = mid;</span><br><span class="line">            <span class="keyword">else</span> l = mid + <span class="number">1</span>;</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">return</span> l;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h1 id="4-二维数组中的查找"><a href="#4-二维数组中的查找" class="headerlink" title="4.二维数组中的查找"></a>4.二维数组中的查找</h1><h2 id="数组遍历"><a href="#数组遍历" class="headerlink" title="数组遍历"></a>数组遍历</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></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 class="keyword">public</span> <span class="type">boolean</span> <span class="title function_">findNumberIn2DArray</span><span class="params">(<span class="type">int</span>[][] matrix, <span class="type">int</span> target)</span> &#123;</span><br><span class="line">        <span class="keyword">for</span> (<span class="type">int</span>[] i : matrix) &#123;</span><br><span class="line">            <span class="keyword">for</span> (<span class="type">int</span> j : i) &#123;</span><br><span class="line">                <span class="keyword">if</span> (j == target) <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="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><h2 id="二维查找"><a href="#二维查找" class="headerlink" title="二维查找"></a>二维查找</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></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 class="keyword">public</span> <span class="type">boolean</span> <span class="title function_">findNumberIn2DArray</span><span class="params">(<span class="type">int</span>[][] matrix, <span class="type">int</span> target)</span> &#123;</span><br><span class="line">        <span class="keyword">if</span> (matrix.length == <span class="number">0</span> || matrix[<span class="number">0</span>].length == <span class="number">0</span>) <span class="keyword">return</span> <span class="literal">false</span>;</span><br><span class="line">        <span class="type">int</span> <span class="variable">i</span> <span class="operator">=</span> <span class="number">0</span>;</span><br><span class="line">        <span class="comment">// 从最后一列开始，排除最后一列小于给定值的行</span></span><br><span class="line">        <span class="type">int</span> <span class="variable">j</span> <span class="operator">=</span> matrix[<span class="number">0</span>].length - <span class="number">1</span>;</span><br><span class="line">        <span class="keyword">while</span>(i &lt; matrix.length &amp;&amp; j &gt;= <span class="number">0</span>) &#123;</span><br><span class="line">            <span class="keyword">if</span> (matrix[i][j] == target) <span class="keyword">return</span> <span class="literal">true</span>;</span><br><span class="line">            <span class="comment">// 如果值大于给定值，代表当前行不存在给定值，所以遍历下一行</span></span><br><span class="line">            <span class="keyword">if</span> (matrix[i][j] &gt; target) j--;</span><br><span class="line">            <span class="keyword">else</span> i++;</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><h1 id="5-替换空格"><a href="#5-替换空格" class="headerlink" title="5.替换空格"></a>5.替换空格</h1><h2 id="replaceAll"><a href="#replaceAll" class="headerlink" title="replaceAll"></a>replaceAll</h2><p>String类里就自带一个替换的方法了，所以可以直接进行替换。</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><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">class</span> <span class="title class_">Solution</span> &#123;</span><br><span class="line">    <span class="keyword">public</span> String <span class="title function_">replaceSpace</span><span class="params">(String s)</span> &#123;</span><br><span class="line">        <span class="keyword">return</span> s.replaceAll(<span class="string">&quot; &quot;</span>,<span class="string">&quot;%20&quot;</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>直接遍历字符串，出现了空格就替换，不然就不替换。</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><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">class</span> <span class="title class_">Solution</span> &#123;</span><br><span class="line">    <span class="keyword">public</span> String <span class="title function_">replaceSpace</span><span class="params">(String s)</span> &#123;</span><br><span class="line">        <span class="type">StringBuilder</span> <span class="variable">res</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">StringBuilder</span>();</span><br><span class="line">        <span class="type">char</span>[] str = s.toCharArray();</span><br><span class="line">        <span class="keyword">for</span> (<span class="type">char</span> c : str) &#123;</span><br><span class="line">            <span class="keyword">if</span> (c == <span class="string">&#x27; &#x27;</span>) res.append(<span class="string">&quot;%20&quot;</span>);</span><br><span class="line">            <span class="keyword">else</span> res.append(c);</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">return</span> res.toString();</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>要是不能用动态的增加字符串，就只能先遍历一遍字符串，然后判断答案的长度，然后在创建一个新的数组，然后依次放入新数组就好了。</p><h1 id="6-从尾到头打印链表"><a href="#6-从尾到头打印链表" class="headerlink" title="6. 从尾到头打印链表"></a>6. 从尾到头打印链表</h1><h2 id="反转链表后输出"><a href="#反转链表后输出" class="headerlink" title="反转链表后输出"></a>反转链表后输出</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><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">class</span> <span class="title class_">Solution</span> &#123;</span><br><span class="line">    <span class="keyword">public</span> <span class="type">int</span>[] reversePrint(ListNode head) &#123;</span><br><span class="line">        <span class="keyword">if</span> (head == <span class="literal">null</span>) <span class="keyword">return</span> <span class="keyword">new</span> <span class="title class_">int</span>[<span class="number">0</span>];</span><br><span class="line">        <span class="type">ListNode</span> <span class="variable">newhead</span> <span class="operator">=</span> reverse(head);</span><br><span class="line">        List&lt;Integer&gt; list = <span class="keyword">new</span> <span class="title class_">ArrayList</span>&lt;&gt;();</span><br><span class="line">        <span class="keyword">while</span>(newhead != <span class="literal">null</span>) &#123;</span><br><span class="line">            list.add(newhead.val);</span><br><span class="line">            newhead = newhead.next;</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="type">int</span>[] res = <span class="keyword">new</span> <span class="title class_">int</span>[list.size()];</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; list.size(); i++) &#123;</span><br><span class="line">            res[i] = list.get(i);</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">return</span> res;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">public</span> ListNode <span class="title function_">reverse</span><span class="params">(ListNode head)</span> &#123;</span><br><span class="line">        <span class="keyword">if</span> (head.next == <span class="literal">null</span>) <span class="keyword">return</span> head;</span><br><span class="line">        <span class="type">ListNode</span> <span class="variable">newhead</span> <span class="operator">=</span> reverse(head.next);</span><br><span class="line">        head.next.next = head;</span><br><span class="line">        head.next = <span class="literal">null</span>;</span><br><span class="line">        <span class="keyword">return</span> newhead;</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>这里可以使用类似后序遍历的操作，先递归进去然后添加值，这样就类似栈了，直接从后到前输出。或者这里定义一个栈也可以进行。</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><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">class</span> <span class="title class_">Solution</span> &#123;</span><br><span class="line">    List&lt;Integer&gt; list = <span class="keyword">new</span> <span class="title class_">ArrayList</span>&lt;&gt;();</span><br><span class="line">    <span class="keyword">public</span> <span class="type">int</span>[] reversePrint(ListNode head) &#123;</span><br><span class="line">        <span class="keyword">if</span> (head == <span class="literal">null</span>) <span class="keyword">return</span> <span class="keyword">new</span> <span class="title class_">int</span>[<span class="number">0</span>];</span><br><span class="line">        reverse(head);</span><br><span class="line">        <span class="type">int</span>[] res = <span class="keyword">new</span> <span class="title class_">int</span>[list.size()];</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; list.size(); i++) &#123;</span><br><span class="line">            res[i] = list.get(i);</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">return</span> res;</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_">reverse</span><span class="params">(ListNode head)</span> &#123;</span><br><span class="line">        <span class="keyword">if</span> (head == <span class="literal">null</span>) <span class="keyword">return</span>;</span><br><span class="line">        reverse(head.next);</span><br><span class="line">        list.add(head.val);</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>其实可以看到，其实从前遍历，然后反向输出就行了，其实并不用一定是正向的数据。</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><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">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="type">int</span>[] reversePrint(ListNode head) &#123;</span><br><span class="line">        <span class="keyword">if</span> (head == <span class="literal">null</span>) <span class="keyword">return</span> <span class="keyword">new</span> <span class="title class_">int</span>[<span class="number">0</span>];</span><br><span class="line">        List&lt;Integer&gt; list = <span class="keyword">new</span> <span class="title class_">ArrayList</span>&lt;&gt;();</span><br><span class="line">        <span class="keyword">while</span> (head != <span class="literal">null</span>) &#123;</span><br><span class="line">            list.add(head.val);</span><br><span class="line">            head = head.next;</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="type">int</span>[] res = <span class="keyword">new</span> <span class="title class_">int</span>[list.size()];</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; list.size(); i++) &#123;</span><br><span class="line">            res[i] = list.get(list.size() - i - <span class="number">1</span>);</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">return</span> res;</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><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></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="type">int</span>[] reversePrint(ListNode head) &#123;</span><br><span class="line">        <span class="keyword">if</span> (head == <span class="literal">null</span>) <span class="keyword">return</span> <span class="keyword">new</span> <span class="title class_">int</span>[<span class="number">0</span>];</span><br><span class="line">        <span class="type">ListNode</span> <span class="variable">node</span> <span class="operator">=</span> head;</span><br><span class="line">        <span class="type">int</span> <span class="variable">size</span> <span class="operator">=</span> <span class="number">0</span>;</span><br><span class="line">        <span class="keyword">while</span>(node != <span class="literal">null</span>) &#123;</span><br><span class="line">            size++;</span><br><span class="line">            node = node.next;</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="type">int</span>[] res = <span class="keyword">new</span> <span class="title class_">int</span>[size];</span><br><span class="line">        <span class="keyword">for</span> (<span class="type">int</span> <span class="variable">i</span> <span class="operator">=</span> size - <span class="number">1</span>; i &gt; -<span class="number">1</span>;i--) &#123;</span><br><span class="line">             res[i] = head.val;</span><br><span class="line">             head = head.next;</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">return</span> res;</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><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><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">class</span> <span class="title class_">Solution</span> &#123;</span><br><span class="line">    <span class="keyword">public</span> <span class="type">int</span>[] reversePrint(ListNode head) &#123;</span><br><span class="line">        <span class="keyword">if</span> (head == <span class="literal">null</span>) <span class="keyword">return</span> <span class="keyword">new</span> <span class="title class_">int</span>[<span class="number">0</span>];</span><br><span class="line">        Stack&lt;ListNode&gt; stack = <span class="keyword">new</span> <span class="title class_">Stack</span>&lt;&gt;();</span><br><span class="line">        <span class="type">ListNode</span> <span class="variable">cur</span> <span class="operator">=</span> head;</span><br><span class="line">        <span class="keyword">while</span> (cur != <span class="literal">null</span>) &#123;</span><br><span class="line">            stack.push(cur);</span><br><span class="line">            cur = cur.next;</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="type">int</span>[] res = <span class="keyword">new</span> <span class="title class_">int</span>[stack.size()];</span><br><span class="line">        <span class="type">int</span> <span class="variable">i</span> <span class="operator">=</span> <span class="number">0</span>;</span><br><span class="line">        <span class="keyword">while</span> (!stack.isEmpty()) &#123;</span><br><span class="line">            res[i++] = stack.pop().val;</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">return</span> res;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h1 id="7-重建二叉树"><a href="#7-重建二叉树" class="headerlink" title="7.重建二叉树"></a>7.重建二叉树</h1><p>这题主要是要划分区间，划分好左子树和右子树的区间。</p><p>前序遍历的区间可以分为三个。根的值，左子树的值，右子树的值。</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><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><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">class</span> <span class="title class_">Solution</span> &#123;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">private</span> <span class="type">int</span> <span class="variable">ino</span> <span class="operator">=</span> <span class="number">0</span>;</span><br><span class="line">    <span class="keyword">private</span> <span class="type">int</span> <span class="variable">pre</span> <span class="operator">=</span> <span class="number">0</span>;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">public</span> TreeNode <span class="title function_">buildTree</span><span class="params">(<span class="type">int</span>[] preorder, <span class="type">int</span>[] inorder)</span> &#123;</span><br><span class="line">        <span class="keyword">return</span> build(preorder, inorder, Integer.MIN_VALUE - <span class="number">1</span>);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">private</span> TreeNode <span class="title function_">build</span><span class="params">(<span class="type">int</span>[] preorder, <span class="type">int</span>[] inorder, <span class="type">long</span> stop)</span> &#123;</span><br><span class="line">        <span class="keyword">if</span> (pre &gt;= preorder.length) <span class="keyword">return</span> <span class="literal">null</span>;</span><br><span class="line">        <span class="comment">// 判断该子树是否碰到根</span></span><br><span class="line">        <span class="keyword">if</span> (inorder[ino] == stop) &#123;</span><br><span class="line">            ino++;</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 class="type">TreeNode</span> <span class="variable">node</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">TreeNode</span>(preorder[pre++]);</span><br><span class="line">        <span class="comment">// 左子树的范围，到当前节点值，即到子树的根值(按前序遍历的数组)</span></span><br><span class="line">        node.left = build(preorder,inorder,node.val);</span><br><span class="line">        <span class="comment">// 右子树的范围，从当前节点到最后</span></span><br><span class="line">        node.right = build(preorder,inorder,stop);</span><br><span class="line"></span><br><span class="line">        <span class="keyword">return</span> node;</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><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><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">class</span> <span class="title class_">Solution</span> &#123; </span><br><span class="line">    </span><br><span class="line">    <span class="keyword">private</span> <span class="type">int</span> <span class="variable">ino</span> <span class="operator">=</span> <span class="number">0</span>;</span><br><span class="line">    <span class="keyword">private</span> <span class="type">int</span> <span class="variable">pos</span> <span class="operator">=</span> <span class="number">0</span>;</span><br><span class="line">    </span><br><span class="line">    <span class="keyword">public</span> TreeNode <span class="title function_">buildTree</span><span class="params">(<span class="type">int</span>[] inorder, <span class="type">int</span>[] postorder)</span> &#123;</span><br><span class="line">        pos = postorder.length - <span class="number">1</span>;</span><br><span class="line">        ino = inorder.length - <span class="number">1</span>;</span><br><span class="line">        <span class="keyword">return</span> build(inorder,postorder,Integer.MIN_VALUE - <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> TreeNode <span class="title function_">build</span><span class="params">(<span class="type">int</span>[] inorder, <span class="type">int</span>[] postorder,<span class="type">long</span> stop)</span> &#123;</span><br><span class="line">        <span class="keyword">if</span> (pos == -<span class="number">1</span>) <span class="keyword">return</span> <span class="literal">null</span>;</span><br><span class="line">        <span class="keyword">if</span> (inorder[ino] == stop) &#123;</span><br><span class="line">            ino--;</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 class="type">TreeNode</span> <span class="variable">node</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">TreeNode</span>(postorder[pos--]);</span><br><span class="line">        node.right = build(inorder,postorder,node.val);</span><br><span class="line">        node.left = build(inorder,postorder,stop);</span><br><span class="line">        <span class="keyword">return</span> node;</span><br><span class="line">    &#125;</span><br><span class="line">    </span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h1 id="8-二叉树的下一个节点"><a href="#8-二叉树的下一个节点" class="headerlink" title="8.二叉树的下一个节点"></a>8.二叉树的下一个节点</h1><p>题目详情可以参考<a href="https://www.acwing.com/problem/content/31/">https://www.acwing.com/problem/content/31/</a></p><p>这里就是直接找下一个节点就好了。</p><p>于是会有两种情况。</p><ol><li>当前节点有右子树。那么该节点的下一个节点就是右子树里的最左子节点。</li><li>当前节点没有右子树，那就要追溯回原来第一个是其father左儿子的节点。<ul><li>该节点是父节点的左孩子，这样下一个节点就是它的father</li><li>该节点是父孩子的右孩子，这样下一个节点就是该左子树的父节点</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><span class="line">10</span><br><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">class</span> <span class="title class_">Solution</span> &#123;</span><br><span class="line">    <span class="keyword">public</span> TreeNode <span class="title function_">inorderSuccessor</span><span class="params">(TreeNode p)</span> &#123;</span><br><span class="line">        <span class="comment">// 存在右子树的情况</span></span><br><span class="line">        <span class="keyword">if</span> (p.right != <span class="literal">null</span>) &#123;</span><br><span class="line">            p = p.right;</span><br><span class="line">            <span class="keyword">while</span>(p.left != <span class="literal">null</span>) p = p.left;</span><br><span class="line">            <span class="keyword">return</span> p;</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>(p.father != <span class="literal">null</span> &amp;&amp; p == p.father.right) p = p.father;</span><br><span class="line">        </span><br><span class="line">        <span class="keyword">return</span> p.father;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h1 id="9-用两个栈实现队列"><a href="#9-用两个栈实现队列" class="headerlink" title="9.用两个栈实现队列"></a>9.用两个栈实现队列</h1><p>这里先来分析一下这两个栈的作用。栈是先进后出的数据结构，而队列是先进先出的，所以无法进行互通，引入第二个栈是用来存储第一个栈中出栈的元素，这样第二个栈的元素就是第一个栈元素的倒序了，直接出栈就是最先进来的元素了。</p><p>入队就直接压入第一个栈中就可以了。</p><p>主要是要探讨出队的情况。</p><ol><li>当第二个栈中还有元素的时候，代表这些元素是最先进入的元素，所以直接出栈就是之前最先入队的元素了。</li><li>当第二个栈和第一个栈都没有元素了，那就证明队列为空。</li><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><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><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">class</span> <span class="title class_">CQueue</span> &#123;</span><br><span class="line"></span><br><span class="line">    Stack&lt;Integer&gt; stack1;</span><br><span class="line">    Stack&lt;Integer&gt; stack2;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">public</span> <span class="title function_">CQueue</span><span class="params">()</span> &#123;</span><br><span class="line">        stack1 = <span class="keyword">new</span> <span class="title class_">Stack</span>&lt;&gt;();</span><br><span class="line">        stack2 = <span class="keyword">new</span> <span class="title class_">Stack</span>&lt;&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_">appendTail</span><span class="params">(<span class="type">int</span> value)</span> &#123;</span><br><span class="line">        stack1.push(value);</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_">deleteHead</span><span class="params">()</span> &#123;</span><br><span class="line">        <span class="keyword">if</span> (!stack2.isEmpty()) <span class="keyword">return</span> stack2.pop();</span><br><span class="line">        <span class="keyword">if</span> (stack1.isEmpty()) <span class="keyword">return</span> -<span class="number">1</span>;</span><br><span class="line">        <span class="keyword">while</span> (!stack1.isEmpty()) &#123;</span><br><span class="line">            stack2.push(stack1.pop());</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">return</span> stack2.pop();</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"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * Your CQueue object will be instantiated and called as such:</span></span><br><span class="line"><span class="comment"> * CQueue obj = new CQueue();</span></span><br><span class="line"><span class="comment"> * obj.appendTail(value);</span></span><br><span class="line"><span class="comment"> * int param_2 = obj.deleteHead();</span></span><br><span class="line"><span class="comment"> */</span></span><br></pre></td></tr></table></figure><h1 id="10-斐波那契数列"><a href="#10-斐波那契数列" class="headerlink" title="10.斐波那契数列"></a>10.斐波那契数列</h1><p>剑指Offer这题的斐波那契数列是结果取模的，因为返回的是int，除了这个需要注意的，其他的和经常的解法差不多了。</p><h2 id="递归"><a href="#递归" class="headerlink" title="递归"></a>递归</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></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 class="keyword">public</span> <span class="type">int</span> <span class="title function_">fib</span><span class="params">(<span class="type">int</span> n)</span> &#123;</span><br><span class="line">        <span class="keyword">if</span> (n &lt; <span class="number">0</span>) <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">        <span class="keyword">if</span> (n == <span class="number">1</span> || n == <span class="number">2</span>) <span class="keyword">return</span> <span class="number">1</span>;</span><br><span class="line">        <span class="keyword">return</span> (fib(n - <span class="number">1</span>) + fib(n - <span class="number">2</span>)) % <span class="number">1000000007</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><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><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">class</span> <span class="title class_">Solution</span> &#123;</span><br><span class="line">    <span class="keyword">public</span> <span class="type">int</span> <span class="title function_">fib</span><span class="params">(<span class="type">int</span> n)</span> &#123;</span><br><span class="line">        <span class="keyword">if</span> (n == <span class="number">0</span> || n == <span class="number">1</span>) <span class="keyword">return</span> n;</span><br><span class="line">        <span class="type">int</span>[] dp = <span class="keyword">new</span> <span class="title class_">int</span>[n + <span class="number">1</span>];</span><br><span class="line">        dp[<span class="number">1</span>] = <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">2</span>; i &lt;= n; i++) &#123;</span><br><span class="line">            dp[i] = dp[i - <span class="number">1</span>] + dp[i - <span class="number">2</span>];</span><br><span class="line">            <span class="keyword">if</span> (dp[i] &gt;= <span class="number">1000000007</span>) dp[i] -= <span class="number">1000000007</span>;</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">return</span> dp[n];</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>这里是可以考虑dp数组的迭代解法的，但是这里只是一次性达到答案，不会回头了，所以记录之前的状态并没有什么意思，所以可以创建临时变量保存需要的临时变量就可以了。</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><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">class</span> <span class="title class_">Solution</span> &#123;</span><br><span class="line">    <span class="keyword">public</span> <span class="type">int</span> <span class="title function_">fib</span><span class="params">(<span class="type">int</span> n)</span> &#123;</span><br><span class="line">        <span class="keyword">if</span> (n == <span class="number">0</span> || n == <span class="number">1</span>) <span class="keyword">return</span> n;</span><br><span class="line">        <span class="type">int</span> <span class="variable">a</span> <span class="operator">=</span> <span class="number">0</span>;</span><br><span class="line">        <span class="type">int</span> <span class="variable">b</span> <span class="operator">=</span> <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">2</span>; i &lt;= n; i++) &#123;</span><br><span class="line">            <span class="type">int</span> <span class="variable">temp</span> <span class="operator">=</span> b;</span><br><span class="line">            b = a + b;</span><br><span class="line">            a = temp;</span><br><span class="line">            <span class="keyword">if</span> (b &gt;= <span class="number">1000000007</span>) b -= <span class="number">1000000007</span>;</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">return</span> b;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><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></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 class="keyword">public</span> <span class="type">int</span> <span class="title function_">numWays</span><span class="params">(<span class="type">int</span> n)</span> &#123;</span><br><span class="line">        <span class="keyword">if</span> (n &lt;= <span class="number">1</span>) <span class="keyword">return</span> <span class="number">1</span>;</span><br><span class="line">        <span class="keyword">if</span> (n == <span class="number">2</span>) <span class="keyword">return</span> <span class="number">2</span>;</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><br><span class="line">        <span class="type">int</span> <span class="variable">b</span> <span class="operator">=</span> <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">2</span>;i &lt;= n;i++) &#123;</span><br><span class="line">            <span class="type">int</span> <span class="variable">c</span> <span class="operator">=</span> (a + b) % <span class="number">1000000007</span>;</span><br><span class="line">            a = b;</span><br><span class="line">            b = c;</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">return</span> b;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h1 id="11-旋转数组的最小数字"><a href="#11-旋转数组的最小数字" class="headerlink" title="11.旋转数组的最小数字"></a>11.旋转数组的最小数字</h1><h2 id="排序"><a href="#排序" class="headerlink" title="排序"></a>排序</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></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 class="keyword">public</span> <span class="type">int</span> <span class="title function_">minArray</span><span class="params">(<span class="type">int</span>[] numbers)</span> &#123;</span><br><span class="line">        Arrays.sort(numbers);</span><br><span class="line">        <span class="keyword">return</span> numbers[<span class="number">0</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>找到第一个不递增的数字那就是最小的数字了。</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><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">class</span> <span class="title class_">Solution</span> &#123;</span><br><span class="line">    <span class="keyword">public</span> <span class="type">int</span> <span class="title function_">minArray</span><span class="params">(<span class="type">int</span>[] numbers)</span> &#123;</span><br><span class="line">        <span class="keyword">for</span> (<span class="type">int</span> <span class="variable">i</span> <span class="operator">=</span> <span class="number">0</span>;i &lt; numbers.length - <span class="number">1</span>; i++) &#123;</span><br><span class="line">            <span class="keyword">if</span> (numbers[i] &gt; numbers[i + <span class="number">1</span>]) <span class="keyword">return</span> numbers[i + <span class="number">1</span>];</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">return</span> numbers[<span class="number">0</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>二分查找最主要的就是划分区间，需要先分析一下怎样确定好区间。</p><ol><li>中间的数大于最右边的数，代表期间不会单调递增，直接进入右边的区间</li><li>中间的数小于最右边的数，代表期间是单调递增的，直接进入左边的区间</li><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><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">class</span> <span class="title class_">Solution</span> &#123;</span><br><span class="line">    <span class="keyword">public</span> <span class="type">int</span> <span class="title function_">minArray</span><span class="params">(<span class="type">int</span>[] numbers)</span> &#123;</span><br><span class="line">        <span class="type">int</span> <span class="variable">l</span> <span class="operator">=</span> <span class="number">0</span>;</span><br><span class="line">        <span class="type">int</span> <span class="variable">r</span> <span class="operator">=</span> numbers.length - <span class="number">1</span>;</span><br><span class="line">        <span class="keyword">while</span> (l &lt; r) &#123;</span><br><span class="line">            <span class="type">int</span> <span class="variable">mid</span> <span class="operator">=</span> l + r &gt;&gt; <span class="number">1</span>;</span><br><span class="line">            <span class="keyword">if</span> (numbers[mid] &gt; numbers[r]) &#123;</span><br><span class="line">                l = mid + <span class="number">1</span>;</span><br><span class="line">            &#125; <span class="keyword">else</span> <span class="keyword">if</span> (numbers[mid] &lt; numbers[r]) &#123;</span><br><span class="line">                r = mid;</span><br><span class="line">            &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">                r -= <span class="number">1</span>;</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">return</span> numbers[l];</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h1 id="12-矩阵中的路径"><a href="#12-矩阵中的路径" class="headerlink" title="12.矩阵中的路径"></a>12.矩阵中的路径</h1><p>经典的dfs和回溯。</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><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">class</span> <span class="title class_">Solution</span> &#123;</span><br><span class="line">    <span class="keyword">public</span> <span class="type">boolean</span> <span class="title function_">exist</span><span class="params">(<span class="type">char</span>[][] board, String word)</span> &#123;</span><br><span class="line">        <span class="keyword">if</span> (board == <span class="literal">null</span>) <span class="keyword">return</span> <span class="literal">false</span>;</span><br><span class="line">        <span class="type">int</span> <span class="variable">row</span> <span class="operator">=</span> board.length;</span><br><span class="line">        <span class="type">int</span> <span class="variable">col</span> <span class="operator">=</span> board[<span class="number">0</span>].length;</span><br><span class="line">        <span class="comment">// 标记是否经过的数组</span></span><br><span class="line">        <span class="type">boolean</span>[][] isVisited = <span class="keyword">new</span> <span class="title class_">boolean</span>[row][col];</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; row; i++) &#123;</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; col; j++) &#123;</span><br><span class="line">                <span class="comment">// 遍历每个点，确定dfs的起点</span></span><br><span class="line">                <span class="keyword">if</span> (board[i][j] == word.charAt(<span class="number">0</span>) &amp;&amp; dfs(board,word,<span class="number">0</span>,i,j,isVisited)) <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="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_">dfs</span><span class="params">(<span class="type">char</span>[][] board,String word,<span class="type">int</span> n,<span class="type">int</span> x,<span class="type">int</span> y,<span class="type">boolean</span>[][] isVisited)</span>&#123;</span><br><span class="line">        <span class="keyword">if</span> (board[x][y] != word.charAt(n)) <span class="keyword">return</span> <span class="literal">false</span>;</span><br><span class="line">        <span class="keyword">if</span> (n == word.length() - <span class="number">1</span>) <span class="keyword">return</span> <span class="literal">true</span>;</span><br><span class="line">        <span class="comment">// 标记经过</span></span><br><span class="line">        isVisited[x][y] = <span class="literal">true</span>;</span><br><span class="line">        <span class="comment">// 偏移数组，上下左右</span></span><br><span class="line">        <span class="type">int</span>[] dx = &#123;<span class="number">0</span>,<span class="number">0</span>,-<span class="number">1</span>,<span class="number">1</span>&#125;;</span><br><span class="line">        <span class="type">int</span>[] dy = &#123;-<span class="number">1</span>,<span class="number">1</span>,<span class="number">0</span>,<span class="number">0</span>&#125;;</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">4</span>; i++) &#123;</span><br><span class="line">            <span class="type">int</span> <span class="variable">lx</span> <span class="operator">=</span> x + dx[i];</span><br><span class="line">            <span class="type">int</span> <span class="variable">ly</span> <span class="operator">=</span> y + dy[i];</span><br><span class="line">            <span class="comment">// 保证下一步没有越界而且没有经过</span></span><br><span class="line">            <span class="keyword">if</span> (lx &gt;= <span class="number">0</span> &amp;&amp; lx &lt; board.length &amp;&amp; ly &gt;= <span class="number">0</span> &amp;&amp; ly &lt; board[<span class="number">0</span>].length &amp;&amp; !isVisited[lx][ly]) &#123;</span><br><span class="line">                <span class="keyword">if</span> (dfs(board,word,n + <span class="number">1</span>,lx,ly,isVisited)) <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">        isVisited[x][y] = <span class="literal">false</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><h1 id="13-机器人的运动范围"><a href="#13-机器人的运动范围" class="headerlink" title="13.机器人的运动范围"></a>13.机器人的运动范围</h1><h2 id="dfs"><a href="#dfs" class="headerlink" title="dfs"></a>dfs</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">class</span> <span class="title class_">Solution</span> &#123;</span><br><span class="line">    <span class="keyword">public</span> <span class="type">int</span> <span class="title function_">movingCount</span><span class="params">(<span class="type">int</span> m, <span class="type">int</span> n, <span class="type">int</span> k)</span> &#123;</span><br><span class="line">        <span class="keyword">if</span> (m == <span class="number">0</span> || n == <span class="number">0</span>) <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">        <span class="type">boolean</span>[][] isVisited = <span class="keyword">new</span> <span class="title class_">boolean</span>[m][n];</span><br><span class="line">        <span class="keyword">return</span> dfs(<span class="number">0</span>,<span class="number">0</span>,k,isVisited);</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_">dfs</span><span class="params">(<span class="type">int</span> i,<span class="type">int</span> j,<span class="type">int</span> k,<span class="type">boolean</span>[][] isVisited)</span> &#123;</span><br><span class="line">        <span class="keyword">if</span> (i &lt; <span class="number">0</span> || i &gt;= isVisited.length || j &lt; <span class="number">0</span> || j &gt;= isVisited[<span class="number">0</span>].length || isVisited[i][j] || !isAllowed(i,j,k)) <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">        isVisited[i][j] = <span class="literal">true</span>;</span><br><span class="line">        <span class="comment">// 上下左右</span></span><br><span class="line">        <span class="keyword">return</span> dfs(i-<span class="number">1</span>,j,k,isVisited) + dfs(i+<span class="number">1</span>,j,k,isVisited) + dfs(i,j-<span class="number">1</span>,k,isVisited) + dfs(i,j+<span class="number">1</span>,k,isVisited) + <span class="number">1</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="type">boolean</span> <span class="title function_">isAllowed</span><span class="params">(<span class="type">int</span> m, <span class="type">int</span> n, <span class="type">int</span> k)</span> &#123;</span><br><span class="line">        <span class="type">int</span> <span class="variable">sum</span> <span class="operator">=</span> <span class="number">0</span>;</span><br><span class="line">        <span class="keyword">while</span> (m != <span class="number">0</span>) &#123;</span><br><span class="line">            sum += m % <span class="number">10</span>;</span><br><span class="line">            m = m / <span class="number">10</span>;</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">while</span> (n != <span class="number">0</span>) &#123;</span><br><span class="line">            sum += n % <span class="number">10</span>;</span><br><span class="line">            n = n / <span class="number">10</span>;</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">if</span> (sum &lt;= k) <span class="keyword">return</span> <span class="literal">true</span>;</span><br><span class="line">        <span class="keyword">else</span> <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>看看别人的发现判断是否合法不用这么麻烦，还可以优化一下，所以变成下面这样。</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><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">class</span> <span class="title class_">Solution</span> &#123;</span><br><span class="line">    <span class="keyword">public</span> <span class="type">int</span> <span class="title function_">movingCount</span><span class="params">(<span class="type">int</span> m, <span class="type">int</span> n, <span class="type">int</span> k)</span> &#123;</span><br><span class="line">        <span class="keyword">if</span> (m == <span class="number">0</span> || n == <span class="number">0</span>) <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">        <span class="type">boolean</span>[][] isVisited = <span class="keyword">new</span> <span class="title class_">boolean</span>[m][n];</span><br><span class="line">        <span class="keyword">return</span> dfs(<span class="number">0</span>,<span class="number">0</span>,k,isVisited);</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_">dfs</span><span class="params">(<span class="type">int</span> i,<span class="type">int</span> j,<span class="type">int</span> k,<span class="type">boolean</span>[][] isVisited)</span> &#123;</span><br><span class="line">        <span class="keyword">if</span> (i &lt; <span class="number">0</span> || i &gt;= isVisited.length || j &lt; <span class="number">0</span> || j &gt;= isVisited[<span class="number">0</span>].length || isVisited[i][j] || (i/<span class="number">10</span>+i%<span class="number">10</span>+j/<span class="number">10</span>+j%<span class="number">10</span>)&gt;k) <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">        isVisited[i][j] = <span class="literal">true</span>;</span><br><span class="line">        <span class="keyword">return</span> dfs(i-<span class="number">1</span>,j,k,isVisited) + dfs(i+<span class="number">1</span>,j,k,isVisited) + dfs(i,j-<span class="number">1</span>,k,isVisited) + dfs(i,j+<span class="number">1</span>,k,isVisited) + <span class="number">1</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>从0,0开始，其实只用向右和向下就可以走完全部的格子了，所以可以去掉向左和向上的dfs。</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><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">class</span> <span class="title class_">Solution</span> &#123;</span><br><span class="line">    <span class="keyword">public</span> <span class="type">int</span> <span class="title function_">movingCount</span><span class="params">(<span class="type">int</span> m, <span class="type">int</span> n, <span class="type">int</span> k)</span> &#123;</span><br><span class="line">        <span class="keyword">if</span> (m == <span class="number">0</span> || n == <span class="number">0</span>) <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">        <span class="type">boolean</span>[][] isVisited = <span class="keyword">new</span> <span class="title class_">boolean</span>[m][n];</span><br><span class="line">        <span class="keyword">return</span> dfs(<span class="number">0</span>,<span class="number">0</span>,k,isVisited);</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_">dfs</span><span class="params">(<span class="type">int</span> i,<span class="type">int</span> j,<span class="type">int</span> k,<span class="type">boolean</span>[][] isVisited)</span> &#123;</span><br><span class="line">        <span class="keyword">if</span> (i &gt;= isVisited.length || j &gt;= isVisited[<span class="number">0</span>].length || isVisited[i][j] || (i/<span class="number">10</span>+i%<span class="number">10</span>+j/<span class="number">10</span>+j%<span class="number">10</span>)&gt;k) <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">        isVisited[i][j] = <span class="literal">true</span>;</span><br><span class="line">        <span class="keyword">return</span> dfs(i+<span class="number">1</span>,j,k,isVisited) + dfs(i,j+<span class="number">1</span>,k,isVisited) + <span class="number">1</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><h1 id="14-1-剪绳子"><a href="#14-1-剪绳子" class="headerlink" title="14.1 剪绳子"></a>14.1 剪绳子</h1><h2 id="动态规划-1"><a href="#动态规划-1" class="headerlink" title="动态规划"></a>动态规划</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></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 class="keyword">public</span> <span class="type">int</span> <span class="title function_">cuttingRope</span><span class="params">(<span class="type">int</span> n)</span> &#123;</span><br><span class="line">        <span class="keyword">if</span> (n == <span class="number">1</span> || n == <span class="number">2</span>) <span class="keyword">return</span> <span class="number">1</span>;</span><br><span class="line">        <span class="keyword">if</span> (n == <span class="number">3</span>) <span class="keyword">return</span> <span class="number">2</span>;</span><br><span class="line">        <span class="type">int</span>[] dp = <span class="keyword">new</span> <span class="title class_">int</span>[n + <span class="number">1</span>];</span><br><span class="line">        dp[<span class="number">1</span>] = <span class="number">1</span>;</span><br><span class="line">        dp[<span class="number">2</span>] = <span class="number">2</span>;</span><br><span class="line">        dp[<span class="number">3</span>] = <span class="number">3</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">4</span>; i &lt;= n; i++) &#123;</span><br><span class="line">            <span class="type">int</span> <span class="variable">max</span> <span class="operator">=</span> <span class="number">0</span>;</span><br><span class="line">            <span class="comment">// j &gt;i / 2就会出现重复了，所以就不需要那部分的数据比较了</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">1</span>; j &lt;= i / <span class="number">2</span>; j++) &#123;</span><br><span class="line">                max = Math.max(max,dp[j] * dp[i - j]);</span><br><span class="line">            &#125;</span><br><span class="line">            dp[i] = max;</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">return</span> dp[n];</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>搬用题解关于该题的解释。</p><ol><li>任何大于1的数都可由2和3相加组成。</li><li>因为都能有2和3组成，所以当3的数量越多，乘积越大。当剩余的绳子长度大于等于5，尽可能剪长度为3的绳子。</li><li>有一个特例。当剩余的绳子长度等于4，就将绳子剪为两段2。</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="keyword">class</span> <span class="title class_">Solution</span> &#123;</span><br><span class="line">    <span class="keyword">public</span> <span class="type">int</span> <span class="title function_">cuttingRope</span><span class="params">(<span class="type">int</span> n)</span> &#123;</span><br><span class="line">        <span class="keyword">if</span> (n == <span class="number">1</span> || n == <span class="number">2</span>) <span class="keyword">return</span> <span class="number">1</span>;</span><br><span class="line">        <span class="keyword">if</span> (n == <span class="number">3</span>) <span class="keyword">return</span> <span class="number">2</span>;</span><br><span class="line">        <span class="type">int</span> <span class="variable">res</span> <span class="operator">=</span> <span class="number">1</span>;</span><br><span class="line">        <span class="keyword">while</span> (n &gt; <span class="number">4</span>) &#123;</span><br><span class="line">            res *= <span class="number">3</span>;</span><br><span class="line">            n -= <span class="number">3</span>;</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">return</span> res * n;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h1 id="14-2-剪绳子2"><a href="#14-2-剪绳子2" class="headerlink" title="14.2 剪绳子2"></a>14.2 剪绳子2</h1><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">class</span> <span class="title class_">Solution</span> &#123;</span><br><span class="line">    <span class="keyword">public</span> <span class="type">int</span> <span class="title function_">cuttingRope</span><span class="params">(<span class="type">int</span> n)</span> &#123;</span><br><span class="line">        <span class="keyword">if</span> (n == <span class="number">1</span> || n == <span class="number">2</span>) <span class="keyword">return</span> <span class="number">1</span>;</span><br><span class="line">        <span class="keyword">if</span> (n == <span class="number">3</span>) <span class="keyword">return</span> <span class="number">2</span>;</span><br><span class="line">        <span class="type">long</span> <span class="variable">res</span> <span class="operator">=</span> <span class="number">1</span>;</span><br><span class="line">        <span class="keyword">while</span> (n &gt; <span class="number">4</span>) &#123;</span><br><span class="line">            res *= <span class="number">3</span>;</span><br><span class="line">            res %= <span class="number">1000000007</span>;</span><br><span class="line">            n -= <span class="number">3</span>;</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">return</span> (<span class="type">int</span>)(res * n % <span class="number">1000000007</span>);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h1 id="15-二进制中1的个数"><a href="#15-二进制中1的个数" class="headerlink" title="15.二进制中1的个数"></a>15.二进制中1的个数</h1><h2 id="用方法"><a href="#用方法" class="headerlink" title="用方法"></a>用方法</h2><p>有个方法是可以算出二进制中1的个数。</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</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="type">int</span> <span class="title function_">hammingWeight</span><span class="params">(<span class="type">int</span> n)</span> &#123;</span><br><span class="line">        <span class="keyword">return</span> Integer.bitCount(n);</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>通过数字与1的与运算，来判断最后一个二进制的数是不是1，是的话与运算会返回1，就这样逐个判断二进制1的个数。</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</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="type">int</span> <span class="title function_">hammingWeight</span><span class="params">(<span class="type">int</span> n)</span> &#123;</span><br><span class="line">        <span class="type">int</span> <span class="variable">res</span> <span class="operator">=</span> <span class="number">0</span>;</span><br><span class="line">        <span class="keyword">while</span>(n != <span class="number">0</span>) &#123;</span><br><span class="line">            res += n &amp; <span class="number">1</span>;</span><br><span class="line">            n &gt;&gt;&gt;= <span class="number">1</span>;</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">return</span> res;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>还有一个就是用<code>n &amp; (n - 1)</code>来判断，这样的与运算会消去二进制中最右边的1。</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <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="type">int</span> <span class="title function_">hammingWeight</span><span class="params">(<span class="type">int</span> n)</span> &#123;</span><br><span class="line">        <span class="type">int</span> <span class="variable">res</span> <span class="operator">=</span> <span class="number">0</span>;</span><br><span class="line">        <span class="keyword">while</span> (n != <span class="number">0</span>) &#123;</span><br><span class="line">            n = n &amp; (n - <span class="number">1</span>);</span><br><span class="line">            res++;</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">return</span> res;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p><code>n &amp; (n - 1)</code>还能用来判断是不是2的幂。</p><h1 id="16-数值的整数次方"><a href="#16-数值的整数次方" class="headerlink" title="16.数值的整数次方"></a>16.数值的整数次方</h1><p>这里直接使用相乘的算法会超时，所以用到了快速幂的写法。这里需要注意的点是n为负数的时候，<code>int</code>的负数会比正数多一个，力扣上正好用到了这个边界<code>−2147483648</code>，所以需要用<code>long</code>类型来存储。</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></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 class="keyword">public</span> <span class="type">double</span> <span class="title function_">myPow</span><span class="params">(<span class="type">double</span> x, <span class="type">int</span> n)</span> &#123;</span><br><span class="line">        <span class="comment">// 每个数的0次方都等于1</span></span><br><span class="line">        <span class="keyword">if</span> (n == <span class="number">0</span>) <span class="keyword">return</span> <span class="number">1</span>;</span><br><span class="line">        <span class="comment">// 因为负数是比正数多一个数的，如果取得是负数的最小值，取反就会超出正数的最大值，所以使用long</span></span><br><span class="line">        <span class="type">long</span> <span class="variable">num</span> <span class="operator">=</span> n;</span><br><span class="line">        <span class="type">double</span> <span class="variable">res</span> <span class="operator">=</span> <span class="number">1.0</span>;</span><br><span class="line">        <span class="comment">// 如果是负次方，就转换为正数次方</span></span><br><span class="line">        <span class="keyword">if</span> (n &lt; <span class="number">0</span>) &#123;</span><br><span class="line">            x = <span class="number">1</span> / x;</span><br><span class="line">            num = -num;</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="comment">// 若num还大于0代表运算还没有结束</span></span><br><span class="line">        <span class="keyword">while</span> (num &gt; <span class="number">0</span>) &#123;</span><br><span class="line">            <span class="comment">// 当次方数为奇数的时候，就乘底数，次方数减一，转换为次方数为偶数的情况</span></span><br><span class="line">            <span class="keyword">if</span> ((num &amp; <span class="number">1</span>) == <span class="number">1</span>) res *= x;</span><br><span class="line">            <span class="comment">// 次方数为偶数的时候，底数平方，次方数除以2，缩短运算</span></span><br><span class="line">            x = x * x;</span><br><span class="line">            num &gt;&gt;= <span class="number">1</span>;</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">return</span> res;</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><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><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">class</span> <span class="title class_">Solution</span> &#123;</span><br><span class="line">    <span class="keyword">public</span> <span class="type">double</span> <span class="title function_">myPow</span><span class="params">(<span class="type">double</span> x, <span class="type">int</span> n)</span> &#123;</span><br><span class="line">        <span class="keyword">if</span> (n == <span class="number">0</span>) <span class="keyword">return</span> <span class="number">1</span>;</span><br><span class="line">        <span class="keyword">if</span> (n == -<span class="number">1</span>) <span class="keyword">return</span> <span class="number">1</span> / x;</span><br><span class="line">        <span class="keyword">if</span> ((n &amp; <span class="number">1</span>) == <span class="number">1</span>) <span class="keyword">return</span> myPow(x * x,n &gt;&gt; <span class="number">1</span>) * x;</span><br><span class="line">        <span class="keyword">else</span> <span class="keyword">return</span> myPow(x * x,n &gt;&gt; <span class="number">1</span>);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h1 id="18-删除链表的节点"><a href="#18-删除链表的节点" class="headerlink" title="18.删除链表的节点"></a>18.删除链表的节点</h1><p>这里可以直接查到那个需要删除的节点，然后改变链表的指向就好了。</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></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 class="keyword">public</span> ListNode <span class="title function_">deleteNode</span><span class="params">(ListNode head, <span class="type">int</span> val)</span> &#123;</span><br><span class="line">        <span class="keyword">if</span> (head == <span class="literal">null</span>) <span class="keyword">return</span> head;</span><br><span class="line">        <span class="keyword">if</span> (head.val == val) <span class="keyword">return</span> head.next;</span><br><span class="line">        <span class="type">ListNode</span> <span class="variable">newHead</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">ListNode</span>(<span class="number">0</span>);</span><br><span class="line">        newHead.next = head;</span><br><span class="line">        <span class="type">ListNode</span> <span class="variable">pre</span> <span class="operator">=</span> newHead;</span><br><span class="line">        <span class="type">ListNode</span> <span class="variable">cur</span> <span class="operator">=</span> head;</span><br><span class="line">        <span class="keyword">while</span> (cur != <span class="literal">null</span>) &#123;</span><br><span class="line">            <span class="keyword">if</span> (cur.val == val) &#123;</span><br><span class="line">                pre.next = cur.next;</span><br><span class="line">                cur.next = <span class="literal">null</span>;</span><br><span class="line">                <span class="keyword">break</span>;</span><br><span class="line">            &#125;</span><br><span class="line">            pre = pre.next;</span><br><span class="line">            cur = cur.next;</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">return</span> newHead.next;</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><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><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">class</span> <span class="title class_">Solution</span> &#123;</span><br><span class="line">    <span class="keyword">public</span> ListNode <span class="title function_">deleteNode</span><span class="params">(ListNode head, <span class="type">int</span> val)</span> &#123;</span><br><span class="line">        <span class="keyword">if</span> (head == <span class="literal">null</span>) <span class="keyword">return</span> <span class="literal">null</span>;</span><br><span class="line">        <span class="keyword">if</span> (head.val == val) <span class="keyword">return</span> head.next;</span><br><span class="line">        head.next = deleteNode(head.next,val);</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><h1 id="21-调整数组顺序使奇数位于偶数前面"><a href="#21-调整数组顺序使奇数位于偶数前面" class="headerlink" title="21.调整数组顺序使奇数位于偶数前面"></a>21.调整数组顺序使奇数位于偶数前面</h1><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">class</span> <span class="title class_">Solution</span> &#123;</span><br><span class="line">    <span class="keyword">public</span> <span class="type">int</span>[] exchange(<span class="type">int</span>[] nums) &#123;</span><br><span class="line">        <span class="type">int</span> <span class="variable">l</span> <span class="operator">=</span> <span class="number">0</span>, r = nums.length - <span class="number">1</span>;</span><br><span class="line">        <span class="keyword">while</span> (l &lt; r) &#123;</span><br><span class="line">            <span class="keyword">while</span> (nums[l] % <span class="number">2</span> != <span class="number">0</span> &amp;&amp; l &lt; r) l++;</span><br><span class="line">            <span class="keyword">while</span> (nums[r] % <span class="number">2</span> == <span class="number">0</span> &amp;&amp; l &lt; r) r--;</span><br><span class="line">            <span class="type">int</span> <span class="variable">temp</span> <span class="operator">=</span> nums[l];</span><br><span class="line">            nums[l] = nums[r];</span><br><span class="line">            nums[r] = temp;</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">return</span> nums;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h1 id="22-链表中倒数第K个节点"><a href="#22-链表中倒数第K个节点" class="headerlink" title="22.链表中倒数第K个节点"></a>22.链表中倒数第K个节点</h1><h2 id="快慢指针"><a href="#快慢指针" class="headerlink" title="快慢指针"></a>快慢指针</h2><p>这题用快慢指针还是很简单的，先让快指针走N步，然后快慢指针一起走，当快指针都到链表尾部，那么慢指针就是倒数第K个节点了。</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><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">class</span> <span class="title class_">Solution</span> &#123;</span><br><span class="line">    <span class="keyword">public</span> ListNode <span class="title function_">getKthFromEnd</span><span class="params">(ListNode head, <span class="type">int</span> k)</span> &#123;</span><br><span class="line">        <span class="type">ListNode</span> <span class="variable">fast</span> <span class="operator">=</span> head;</span><br><span class="line">        <span class="type">ListNode</span> <span class="variable">slow</span> <span class="operator">=</span> head;</span><br><span class="line">        <span class="keyword">while</span> (k-- &gt; <span class="number">0</span>) fast = fast.next;</span><br><span class="line">        <span class="keyword">while</span> (fast != <span class="literal">null</span>) &#123;</span><br><span class="line">            fast = fast.next;</span><br><span class="line">            slow = slow.next;</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">return</span> slow;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h1 id="24-反转链表"><a href="#24-反转链表" class="headerlink" title="24.反转链表"></a>24.反转链表</h1><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></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 class="keyword">public</span> ListNode <span class="title function_">reverseList</span><span class="params">(ListNode head)</span> &#123;</span><br><span class="line">        <span class="keyword">if</span> (head == <span class="literal">null</span> || head.next == <span class="literal">null</span>) <span class="keyword">return</span> head;</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">cur</span> <span class="operator">=</span> head;</span><br><span class="line">        <span class="keyword">while</span> (cur != <span class="literal">null</span>) &#123;</span><br><span class="line">            <span class="type">ListNode</span> <span class="variable">node</span> <span class="operator">=</span> cur.next;</span><br><span class="line">            cur.next = pre;</span><br><span class="line">            pre = cur;</span><br><span class="line">            cur = node;</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">&#125;</span><br></pre></td></tr></table></figure><h2 id="递归-1"><a href="#递归-1" 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></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 class="keyword">public</span> ListNode <span class="title function_">reverseList</span><span class="params">(ListNode head)</span> &#123;</span><br><span class="line">        <span class="keyword">return</span> reverse(head);</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">public</span> ListNode <span class="title function_">reverse</span><span class="params">(ListNode head)</span> &#123;</span><br><span class="line">        <span class="keyword">if</span> (head == <span class="literal">null</span> || head.next == <span class="literal">null</span>) <span class="keyword">return</span> head;</span><br><span class="line">        <span class="type">ListNode</span> <span class="variable">last</span> <span class="operator">=</span> reverse(head.next);</span><br><span class="line">        head.next.next = head;</span><br><span class="line">        head.next = <span class="literal">null</span>;</span><br><span class="line">        <span class="keyword">return</span> last;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h1 id="25-合并两个排序的链表"><a href="#25-合并两个排序的链表" class="headerlink" title="25.合并两个排序的链表"></a>25.合并两个排序的链表</h1><h2 id="迭代-1"><a href="#迭代-1" class="headerlink" title="迭代"></a>迭代</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></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 class="keyword">public</span> ListNode <span class="title function_">mergeTwoLists</span><span class="params">(ListNode l1, ListNode l2)</span> &#123;</span><br><span class="line">        <span class="type">ListNode</span> <span class="variable">head</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">ListNode</span>(<span class="number">1</span>);</span><br><span class="line">        <span class="type">ListNode</span> <span class="variable">cur</span> <span class="operator">=</span> head;</span><br><span class="line">        <span class="keyword">while</span> (l1 != <span class="literal">null</span> &amp;&amp; l2 != <span class="literal">null</span>) &#123;</span><br><span class="line">            <span class="keyword">if</span> (l1.val &gt; l2.val) &#123;</span><br><span class="line">                cur.next = l2;</span><br><span class="line">                l2 = l2.next;</span><br><span class="line">            &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">                cur.next = l1;</span><br><span class="line">                l1 = l1.next;</span><br><span class="line">            &#125;</span><br><span class="line">            cur = cur.next;</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">if</span> (l1 == <span class="literal">null</span>) cur.next = l2;</span><br><span class="line">        <span class="keyword">else</span> cur.next = l1;</span><br><span class="line">        <span class="keyword">return</span> head.next;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h2 id="递归-2"><a href="#递归-2" class="headerlink" title="递归"></a>递归</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></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 class="keyword">public</span> ListNode <span class="title function_">mergeTwoLists</span><span class="params">(ListNode l1, ListNode l2)</span> &#123;</span><br><span class="line">        <span class="comment">// 要不要这行都可以</span></span><br><span class="line">        <span class="keyword">if</span> (l1 == <span class="literal">null</span> &amp;&amp; l2 == <span class="literal">null</span>) <span class="keyword">return</span> <span class="literal">null</span>;</span><br><span class="line">        <span class="keyword">if</span> (l1 == <span class="literal">null</span>) <span class="keyword">return</span> l2;</span><br><span class="line">        <span class="keyword">if</span> (l2 == <span class="literal">null</span>) <span class="keyword">return</span> l1;</span><br><span class="line">        <span class="keyword">if</span> (l1.val &lt; l2.val) &#123;</span><br><span class="line">            l1.next = mergeTwoLists(l1.next, l2);</span><br><span class="line">            <span class="keyword">return</span> l1;</span><br><span class="line">        &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">            l2.next = mergeTwoLists(l1, l2.next);</span><br><span class="line">            <span class="keyword">return</span> l2;</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><h1 id="26-树的子结构"><a href="#26-树的子结构" class="headerlink" title="26. 树的子结构"></a>26. 树的子结构</h1><p>首先要先找到A中与B节点的头节点相等的点，然后递归判断A与B的左子树与右子树，看是否结构与节点值相等。</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">class</span> <span class="title class_">Solution</span> &#123; </span><br><span class="line">    </span><br><span class="line">    <span class="keyword">public</span> <span class="type">boolean</span> <span class="title function_">isSubStructure</span><span class="params">(TreeNode A, TreeNode B)</span> &#123;</span><br><span class="line">        <span class="keyword">if</span> (A == <span class="literal">null</span> || B == <span class="literal">null</span>) <span class="keyword">return</span> <span class="literal">false</span>;</span><br><span class="line">        <span class="keyword">return</span> check(A, B) || isSubStructure(A.left, B) || isSubStructure(A.right, B);</span><br><span class="line">    &#125;</span><br><span class="line">    </span><br><span class="line">    <span class="comment">// 判断a树是否和b树是一样的</span></span><br><span class="line">    <span class="keyword">public</span> <span class="type">boolean</span> <span class="title function_">check</span><span class="params">(TreeNode A, TreeNode B)</span> &#123;</span><br><span class="line">        <span class="comment">// 若B节点为空，无论A节点是否为空，都是A树的子结构</span></span><br><span class="line">        <span class="keyword">if</span> (B == <span class="literal">null</span>) <span class="keyword">return</span> <span class="literal">true</span>;</span><br><span class="line">        <span class="comment">// 因为此时B节点不为空，但是如果A节点为空，那么B树就不为A树的子结构了</span></span><br><span class="line">        <span class="keyword">if</span> (A == <span class="literal">null</span>) <span class="keyword">return</span> <span class="literal">false</span>;</span><br><span class="line">        <span class="comment">// 两个值不相同，代表树种该位置两个节点不一样</span></span><br><span class="line">        <span class="keyword">if</span> (A.val != B.val) <span class="keyword">return</span> <span class="literal">false</span>;</span><br><span class="line">        <span class="comment">// 判断左子树与右子树</span></span><br><span class="line">        <span class="keyword">return</span> check(A.left, B.left) &amp;&amp; check(A.right, B.right);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h1 id="27-二叉树的镜像"><a href="#27-二叉树的镜像" class="headerlink" title="27.二叉树的镜像"></a>27.二叉树的镜像</h1><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></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 class="keyword">public</span> TreeNode <span class="title function_">mirrorTree</span><span class="params">(TreeNode root)</span> &#123;</span><br><span class="line">        <span class="keyword">if</span> (root == <span class="literal">null</span>) <span class="keyword">return</span> <span class="literal">null</span>;</span><br><span class="line">        <span class="type">TreeNode</span> <span class="variable">node</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">TreeNode</span>(root.val);</span><br><span class="line">        node.left = mirrorTree(root.right);</span><br><span class="line">        node.right = mirrorTree(root.left);</span><br><span class="line">        <span class="keyword">return</span> node;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h1 id="28-对称的二叉树"><a href="#28-对称的二叉树" class="headerlink" title="28.对称的二叉树"></a>28.对称的二叉树</h1><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">class</span> <span class="title class_">Solution</span> &#123;</span><br><span class="line">    <span class="keyword">public</span> <span class="type">boolean</span> <span class="title function_">isSymmetric</span><span class="params">(TreeNode root)</span> &#123;</span><br><span class="line">        <span class="keyword">if</span> (root == <span class="literal">null</span>) <span class="keyword">return</span> <span class="literal">true</span>;</span><br><span class="line">        <span class="keyword">return</span> check(root.left, root.right);</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">(TreeNode left, TreeNode right)</span> &#123;</span><br><span class="line">        <span class="keyword">if</span> (left == <span class="literal">null</span> &amp;&amp; right == <span class="literal">null</span>) <span class="keyword">return</span> <span class="literal">true</span>;</span><br><span class="line">        <span class="keyword">if</span> (left == <span class="literal">null</span> || right == <span class="literal">null</span>) <span class="keyword">return</span> <span class="literal">false</span>;</span><br><span class="line">        <span class="keyword">if</span> (left.val != right.val) <span class="keyword">return</span> <span class="literal">false</span>;</span><br><span class="line">        <span class="keyword">return</span> check(left.left, right.right) &amp;&amp; check(left.right, right.left);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h1 id="29-顺时针打印矩阵"><a href="#29-顺时针打印矩阵" class="headerlink" title="29.顺时针打印矩阵"></a>29.顺时针打印矩阵</h1><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></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 class="keyword">public</span> <span class="type">int</span>[] spiralOrder(<span class="type">int</span>[][] matrix) &#123;</span><br><span class="line">        <span class="type">int</span> <span class="variable">rows</span> <span class="operator">=</span> matrix.length;</span><br><span class="line">        <span class="keyword">if</span> (rows == <span class="number">0</span>) <span class="keyword">return</span> <span class="keyword">new</span> <span class="title class_">int</span>[<span class="number">0</span>];</span><br><span class="line">        <span class="type">int</span> <span class="variable">cols</span> <span class="operator">=</span> matrix[<span class="number">0</span>].length;</span><br><span class="line">        <span class="keyword">if</span> (cols == <span class="number">0</span>) <span class="keyword">return</span> <span class="keyword">new</span> <span class="title class_">int</span>[<span class="number">0</span>];</span><br><span class="line">        <span class="type">int</span> <span class="variable">count</span> <span class="operator">=</span> <span class="number">0</span>;</span><br><span class="line">        <span class="type">int</span>[] res = <span class="keyword">new</span> <span class="title class_">int</span>[rows * cols];</span><br><span class="line">        <span class="type">int</span> <span class="variable">up</span> <span class="operator">=</span> <span class="number">0</span>, down = rows - <span class="number">1</span>, left = <span class="number">0</span>, right = cols - <span class="number">1</span>;</span><br><span class="line">        <span class="comment">// 注意调整边界时要注意不可过界</span></span><br><span class="line">        <span class="keyword">while</span> (count &lt; rows * cols) &#123;</span><br><span class="line">            <span class="comment">// 向右，遍历完，上边界下移</span></span><br><span class="line">            <span class="keyword">for</span> (<span class="type">int</span> <span class="variable">j</span> <span class="operator">=</span> left; j &lt;= right &amp;&amp; left &lt;= right; j++) &#123;</span><br><span class="line">                res[count++] = matrix[up][j];</span><br><span class="line">            &#125;</span><br><span class="line">            up++;</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> up; i &lt;= down &amp;&amp; up &lt;= down; i++) &#123;</span><br><span class="line">                res[count++] = matrix[i][right];</span><br><span class="line">            &#125;</span><br><span class="line">            right--;</span><br><span class="line">            <span class="comment">// 向左，遍历完，下边界上移</span></span><br><span class="line">            <span class="keyword">for</span> (<span class="type">int</span> <span class="variable">j</span> <span class="operator">=</span> right; j &gt;= left &amp;&amp; up &lt;= down; j--) &#123;</span><br><span class="line">                res[count++] = matrix[down][j];</span><br><span class="line">            &#125;</span><br><span class="line">            down--;</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> down; i &gt;= up &amp;&amp; left &lt;= right; i--) &#123;</span><br><span class="line">                res[count++] = matrix[i][left];</span><br><span class="line">            &#125;</span><br><span class="line">            left++;</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">return</span> res;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h1 id="30-包含min函数的栈"><a href="#30-包含min函数的栈" class="headerlink" title="30. 包含min函数的栈"></a>30. 包含min函数的栈</h1><p>找到最小元素其实并不难，这题难的地方是调用<code>min</code>，<code>push</code>，<code>pop</code>的时间复杂度都要为O(1)。</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></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">    Stack&lt;Integer&gt; stack1;</span><br><span class="line">    Stack&lt;Integer&gt; stack2;</span><br><span class="line">    <span class="comment">/** initialize your data structure here. */</span></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">        stack1 = <span class="keyword">new</span> <span class="title class_">Stack</span>&lt;&gt;();</span><br><span class="line">        stack2 = <span class="keyword">new</span> <span class="title class_">Stack</span>&lt;&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> x)</span> &#123;</span><br><span class="line">        stack1.push(x);</span><br><span class="line">        <span class="keyword">if</span> (stack2.isEmpty()) &#123;</span><br><span class="line">            stack2.push(x);</span><br><span class="line">        &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">            stack2.push(Math.min(x, stack2.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="keyword">public</span> <span class="keyword">void</span> <span class="title function_">pop</span><span class="params">()</span> &#123;</span><br><span class="line">        stack1.pop();</span><br><span class="line">        stack2.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_">top</span><span class="params">()</span> &#123;</span><br><span class="line">        <span class="keyword">return</span> stack1.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">int</span> <span class="title function_">min</span><span class="params">()</span> &#123;</span><br><span class="line">        <span class="keyword">return</span> stack2.peek();</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h1 id="31-栈的压入和弹出序列"><a href="#31-栈的压入和弹出序列" class="headerlink" title="31. 栈的压入和弹出序列"></a>31. 栈的压入和弹出序列</h1><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></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 class="keyword">public</span> <span class="type">boolean</span> <span class="title function_">validateStackSequences</span><span class="params">(<span class="type">int</span>[] pushed, <span class="type">int</span>[] popped)</span> &#123;</span><br><span class="line">        Stack&lt;Integer&gt; stack = <span class="keyword">new</span> <span class="title class_">Stack</span>&lt;&gt;();</span><br><span class="line">        <span class="type">int</span> <span class="variable">start</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> i : pushed) &#123;</span><br><span class="line">            stack.push(i);</span><br><span class="line">            <span class="keyword">while</span> (!stack.isEmpty() &amp;&amp; stack.peek() == popped[start]) &#123;</span><br><span class="line">                stack.pop();</span><br><span class="line">                start++;</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">return</span> stack.isEmpty();</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h1 id="32-1-从上到下打印二叉树"><a href="#32-1-从上到下打印二叉树" class="headerlink" title="32.1.从上到下打印二叉树"></a>32.1.从上到下打印二叉树</h1><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></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 class="keyword">public</span> <span class="type">int</span>[] levelOrder(TreeNode root) &#123;</span><br><span class="line">        <span class="keyword">if</span> (root == <span class="literal">null</span>) <span class="keyword">return</span> <span class="keyword">new</span> <span class="title class_">int</span>[<span class="number">0</span>];</span><br><span class="line">        List&lt;Integer&gt; list = <span class="keyword">new</span> <span class="title class_">ArrayList</span>&lt;&gt;();</span><br><span class="line">        Deque&lt;TreeNode&gt; deque = <span class="keyword">new</span> <span class="title class_">LinkedList</span>&lt;&gt;();</span><br><span class="line">        deque.offer(root);</span><br><span class="line">        <span class="keyword">while</span> (!deque.isEmpty()) &#123;</span><br><span class="line">            <span class="type">TreeNode</span> <span class="variable">node</span> <span class="operator">=</span> deque.poll();</span><br><span class="line">            list.add(node.val);</span><br><span class="line">            <span class="keyword">if</span> (node.left != <span class="literal">null</span>) deque.offer(node.left);</span><br><span class="line">            <span class="keyword">if</span> (node.right != <span class="literal">null</span>) deque.offer(node.right);</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="type">int</span>[] res = <span class="keyword">new</span> <span class="title class_">int</span>[list.size()];</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; res.length; i++) &#123;</span><br><span class="line">            res[i] = list.get(i);</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">return</span> res;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h1 id="32-2-从上到下打印二叉树"><a href="#32-2-从上到下打印二叉树" class="headerlink" title="32.2.从上到下打印二叉树"></a>32.2.从上到下打印二叉树</h1><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">class</span> <span class="title class_">Solution</span> &#123;</span><br><span class="line">    <span class="keyword">public</span> List&lt;List&lt;Integer&gt;&gt; <span class="title function_">levelOrder</span><span class="params">(TreeNode root)</span> &#123;</span><br><span class="line">        List&lt;List&lt;Integer&gt;&gt; res = <span class="keyword">new</span> <span class="title class_">ArrayList</span>&lt;&gt;();</span><br><span class="line">        <span class="keyword">if</span> (root == <span class="literal">null</span>) <span class="keyword">return</span> res;</span><br><span class="line">        Deque&lt;TreeNode&gt; deque = <span class="keyword">new</span> <span class="title class_">LinkedList</span>&lt;&gt;();</span><br><span class="line">        deque.offer(root);</span><br><span class="line">        <span class="keyword">while</span> (!deque.isEmpty()) &#123;</span><br><span class="line">            <span class="type">int</span> <span class="variable">size</span> <span class="operator">=</span> deque.size();</span><br><span class="line">            List&lt;Integer&gt; list = <span class="keyword">new</span> <span class="title class_">ArrayList</span>&lt;&gt;();</span><br><span class="line">            <span class="keyword">while</span> (size-- != <span class="number">0</span>) &#123;</span><br><span class="line">                <span class="type">TreeNode</span> <span class="variable">node</span> <span class="operator">=</span> deque.poll();</span><br><span class="line">                list.add(node.val);</span><br><span class="line">                <span class="keyword">if</span> (node.left != <span class="literal">null</span>) deque.offer(node.left);</span><br><span class="line">                <span class="keyword">if</span> (node.right != <span class="literal">null</span>) deque.offer(node.right);</span><br><span class="line">            &#125;</span><br><span class="line">            res.add(list);            </span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">return</span> res;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h1 id="32-3从上到下打印二叉树"><a href="#32-3从上到下打印二叉树" class="headerlink" title="32.3从上到下打印二叉树"></a>32.3从上到下打印二叉树</h1><p>这个就是要记录一下层数，这里的倒序可以分为两种解法。</p><ol><li>倒着加入到列表</li><li>加入到列表之后再倒序</li></ol><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">class</span> <span class="title class_">Solution</span> &#123;</span><br><span class="line">    <span class="keyword">public</span> List&lt;List&lt;Integer&gt;&gt; <span class="title function_">levelOrder</span><span class="params">(TreeNode root)</span> &#123;</span><br><span class="line">        List&lt;List&lt;Integer&gt;&gt; res = <span class="keyword">new</span> <span class="title class_">ArrayList</span>&lt;&gt;();</span><br><span class="line">        <span class="keyword">if</span> (root == <span class="literal">null</span>) <span class="keyword">return</span> res;</span><br><span class="line">        Deque&lt;TreeNode&gt; deque = <span class="keyword">new</span> <span class="title class_">LinkedList</span>&lt;&gt;();</span><br><span class="line">        deque.offer(root);</span><br><span class="line">        <span class="keyword">while</span> (!deque.isEmpty()) &#123;</span><br><span class="line">            List&lt;Integer&gt; list = <span class="keyword">new</span> <span class="title class_">ArrayList</span>&lt;&gt;();</span><br><span class="line">            <span class="type">int</span> <span class="variable">size</span> <span class="operator">=</span> deque.size();</span><br><span class="line">            <span class="keyword">while</span> (size-- != <span class="number">0</span>) &#123;</span><br><span class="line">                <span class="type">TreeNode</span> <span class="variable">node</span> <span class="operator">=</span> deque.poll();</span><br><span class="line">                list.add(node.val);</span><br><span class="line">                <span class="keyword">if</span> (node.left != <span class="literal">null</span>) deque.offer(node.left);</span><br><span class="line">                <span class="keyword">if</span> (node.right != <span class="literal">null</span>) deque.offer(node.right);</span><br><span class="line">            &#125;</span><br><span class="line">            <span class="keyword">if</span> (res.size() % <span class="number">2</span> != <span class="number">0</span>) Collections.reverse(list);</span><br><span class="line">            res.add(list);</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">return</span> res;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h1 id="33-二叉搜索树的后序遍历序列"><a href="#33-二叉搜索树的后序遍历序列" class="headerlink" title="33.二叉搜索树的后序遍历序列"></a>33.二叉搜索树的后序遍历序列</h1><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></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="type">boolean</span> <span class="title function_">verifyPostorder</span><span class="params">(<span class="type">int</span>[] postorder)</span> &#123;</span><br><span class="line">        <span class="keyword">return</span> check(postorder,<span class="number">0</span>,postorder.length - <span class="number">1</span>);</span><br><span class="line">    &#125;</span><br><span class="line">    </span><br><span class="line"><span class="comment">// 判断该树是否正确</span></span><br><span class="line">    <span class="keyword">public</span> <span class="type">boolean</span> <span class="title function_">check</span><span class="params">(<span class="type">int</span>[] postorder, <span class="type">int</span> start, <span class="type">int</span> end)</span> &#123;</span><br><span class="line">        <span class="comment">// start &gt;= end代表该树只有小于等于1的节点，所以不用判断，返回true</span></span><br><span class="line">        <span class="keyword">if</span> (start &gt;= end) <span class="keyword">return</span> <span class="literal">true</span>;</span><br><span class="line">        <span class="type">int</span> <span class="variable">p</span> <span class="operator">=</span> start;</span><br><span class="line">        <span class="comment">// mid 为根节点的值</span></span><br><span class="line">        <span class="type">int</span> <span class="variable">mid</span> <span class="operator">=</span> postorder[end];</span><br><span class="line">        <span class="comment">// 检查左子树区间节点是否正确</span></span><br><span class="line">        <span class="keyword">while</span> (postorder[p] &lt; mid) &#123;</span><br><span class="line">            p++;</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="type">int</span> <span class="variable">lr</span> <span class="operator">=</span> p;</span><br><span class="line">        <span class="comment">// 检查右子树区间节点是否正确</span></span><br><span class="line">        <span class="keyword">while</span> (postorder[p] &gt; mid) &#123;</span><br><span class="line">            p++;</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="comment">// 两个区间正确的话，p会遍历到根节点的位置，即p == end的话该树正确</span></span><br><span class="line">        <span class="keyword">if</span> (p != end) <span class="keyword">return</span> <span class="literal">false</span>;</span><br><span class="line">        <span class="comment">// 然后遍历左子树和右子树是否正确</span></span><br><span class="line">        <span class="keyword">return</span> check(postorder, start, lr - <span class="number">1</span>) &amp;&amp; check(postorder, lr, end - <span class="number">1</span>);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h1 id="34-二叉树中和为某一值的路径"><a href="#34-二叉树中和为某一值的路径" class="headerlink" title="34. 二叉树中和为某一值的路径"></a>34. 二叉树中和为某一值的路径</h1><h2 id="回溯"><a href="#回溯" class="headerlink" title="回溯"></a>回溯</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><span class="line">21</span><br><span class="line">22</span><br><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">class</span> <span class="title class_">Solution</span> &#123;</span><br><span class="line">    </span><br><span class="line">    List&lt;List&lt;Integer&gt;&gt; res = <span class="keyword">new</span> <span class="title class_">LinkedList</span>&lt;&gt;();</span><br><span class="line">    LinkedList&lt;Integer&gt; path = <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="keyword">public</span> List&lt;List&lt;Integer&gt;&gt; <span class="title function_">pathSum</span><span class="params">(TreeNode root, <span class="type">int</span> target)</span> &#123;</span><br><span class="line">        dfs(root, target);</span><br><span class="line">        <span class="keyword">return</span> res;</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_">dfs</span><span class="params">(TreeNode node, <span class="type">int</span> target)</span> &#123;</span><br><span class="line">        <span class="keyword">if</span> (node == <span class="literal">null</span>) <span class="keyword">return</span>;</span><br><span class="line">        <span class="comment">// 先进行添加在判断条件，如果是先判断条件的话，target的判断就不能使用减法了，需要多添加一个b</span></span><br><span class="line">        path.addFirst(node.val);</span><br><span class="line">        target -= node.val;</span><br><span class="line">        <span class="comment">// 若此时当前节点是子节点，并且target为0了，就代表是一条路径</span></span><br><span class="line">        <span class="keyword">if</span>(target == <span class="number">0</span> &amp;&amp; node.left == <span class="literal">null</span> &amp;&amp; node.right == <span class="literal">null</span>) &#123;</span><br><span class="line">            res.add(<span class="keyword">new</span> <span class="title class_">LinkedList</span>(path));</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="comment">// 遍历左子树和右子树</span></span><br><span class="line">        dfs(node.left, target);</span><br><span class="line">        dfs(node.right, target);</span><br><span class="line">        <span class="comment">// 状态回溯</span></span><br><span class="line">        path.removeLast();</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h1 id="35-复杂链表的复制"><a href="#35-复杂链表的复制" class="headerlink" title="35. 复杂链表的复制"></a>35. 复杂链表的复制</h1><h2 id="哈希表-1"><a href="#哈希表-1" class="headerlink" title="哈希表"></a>哈希表</h2><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></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> Node <span class="title function_">copyRandomList</span><span class="params">(Node head)</span> &#123;</span><br><span class="line">        <span class="keyword">if</span> (head == <span class="literal">null</span>) <span class="keyword">return</span> <span class="literal">null</span>;</span><br><span class="line">        <span class="type">Node</span> <span class="variable">newhead</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">Node</span>(head.val);</span><br><span class="line">        <span class="type">Node</span> <span class="variable">newcur</span> <span class="operator">=</span> newhead;</span><br><span class="line">        <span class="type">Node</span> <span class="variable">cur</span> <span class="operator">=</span> head.next;</span><br><span class="line">        <span class="keyword">while</span> (cur != <span class="literal">null</span>) &#123;</span><br><span class="line">            <span class="type">Node</span> <span class="variable">node</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">Node</span>(cur.val);</span><br><span class="line">            newcur.next = node;</span><br><span class="line">            newcur = node;</span><br><span class="line">            cur = cur.next;</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">return</span> newhead;</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>这里可以用哈希表存储链表节点，键为原链表节点，值为新链表节点。</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></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 class="keyword">public</span> Node <span class="title function_">copyRandomList</span><span class="params">(Node head)</span> &#123;</span><br><span class="line">        <span class="keyword">if</span> (head == <span class="literal">null</span>) <span class="keyword">return</span> <span class="literal">null</span>;</span><br><span class="line">        Map&lt;Node, Node&gt; map = <span class="keyword">new</span> <span class="title class_">HashMap</span>&lt;&gt;();</span><br><span class="line">        <span class="type">Node</span> <span class="variable">newHead</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">Node</span>(head.val);</span><br><span class="line">        map.put(head, newHead);</span><br><span class="line">        <span class="comment">// 链表复制节点，并存储节点到哈希表</span></span><br><span class="line">        <span class="type">Node</span> <span class="variable">newCur</span> <span class="operator">=</span> newHead;</span><br><span class="line">        <span class="type">Node</span> <span class="variable">cur</span> <span class="operator">=</span> head.next;</span><br><span class="line">        <span class="keyword">while</span> (cur != <span class="literal">null</span>) &#123;</span><br><span class="line">            <span class="type">Node</span> <span class="variable">node</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">Node</span>(cur.val);</span><br><span class="line">            newCur.next = node;</span><br><span class="line">            newCur = node;</span><br><span class="line">            map.put(cur, newCur);</span><br><span class="line">            cur = cur.next;</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> (head != <span class="literal">null</span>) &#123;</span><br><span class="line">            map.get(head).random = map.get(head.random);</span><br><span class="line">            head = head.next;</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">return</span> newHead;</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>将原链表和新链表拼接在一起</p><p>原节点1 —&gt; 新节点1 —&gt; 原节点2 —&gt; 新节点2 ………..</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><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><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="keyword">class</span> <span class="title class_">Solution</span> &#123;</span><br><span class="line">    <span class="keyword">public</span> Node <span class="title function_">copyRandomList</span><span class="params">(Node head)</span> &#123;</span><br><span class="line">        <span class="keyword">if</span> (head == <span class="literal">null</span>) <span class="keyword">return</span> head;</span><br><span class="line">        <span class="comment">// 为链表的每个节点复制一个节点并接在该节点后面</span></span><br><span class="line">        <span class="type">Node</span> <span class="variable">cur</span> <span class="operator">=</span> head;</span><br><span class="line">        <span class="keyword">while</span> (cur != <span class="literal">null</span>) &#123;</span><br><span class="line">            <span class="type">Node</span> <span class="variable">node</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">Node</span>(cur.val);</span><br><span class="line">            node.next = cur.next;</span><br><span class="line">            cur.next = node;</span><br><span class="line">            cur = node.next;</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="comment">// 为新的复制节点的随机指针赋值</span></span><br><span class="line">        cur = head;</span><br><span class="line">        <span class="keyword">while</span> (cur != <span class="literal">null</span>) &#123;</span><br><span class="line">            <span class="keyword">if</span> (cur.random != <span class="literal">null</span>) &#123;</span><br><span class="line">                cur.next.random = cur.random.next;</span><br><span class="line">            &#125;</span><br><span class="line">            cur = cur.next.next;</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="comment">// 链表拆分</span></span><br><span class="line">        <span class="type">Node</span> <span class="variable">newHead</span> <span class="operator">=</span> head.next;</span><br><span class="line">        <span class="type">Node</span> <span class="variable">newCur</span> <span class="operator">=</span> newHead;</span><br><span class="line">        cur = head;</span><br><span class="line">        <span class="keyword">while</span> (newCur.next != <span class="literal">null</span>) &#123;</span><br><span class="line">            cur.next = cur.next.next;</span><br><span class="line">            newCur.next = newCur.next.next;</span><br><span class="line">            cur = cur.next;</span><br><span class="line">            newCur = newCur.next;</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="comment">// 如果不加这句，系统判定是原来的节点，d</span></span><br><span class="line">        cur.next = <span class="literal">null</span>;</span><br><span class="line">        <span class="keyword">return</span> newHead;</span><br><span class="line">        </span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h1 id="39-数组中出现次数超过一半的数字"><a href="#39-数组中出现次数超过一半的数字" class="headerlink" title="39. 数组中出现次数超过一半的数字"></a>39. 数组中出现次数超过一半的数字</h1><h2 id="排序-1"><a href="#排序-1" class="headerlink" title="排序"></a>排序</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></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 class="keyword">public</span> <span class="type">int</span> <span class="title function_">majorityElement</span><span class="params">(<span class="type">int</span>[] nums)</span> &#123;</span><br><span class="line">        <span class="keyword">if</span> (nums.length &lt;= <span class="number">2</span>) <span class="keyword">return</span> nums[<span class="number">0</span>];</span><br><span class="line">        Arrays.sort(nums);</span><br><span class="line">        <span class="keyword">return</span> nums[nums.length / <span class="number">2</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><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><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">class</span> <span class="title class_">Solution</span> &#123;</span><br><span class="line">    <span class="keyword">public</span> <span class="type">int</span> <span class="title function_">majorityElement</span><span class="params">(<span class="type">int</span>[] nums)</span> &#123;</span><br><span class="line">        <span class="keyword">if</span> (nums.length &lt;= <span class="number">2</span>) <span class="keyword">return</span> nums[<span class="number">0</span>];</span><br><span class="line">        <span class="type">int</span> <span class="variable">count</span> <span class="operator">=</span> <span class="number">0</span>;</span><br><span class="line">        <span class="type">int</span> <span class="variable">temp</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> num : nums) &#123;</span><br><span class="line">            <span class="keyword">if</span> (count == <span class="number">0</span>) temp = num;</span><br><span class="line">            <span class="keyword">if</span> (num == temp) &#123;</span><br><span class="line">                count++;</span><br><span class="line">            &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">                count --;</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;</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></pre></td></tr></table></figure><h1 id="40-最小的k个数"><a href="#40-最小的k个数" class="headerlink" title="40. 最小的k个数"></a>40. 最小的k个数</h1><h2 id="暴力解法"><a href="#暴力解法" class="headerlink" title="暴力解法"></a>暴力解法</h2><p>直接排序取前k个数行了。</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><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">class</span> <span class="title class_">Solution</span> &#123;</span><br><span class="line">    <span class="keyword">public</span> <span class="type">int</span>[] getLeastNumbers(<span class="type">int</span>[] arr, <span class="type">int</span> k) &#123;</span><br><span class="line">        <span class="keyword">if</span> (k == <span class="number">0</span>) <span class="keyword">return</span> <span class="keyword">new</span> <span class="title class_">int</span>[<span class="number">0</span>];</span><br><span class="line">        Arrays.sort(arr);</span><br><span class="line">        <span class="type">int</span>[] mink = <span class="keyword">new</span> <span class="title class_">int</span>[k];</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; k; i++) &#123;</span><br><span class="line">            mink[i] = arr[i];</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">return</span> mink;</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>因为要返回的最小的K个数，并没有说是有序的k个数，所以，快速排序的时候，排到第k个其实就可以停止了。</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><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">class</span> <span class="title class_">Solution</span> &#123;</span><br><span class="line">    <span class="keyword">public</span> <span class="type">int</span>[] getLeastNumbers(<span class="type">int</span>[] arr, <span class="type">int</span> k) &#123;</span><br><span class="line">        <span class="keyword">if</span> (k &gt;= arr.length) <span class="keyword">return</span> arr;</span><br><span class="line">        <span class="keyword">return</span> quickSort(arr, <span class="number">0</span>, arr.length - <span class="number">1</span>, k);</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>[] quickSort(<span class="type">int</span>[] arr, <span class="type">int</span> left, <span class="type">int</span> right, <span class="type">int</span> k) &#123;</span><br><span class="line">        <span class="type">int</span> <span class="variable">i</span> <span class="operator">=</span> left, j = right, x = arr[left];</span><br><span class="line">        <span class="keyword">while</span> (i &lt; j) &#123;</span><br><span class="line">            <span class="keyword">while</span> (i &lt; j &amp;&amp; arr[j] &gt;= x) j--;</span><br><span class="line">            <span class="keyword">while</span> (i &lt; j &amp;&amp; arr[i] &lt;= x) i++;</span><br><span class="line">            <span class="keyword">if</span> (i &lt; j) &#123;</span><br><span class="line">                <span class="type">int</span> <span class="variable">temp</span> <span class="operator">=</span> arr[j];</span><br><span class="line">                arr[j] = arr[i];</span><br><span class="line">                arr[i] = temp;</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;</span><br><span class="line">        arr[left] = arr[i];</span><br><span class="line">        arr[i] = x;</span><br><span class="line">        <span class="keyword">if</span> (i &gt; k) <span class="keyword">return</span> quickSort(arr, left, i - <span class="number">1</span>, k);</span><br><span class="line">        <span class="keyword">if</span> (i &lt; k) <span class="keyword">return</span> quickSort(arr, i + <span class="number">1</span>, right, k);</span><br><span class="line">        <span class="keyword">return</span> Arrays.copyOf(arr, k);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h1 id="45-把数组排成最小的数"><a href="#45-把数组排成最小的数" class="headerlink" title="45.把数组排成最小的数"></a>45.把数组排成最小的数</h1><p>最简单的方法找出数组的全排序，得到所有数字的可能性，然后比较每个数字的大小就可以了。</p><p>不过这会很麻烦，暂时不考虑这种解法。</p><p>解法的关键是这个数组该怎么排序。使用普通的大于小于关系当然是不行的，所以需要自定义一个大小关系。</p><p>这里就定义了一个关系。要是<code>ab &gt; ba</code>，那就设为<code>a &gt; b</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><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><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">class</span> <span class="title class_">Solution</span> &#123;</span><br><span class="line">    <span class="keyword">public</span> String <span class="title function_">minNumber</span><span class="params">(<span class="type">int</span>[] nums)</span> &#123;</span><br><span class="line">        <span class="keyword">if</span> (nums.length == <span class="number">1</span>) <span class="keyword">return</span> String.valueOf(nums[<span class="number">0</span>]);</span><br><span class="line">        sort(nums, <span class="number">0</span>, nums.length - <span class="number">1</span>);</span><br><span class="line">        <span class="type">StringBuilder</span> <span class="variable">res</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> (<span class="type">int</span> num : nums) &#123;</span><br><span class="line">            res.append(num);</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">return</span> res.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="keyword">public</span> <span class="keyword">void</span> <span class="title function_">sort</span><span class="params">(<span class="type">int</span>[] nums, <span class="type">int</span> l , <span class="type">int</span> r)</span> &#123;</span><br><span class="line">        <span class="keyword">if</span> (l &gt;= r) <span class="keyword">return</span>;</span><br><span class="line">        <span class="type">int</span> <span class="variable">x</span> <span class="operator">=</span> nums[l], i = l - <span class="number">1</span>, j = r + <span class="number">1</span>;</span><br><span class="line">        <span class="keyword">while</span> (i &lt; j) &#123;</span><br><span class="line">            <span class="keyword">do</span> i++; <span class="keyword">while</span> (check(x,nums[i]));</span><br><span class="line">            <span class="keyword">do</span> j--; <span class="keyword">while</span> (check(nums[j],x));</span><br><span class="line">            <span class="keyword">if</span> (i &lt; j) swap(nums, i, j);</span><br><span class="line">        &#125;</span><br><span class="line">        sort(nums, l, j);</span><br><span class="line">        sort(nums, j + <span class="number">1</span>, r);</span><br><span class="line">    &#125;</span><br><span class="line"><span class="comment">// 判断i是否大于j(自定义的大于)</span></span><br><span class="line">    <span class="keyword">public</span> <span class="type">boolean</span> <span class="title function_">check</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">StringBuilder</span> <span class="variable">a</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">StringBuilder</span>();</span><br><span class="line">        <span class="type">StringBuilder</span> <span class="variable">b</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">StringBuilder</span>();</span><br><span class="line">        a.append(i).append(j);</span><br><span class="line">        b.append(j).append(i);</span><br><span class="line">        <span class="keyword">return</span> a.toString().compareTo(b.toString()) &gt; <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_">swap</span><span class="params">(<span class="type">int</span>[] nums, <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">t</span> <span class="operator">=</span> nums[i];</span><br><span class="line">        nums[i] = nums[j];</span><br><span class="line">        nums[j] = t;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    </span><br><span class="line">&#125;</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></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 class="keyword">public</span> String <span class="title function_">minNumber</span><span class="params">(<span class="type">int</span>[] nums)</span> &#123;</span><br><span class="line">        <span class="keyword">if</span> (nums.length == <span class="number">1</span>) <span class="keyword">return</span> String.valueOf(nums[<span class="number">0</span>]);</span><br><span class="line">        List&lt;String&gt; list = <span class="keyword">new</span> <span class="title class_">ArrayList</span>&lt;&gt;();</span><br><span class="line">        <span class="keyword">for</span> (<span class="type">int</span> num : nums) &#123;</span><br><span class="line">            list.add(String.valueOf(num));</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="comment">// 这个排序虽然简单，但是效率好像没有快排高</span></span><br><span class="line">        list.sort((o1,o2) -&gt; (o1 + o2).compareTo(o2 + o1));</span><br><span class="line">        <span class="keyword">return</span> String.join(<span class="string">&quot;&quot;</span>,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><h1 id="46-把数字翻译成字符串"><a href="#46-把数字翻译成字符串" class="headerlink" title="46.把数字翻译成字符串"></a>46.把数字翻译成字符串</h1><h2 id="递归-3"><a href="#递归-3" 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></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 class="keyword">public</span> <span class="type">int</span> <span class="title function_">translateNum</span><span class="params">(<span class="type">int</span> num)</span> &#123;</span><br><span class="line">        <span class="keyword">if</span> (num &lt;= <span class="number">9</span>) <span class="keyword">return</span> <span class="number">1</span>;</span><br><span class="line">        <span class="type">int</span> <span class="variable">ba</span> <span class="operator">=</span> num % <span class="number">100</span>;</span><br><span class="line">        <span class="keyword">if</span> (ba &lt;= <span class="number">9</span> || ba &gt;= <span class="number">26</span>) &#123;</span><br><span class="line">            <span class="keyword">return</span> translateNum(num / <span class="number">10</span>);</span><br><span class="line">        &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">            <span class="keyword">return</span> translateNum(num / <span class="number">10</span>) + translateNum(num / <span class="number">100</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><h1 id="47-礼物的最大价值"><a href="#47-礼物的最大价值" class="headerlink" title="47.礼物的最大价值"></a>47.礼物的最大价值</h1><h2 id="回溯-1"><a href="#回溯-1" class="headerlink" title="回溯"></a>回溯</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><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><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">class</span> <span class="title class_">Solution</span> &#123;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">private</span> <span class="type">int</span> <span class="variable">res</span> <span class="operator">=</span> -<span class="number">1</span>;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">public</span> <span class="type">int</span> <span class="title function_">maxValue</span><span class="params">(<span class="type">int</span>[][] grid)</span> &#123;</span><br><span class="line">        <span class="type">int</span> <span class="variable">rows</span> <span class="operator">=</span> grid.length;</span><br><span class="line">        <span class="type">int</span> <span class="variable">cols</span> <span class="operator">=</span> grid[<span class="number">0</span>].length;</span><br><span class="line">        <span class="type">boolean</span>[][] visited = <span class="keyword">new</span> <span class="title class_">boolean</span>[rows][cols];</span><br><span class="line">        dfs(grid, <span class="number">0</span>, <span class="number">0</span>, grid[<span class="number">0</span>][<span class="number">0</span>], visited);</span><br><span class="line">        <span class="keyword">return</span> res;</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_">dfs</span><span class="params">(<span class="type">int</span>[][] grid, <span class="type">int</span> x, <span class="type">int</span> y, <span class="type">int</span> count, <span class="type">boolean</span>[][] visited)</span> &#123;</span><br><span class="line">        <span class="keyword">if</span> (x == grid.length - <span class="number">1</span> &amp;&amp; y == grid[<span class="number">0</span>].length - <span class="number">1</span>) &#123;</span><br><span class="line">            res = Math.max(count, res);</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">if</span> (x + <span class="number">1</span> &lt; grid.length &amp;&amp; !visited[x + <span class="number">1</span>][y]) &#123;</span><br><span class="line">            visited[x + <span class="number">1</span>][y] = <span class="literal">true</span>;</span><br><span class="line">            dfs(grid, x + <span class="number">1</span>, y, count + grid[x + <span class="number">1</span>][y],visited);</span><br><span class="line">            visited[x + <span class="number">1</span>][y] = <span class="literal">false</span>;</span><br><span class="line">        &#125;</span><br><span class="line"></span><br><span class="line">        <span class="keyword">if</span> (y + <span class="number">1</span> &lt; grid[<span class="number">0</span>].length &amp;&amp; !visited[x][y + <span class="number">1</span>]) &#123;</span><br><span class="line">            visited[x][y + <span class="number">1</span>] = <span class="literal">true</span>;</span><br><span class="line">            dfs(grid, x, y + <span class="number">1</span>, count + grid[x][y + <span class="number">1</span>],visited);</span><br><span class="line">            visited[x][y + <span class="number">1</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><h2 id="动态规划-2"><a href="#动态规划-2" class="headerlink" title="动态规划"></a>动态规划</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></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="type">int</span> <span class="title function_">maxValue</span><span class="params">(<span class="type">int</span>[][] grid)</span> &#123;</span><br><span class="line">        <span class="type">int</span> <span class="variable">rows</span> <span class="operator">=</span> grid.length;</span><br><span class="line">        <span class="type">int</span> <span class="variable">cols</span> <span class="operator">=</span> grid[<span class="number">0</span>].length;</span><br><span class="line">        <span class="type">int</span>[][] dp = <span class="keyword">new</span> <span class="title class_">int</span>[rows + <span class="number">1</span>][cols + <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">1</span>; i &lt;= rows; i++) &#123;</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">1</span>; j &lt;= cols; j++) &#123;</span><br><span class="line">                dp[i][j] = Math.max(dp[i - <span class="number">1</span>][j], dp[i][j - <span class="number">1</span>]) + grid[i - <span class="number">1</span>][j - <span class="number">1</span>];</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">return</span> dp[rows][cols];</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><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><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">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="type">int</span> <span class="title function_">maxValue</span><span class="params">(<span class="type">int</span>[][] grid)</span> &#123;</span><br><span class="line">        <span class="type">int</span> <span class="variable">rows</span> <span class="operator">=</span> grid.length;</span><br><span class="line">        <span class="type">int</span> <span class="variable">cols</span> <span class="operator">=</span> grid[<span class="number">0</span>].length;</span><br><span class="line">        <span class="type">int</span>[] dp = <span class="keyword">new</span> <span class="title class_">int</span>[cols + <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">1</span>; i &lt;= rows; i++) &#123;</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">1</span>; j &lt;= cols; j++) &#123;</span><br><span class="line">                dp[j] = Math.max(dp[j], dp[j - <span class="number">1</span>]) + grid[i - <span class="number">1</span>][j - <span class="number">1</span>];</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">return</span> dp[cols];</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h1 id="48-最长不含重复字符的子字符串"><a href="#48-最长不含重复字符的子字符串" class="headerlink" title="48. 最长不含重复字符的子字符串"></a>48. 最长不含重复字符的子字符串</h1><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></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 class="keyword">public</span> <span class="type">int</span> <span class="title function_">lengthOfLongestSubstring</span><span class="params">(String s)</span> &#123;</span><br><span class="line">        <span class="type">int</span> <span class="variable">len</span> <span class="operator">=</span> s.length();</span><br><span class="line">        <span class="keyword">if</span> (len &lt;= <span class="number">1</span>) <span class="keyword">return</span> len;</span><br><span class="line">        <span class="type">int</span> <span class="variable">l</span> <span class="operator">=</span> <span class="number">0</span>, max = <span class="number">0</span>;</span><br><span class="line">        Map&lt;Character,Integer&gt; map = <span class="keyword">new</span> <span class="title class_">HashMap</span>&lt;&gt;();</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; len; i++) &#123;</span><br><span class="line">            <span class="comment">// 如果已经存在重复的字符，就更新窗口的左边界让字符串不重复</span></span><br><span class="line">            <span class="keyword">if</span> (map.containsKey(s.charAt(i))) &#123;</span><br><span class="line">                <span class="comment">// 重新定义窗口左边界</span></span><br><span class="line">                l = Math.max(l, map.get(s.charAt(i)) + <span class="number">1</span>);</span><br><span class="line">            &#125;</span><br><span class="line">            <span class="comment">// 更新（添加）map里字符的坐标</span></span><br><span class="line">            map.put(s.charAt(i),i);</span><br><span class="line">            <span class="comment">// 计算最大值</span></span><br><span class="line">            max = Math.max(max, i - l + <span class="number">1</span>);</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">return</span> max;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h1 id="49-丑数"><a href="#49-丑数" class="headerlink" title="49. 丑数"></a>49. 丑数</h1><p>使用动态规划，主要是凑成最小的丑数，然后一个个凑到第N个，就是用2,3,5的公倍数从小凑到N。</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><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">class</span> <span class="title class_">Solution</span> &#123;</span><br><span class="line">    <span class="keyword">public</span> <span class="type">int</span> <span class="title function_">nthUglyNumber</span><span class="params">(<span class="type">int</span> n)</span> &#123;</span><br><span class="line">        <span class="type">int</span>[] dp = <span class="keyword">new</span> <span class="title class_">int</span>[n];</span><br><span class="line">        dp[<span class="number">0</span>] = <span class="number">1</span>;</span><br><span class="line">        <span class="type">int</span> <span class="variable">p2</span> <span class="operator">=</span> <span class="number">0</span>, p3 = <span class="number">0</span>, p5 = <span class="number">0</span>;</span><br><span class="line">        <span class="keyword">for</span> (<span class="type">int</span> <span class="variable">i</span> <span class="operator">=</span> <span class="number">1</span>; i &lt; n; i++) &#123;</span><br><span class="line">            <span class="type">int</span> <span class="variable">min</span> <span class="operator">=</span> Math.min(Math.min(dp[p2] * <span class="number">2</span>,dp[p3] * <span class="number">3</span>),dp[p5] * <span class="number">5</span>);</span><br><span class="line">            <span class="keyword">if</span> (min % <span class="number">2</span> == <span class="number">0</span>) p2++;</span><br><span class="line">            <span class="keyword">if</span> (min % <span class="number">3</span> == <span class="number">0</span>) p3++;</span><br><span class="line">            <span class="keyword">if</span> (min % <span class="number">5</span> == <span class="number">0</span>) p5++;</span><br><span class="line">            dp[i] = min;</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">return</span> dp[n - <span class="number">1</span>];</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h1 id="50-第一个只出现一次的字符"><a href="#50-第一个只出现一次的字符" class="headerlink" title="50.第一个只出现一次的字符"></a>50.第一个只出现一次的字符</h1><p>这题用正常思维就可以解决了，暂时找不到更优的解法，都是先存储出现的次数，然后在遍历找出第一个次数为1的字符。</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="keyword">class</span> <span class="title class_">Solution</span> &#123;</span><br><span class="line">    <span class="keyword">public</span> <span class="type">char</span> <span class="title function_">firstUniqChar</span><span class="params">(String s)</span> &#123;</span><br><span class="line">        Map&lt;Character,Integer&gt; map = <span class="keyword">new</span> <span class="title class_">HashMap</span>&lt;&gt;();</span><br><span class="line">        <span class="keyword">for</span> (<span class="type">char</span> c : s.toCharArray()) &#123;</span><br><span class="line">            map.put(c,map.getOrDefault(c,<span class="number">0</span>) + <span class="number">1</span>);</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">for</span> (<span class="type">char</span> c : s.toCharArray()) &#123;</span><br><span class="line">            <span class="keyword">if</span> (map.get(c) == <span class="number">1</span>) <span class="keyword">return</span> c;</span><br><span class="line">        &#125; </span><br><span class="line">        <span class="keyword">return</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><h1 id="51-数组中的逆序对"><a href="#51-数组中的逆序对" class="headerlink" title="51.数组中的逆序对"></a>51.数组中的逆序对</h1><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></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 class="keyword">public</span> <span class="type">int</span> <span class="title function_">reversePairs</span><span class="params">(<span class="type">int</span>[] nums)</span> &#123;</span><br><span class="line">        <span class="type">int</span>[] sum = <span class="keyword">new</span> <span class="title class_">int</span>[nums.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; sum.length; i++) &#123;</span><br><span class="line">            <span class="keyword">for</span> (<span class="type">int</span> <span class="variable">j</span> <span class="operator">=</span> i + <span class="number">1</span>; j &lt; sum.length; j++) &#123;</span><br><span class="line">                <span class="keyword">if</span> (nums[i] &gt; nums[j]) sum[i]++;</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">res</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> s : sum) &#123;</span><br><span class="line">            res += s;</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">return</span> res;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>超时了。。O(n^2)的确很容易超时。</p><h2 id="归并排序"><a href="#归并排序" class="headerlink" title="归并排序"></a>归并排序</h2><p>这个真没想出来。不过这题的确是归并排序的经典题，求出当前数与前面的数或者是后面的数的大小关系的个数的题基本都是归并排序来进行实现的。</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><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><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">class</span> <span class="title class_">Solution</span> &#123;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">private</span> <span class="type">int</span> <span class="variable">res</span> <span class="operator">=</span> <span class="number">0</span>;</span><br><span class="line">    <span class="keyword">private</span> <span class="type">int</span>[] temp;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">public</span> <span class="type">int</span> <span class="title function_">reversePairs</span><span class="params">(<span class="type">int</span>[] nums)</span> &#123;</span><br><span class="line">        temp = <span class="keyword">new</span> <span class="title class_">int</span>[nums.length];</span><br><span class="line">        <span class="keyword">return</span> mergeSort(nums, <span class="number">0</span>, nums.length - <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_">mergeSort</span><span class="params">(<span class="type">int</span>[] nums, <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 class="number">0</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">mid</span> <span class="operator">=</span> l + (r - l) / <span class="number">2</span>;</span><br><span class="line">        mergeSort(nums, l, mid);</span><br><span class="line">        mergeSort(nums, mid + <span class="number">1</span>, r);</span><br><span class="line">        <span class="comment">// 排序合并</span></span><br><span class="line">        <span class="type">int</span> <span class="variable">k</span> <span class="operator">=</span> <span class="number">0</span>, i = l, j = mid + <span class="number">1</span>;</span><br><span class="line">        <span class="keyword">while</span> (i &lt;= mid &amp;&amp; j &lt;= r) &#123;</span><br><span class="line">            <span class="keyword">if</span> (nums[i] &lt;= nums[j]) &#123;</span><br><span class="line">                temp[k++] = nums[i++];</span><br><span class="line">            &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">                temp[k++] = nums[j++];</span><br><span class="line">                <span class="comment">// 理解了这个结果的计算就容易了</span></span><br><span class="line">                <span class="comment">//如果j位置小于i位置，那么j位置小于i位置后所有的右半边的数</span></span><br><span class="line">                res += mid - i + <span class="number">1</span>;</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">while</span> (i &lt;= mid) &#123;</span><br><span class="line">            temp[k++] = nums[i++];</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">while</span> (j &lt;= r) &#123;</span><br><span class="line">            temp[k++] = nums[j++];</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="comment">// 排序结果放入原数组，完成排序</span></span><br><span class="line">        <span class="keyword">for</span> (i = l, j = <span class="number">0</span>; i &lt;= r; i++, j++) &#123;</span><br><span class="line">            nums[i] = temp[j];</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">return</span> res;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h1 id="52-两个链表的第一个公共节点"><a href="#52-两个链表的第一个公共节点" class="headerlink" title="52.两个链表的第一个公共节点"></a>52.两个链表的第一个公共节点</h1><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></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <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_">getIntersectionNode</span><span class="params">(ListNode headA, ListNode headB)</span> &#123;</span><br><span class="line">        <span class="type">ListNode</span> <span class="variable">n1</span> <span class="operator">=</span> headA;</span><br><span class="line">        <span class="type">ListNode</span> <span class="variable">n2</span> <span class="operator">=</span> headB;</span><br><span class="line">        <span class="comment">// 都遍历了一段公共的节点与两个链表不公共的部分，凑成了等长的</span></span><br><span class="line">        <span class="keyword">while</span> (n1 != n2) &#123;</span><br><span class="line">            n1 = n1 == <span class="literal">null</span> ? headB : n1.next;</span><br><span class="line">            n2 = n2 == <span class="literal">null</span> ? headA : n2.next;</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">return</span> n1;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h1 id="53-1在排序数组中查找数字1"><a href="#53-1在排序数组中查找数字1" class="headerlink" title="53.1在排序数组中查找数字1"></a>53.1在排序数组中查找数字1</h1><h2 id="暴力破解-1"><a href="#暴力破解-1" class="headerlink" title="暴力破解"></a>暴力破解</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></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 class="keyword">public</span> <span class="type">int</span> <span class="title function_">search</span><span class="params">(<span class="type">int</span>[] nums, <span class="type">int</span> target)</span> &#123;</span><br><span class="line">        <span class="type">int</span> <span class="variable">res</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> n : nums) &#123;</span><br><span class="line">            <span class="keyword">if</span> (n == target) res++;</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">return</span> res;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h2 id="二分查找-1"><a href="#二分查找-1" class="headerlink" title="二分查找"></a>二分查找</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></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 class="keyword">public</span> <span class="type">int</span> <span class="title function_">search</span><span class="params">(<span class="type">int</span>[] nums, <span class="type">int</span> target)</span> &#123;</span><br><span class="line">        <span class="type">int</span> <span class="variable">l</span> <span class="operator">=</span> <span class="number">0</span>, r = nums.length;</span><br><span class="line">        <span class="type">int</span> <span class="variable">res</span> <span class="operator">=</span> <span class="number">0</span>;</span><br><span class="line">        <span class="keyword">while</span> (l &lt; r) &#123;</span><br><span class="line">            <span class="type">int</span> <span class="variable">mid</span> <span class="operator">=</span> l + (r - l) / <span class="number">2</span>;</span><br><span class="line">            <span class="keyword">if</span> (nums[mid] &gt;= target) &#123;</span><br><span class="line">                r = mid;</span><br><span class="line">            &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">                l = mid + <span class="number">1</span>;</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">while</span> (l &lt; nums.length &amp;&amp; nums[l++] == target) &#123;</span><br><span class="line">            res++;</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">return</span> res;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h1 id="53-20-n-1中缺少的数字"><a href="#53-20-n-1中缺少的数字" class="headerlink" title="53.20~n-1中缺少的数字"></a>53.20~n-1中缺少的数字</h1><h2 id="暴力破解-2"><a href="#暴力破解-2" class="headerlink" title="暴力破解"></a>暴力破解</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></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 class="keyword">public</span> <span class="type">int</span> <span class="title function_">missingNumber</span><span class="params">(<span class="type">int</span>[] nums)</span> &#123;</span><br><span class="line">        <span class="type">int</span> <span class="variable">res</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> num : nums) &#123;</span><br><span class="line">            <span class="keyword">if</span> (num != res) <span class="keyword">return</span> res;</span><br><span class="line">            res++;</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">return</span> res;</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>求和然后逐个减去数组中的数字，这样就能找出缺少的数字了。</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><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">class</span> <span class="title class_">Solution</span> &#123;</span><br><span class="line">    <span class="keyword">public</span> <span class="type">int</span> <span class="title function_">missingNumber</span><span class="params">(<span class="type">int</span>[] nums)</span> &#123;</span><br><span class="line">        <span class="type">int</span> <span class="variable">res</span> <span class="operator">=</span> <span class="number">0</span>;</span><br><span class="line">        <span class="keyword">for</span> (<span class="type">int</span> <span class="variable">i</span> <span class="operator">=</span> <span class="number">0</span>; i &lt;= nums.length; i++) &#123;</span><br><span class="line">            res += i;</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">for</span> (<span class="type">int</span> num : nums) &#123;</span><br><span class="line">            res -= num;</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">return</span> res;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h2 id="二分查找-2"><a href="#二分查找-2" class="headerlink" title="二分查找"></a>二分查找</h2><p>数组有序，那就是二分查找。</p><p>最主要的问题是查找什么。</p><p>若是每个数字都不缺少，那n这个数字应该会在数组的n的下标的位置。</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">class</span> <span class="title class_">Solution</span> &#123;</span><br><span class="line">    <span class="keyword">public</span> <span class="type">int</span> <span class="title function_">missingNumber</span><span class="params">(<span class="type">int</span>[] nums)</span> &#123;</span><br><span class="line">        <span class="type">int</span> <span class="variable">l</span> <span class="operator">=</span> <span class="number">0</span>, r = nums.length;</span><br><span class="line">        <span class="keyword">while</span> (l &lt; r) &#123;</span><br><span class="line">            <span class="type">int</span> <span class="variable">mid</span> <span class="operator">=</span> l + (r - l) / <span class="number">2</span>;</span><br><span class="line">            <span class="keyword">if</span> (nums[mid] == mid) l = mid + <span class="number">1</span>;</span><br><span class="line">            <span class="keyword">else</span> r = mid;</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">return</span> l;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h1 id="56-1-数组中数字出现的次数"><a href="#56-1-数组中数字出现的次数" class="headerlink" title="56.1 数组中数字出现的次数"></a>56.1 数组中数字出现的次数</h1><h2 id="位运算-1"><a href="#位运算-1" class="headerlink" title="位运算"></a>位运算</h2><p>首先看到这题就想到之前写的找出唯一一个不是两个数的数字，使用异或很快就解决了问题。</p><p>不过这里的数组有两个这样的数，所以就无法直接异或得到答案，需要进行一些处理。</p><h1 id="58-2-左旋转字符串"><a href="#58-2-左旋转字符串" class="headerlink" title="58.2 左旋转字符串"></a>58.2 左旋转字符串</h1><h2 id="切片"><a href="#切片" class="headerlink" title="切片"></a>切片</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></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 class="keyword">public</span> String <span class="title function_">reverseLeftWords</span><span class="params">(String s, <span class="type">int</span> n)</span> &#123;</span><br><span class="line">        <span class="keyword">return</span> s.substring(n) + s.substring(<span class="number">0</span>,n);</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>遍历字符串一个个加入到结果字符中也是可以的。</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><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_">Solution</span> &#123;</span><br><span class="line">    <span class="keyword">public</span> String <span class="title function_">reverseLeftWords</span><span class="params">(String s, <span class="type">int</span> n)</span> &#123;</span><br><span class="line">        <span class="type">StringBuilder</span> <span class="variable">res</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">StringBuilder</span>();</span><br><span class="line">        <span class="type">char</span>[] arr = s.toCharArray();</span><br><span class="line">        <span class="keyword">for</span> (<span class="type">int</span> <span class="variable">i</span> <span class="operator">=</span> n; i &lt; arr.length; i++) res.append(arr[i]);</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++) res.append(arr[i]);</span><br><span class="line">        <span class="keyword">return</span> res.toString();</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h1 id="60-n个筛子的点数"><a href="#60-n个筛子的点数" class="headerlink" title="60. n个筛子的点数"></a>60. n个筛子的点数</h1><p>这题是动态规划问题，状态转移方程有点类似多重背包。</p><p><code>dp[i][j] += dp[i - 1][j - k] * dp[1][k]</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></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 class="keyword">public</span> <span class="type">double</span>[] dicesProbability(<span class="type">int</span> n) &#123;</span><br><span class="line">        <span class="type">double</span>[][] dp = <span class="keyword">new</span> <span class="title class_">double</span>[n + <span class="number">1</span>][<span class="number">6</span> * n + <span class="number">1</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> <span class="variable">i</span> <span class="operator">=</span> <span class="number">1</span>; i &lt;= <span class="number">6</span>; i++) dp[<span class="number">1</span>][i] = (<span class="type">double</span>) <span class="number">1</span> / <span class="number">6</span>;</span><br><span class="line"></span><br><span class="line">        <span class="keyword">for</span> (<span class="type">int</span> <span class="variable">i</span> <span class="operator">=</span> <span class="number">2</span>; i &lt;= n; i++) &#123;</span><br><span class="line">           <span class="comment">// i颗筛子的最大点数</span></span><br><span class="line">            <span class="type">int</span> <span class="variable">max</span> <span class="operator">=</span> <span class="number">6</span> * i;    </span><br><span class="line">            <span class="comment">// 遍历i颗筛子的所有点数求出概率</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> i; j &lt;= max; j++) &#123;</span><br><span class="line">                <span class="keyword">for</span> (<span class="type">int</span> <span class="variable">k</span> <span class="operator">=</span> <span class="number">1</span>; k &lt;= <span class="number">6</span>; k++) &#123;</span><br><span class="line">                    <span class="keyword">if</span> (j - k &lt;= <span class="number">0</span>) &#123;   </span><br><span class="line">                        <span class="keyword">continue</span>;</span><br><span class="line">                    &#125;</span><br><span class="line">                    dp[i][j] += dp[i - <span class="number">1</span>][j - k] * dp[<span class="number">1</span>][k];</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><span class="line">        <span class="type">double</span>[] res = <span class="keyword">new</span> <span class="title class_">double</span>[<span class="number">5</span> * n + <span class="number">1</span>];</span><br><span class="line">        <span class="type">int</span> <span class="variable">value</span> <span class="operator">=</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; res.length; i++) &#123;</span><br><span class="line">            res[i] = dp[n][value++];</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">return</span> res;</span><br><span class="line"></span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h1 id="61-扑克牌的顺子"><a href="#61-扑克牌的顺子" class="headerlink" title="61. 扑克牌的顺子"></a>61. 扑克牌的顺子</h1><p>首先确定答案的约束。</p><ol><li>抽到的五张牌是不是连续的。</li><li>抽到的五张牌是不重复的。</li><li>大小王可以换为任意一张牌，可以存在重复的0</li></ol><p>这里可以找出一定的规律，抽到的五张牌中最大的减去最小的小于5就可以说明这五张牌是重复的了。</p><h2 id="Set去重-1"><a href="#Set去重-1" class="headerlink" title="Set去重"></a>Set去重</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></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 class="keyword">public</span> <span class="type">boolean</span> <span class="title function_">isStraight</span><span class="params">(<span class="type">int</span>[] nums)</span> &#123;</span><br><span class="line">        Set&lt;Integer&gt; res = <span class="keyword">new</span> <span class="title class_">HashSet</span>&lt;&gt;();</span><br><span class="line">        <span class="type">int</span> <span class="variable">max</span> <span class="operator">=</span> -<span class="number">1</span>, min = <span class="number">14</span>;</span><br><span class="line">        <span class="keyword">for</span> (<span class="type">int</span> i : nums) &#123;</span><br><span class="line">            <span class="keyword">if</span> (i == <span class="number">0</span>) <span class="keyword">continue</span>;</span><br><span class="line">            max = Math.max(max,i);</span><br><span class="line">            min = Math.min(min,i);</span><br><span class="line">            <span class="comment">// 如果有重复的就返回false</span></span><br><span class="line">            <span class="keyword">if</span> (!res.add(i)) <span class="keyword">return</span> <span class="literal">false</span>;</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">return</span> max - min &lt; <span class="number">5</span>;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h2 id="排序遍历-1"><a href="#排序遍历-1" 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></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 class="keyword">public</span> <span class="type">boolean</span> <span class="title function_">isStraight</span><span class="params">(<span class="type">int</span>[] nums)</span> &#123;</span><br><span class="line">        Arrays.sort(nums);</span><br><span class="line">        <span class="type">int</span> <span class="variable">count</span> <span class="operator">=</span> <span class="number">0</span>;</span><br><span class="line">        <span class="keyword">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="comment">// 统计大小王的个数，用于寻找最小的牌</span></span><br><span class="line">            <span class="keyword">if</span> (nums[i] == <span class="number">0</span>) count++;</span><br><span class="line">            <span class="comment">// 若是重复了就直接返回false</span></span><br><span class="line">            <span class="keyword">else</span> <span class="keyword">if</span> (nums[i] == nums[i + <span class="number">1</span>]) <span class="keyword">return</span> <span class="literal">false</span>;</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="comment">// 最大的牌减去最小的牌小于5d</span></span><br><span class="line">        <span class="keyword">return</span> nums[<span class="number">4</span>] - nums[count] &lt; <span class="number">5</span>;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h1 id="62-圆圈中最后剩下的数字"><a href="#62-圆圈中最后剩下的数字" class="headerlink" title="62.圆圈中最后剩下的数字"></a>62.圆圈中最后剩下的数字</h1><h2 id="模拟转圈"><a href="#模拟转圈" class="headerlink" title="模拟转圈"></a>模拟转圈</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></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 class="keyword">public</span> <span class="type">int</span> <span class="title function_">lastRemaining</span><span class="params">(<span class="type">int</span> n, <span class="type">int</span> m)</span> &#123;</span><br><span class="line">        <span class="keyword">if</span> (n == <span class="number">1</span>) <span class="keyword">return</span> <span class="number">1</span>;</span><br><span class="line">        List&lt;Integer&gt; list = <span class="keyword">new</span> <span class="title class_">ArrayList</span>&lt;&gt;(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; n; i++) &#123;</span><br><span class="line">            list.add(i);</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="type">int</span> <span class="variable">k</span> <span class="operator">=</span> <span class="number">0</span>;</span><br><span class="line">        <span class="keyword">while</span> (n &gt; <span class="number">1</span>) &#123;</span><br><span class="line">            k = (k + m - <span class="number">1</span>) % n;</span><br><span class="line">            list.remove(k);</span><br><span class="line">            n--;</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">return</span> list.get(<span class="number">0</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>这题就算典序的约瑟夫环的问题。</p><p>约瑟夫环有一个递推公式。</p><p><code>F(n,m) = (f(n - 1,m) + m) % n</code></p><p>F(n,m)表示的是n个人报数，报到m的人会被杀掉，最终胜利者的编号，也就是这题的答案。</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><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">class</span> <span class="title class_">Solution</span> &#123;</span><br><span class="line">    <span class="keyword">public</span> <span class="type">int</span> <span class="title function_">lastRemaining</span><span class="params">(<span class="type">int</span> n, <span class="type">int</span> m)</span> &#123;</span><br><span class="line">        <span class="type">int</span> <span class="variable">res</span> <span class="operator">=</span> <span class="number">0</span>;</span><br><span class="line">        <span class="comment">// f(2,m)是最初的状态，需要加到F(n,m)才是最终答案</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">2</span>; i &lt;= n; i++) &#123;</span><br><span class="line">            res = (res + m) % i;</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">return</span> res;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h1 id="63-股票的最大利润"><a href="#63-股票的最大利润" class="headerlink" title="63. 股票的最大利润"></a>63. 股票的最大利润</h1><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">class</span> <span class="title class_">Solution</span> &#123;</span><br><span class="line">    <span class="keyword">public</span> <span class="type">int</span> <span class="title function_">maxProfit</span><span class="params">(<span class="type">int</span>[] prices)</span> &#123;</span><br><span class="line">        <span class="keyword">if</span> (prices.length &lt;= <span class="number">1</span>) <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">        <span class="type">int</span> <span class="variable">res</span> <span class="operator">=</span> <span class="number">0</span>, min = prices[<span class="number">0</span>];</span><br><span class="line">        <span class="keyword">for</span> (<span class="type">int</span> <span class="variable">i</span> <span class="operator">=</span> <span class="number">1</span>; i &lt; prices.length; i++) &#123;</span><br><span class="line">            <span class="comment">// 找到一个更小的数就替换</span></span><br><span class="line">            <span class="keyword">if</span> (prices[i] &lt;= min) &#123;</span><br><span class="line">                min = prices[i];</span><br><span class="line">            &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">                res = Math.max(res,prices[i] - min);</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">return</span> res;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h1 id="64-求1-2-…-n"><a href="#64-求1-2-…-n" class="headerlink" title="64. 求1+2+…+n"></a>64. 求1+2+…+n</h1><p>这题题目是不难的，但是题目的要求让这题变难了，不能使用乘除法、for、while、if、else、switch、case等关键字及条件判断语句（A?B:C）。</p><p>这里运用到了<code>&amp;&amp;</code>短路与的特性，判断了第一个子式后如果是<code>false</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="keyword">class</span> <span class="title class_">Solution</span> &#123;</span><br><span class="line">    <span class="keyword">public</span> <span class="type">int</span> <span class="title function_">sumNums</span><span class="params">(<span class="type">int</span> n)</span> &#123;</span><br><span class="line">        <span class="type">int</span> <span class="variable">sum</span> <span class="operator">=</span> n;</span><br><span class="line">        <span class="type">boolean</span> <span class="variable">flag</span> <span class="operator">=</span> n &gt; <span class="number">0</span> &amp;&amp; (sum += sumNums(n - <span class="number">1</span>)) &gt; <span class="number">0</span>;</span><br><span class="line">        <span class="keyword">return</span> sum;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h1 id="65-不用加减乘除做加法"><a href="#65-不用加减乘除做加法" class="headerlink" title="65. 不用加减乘除做加法"></a>65. 不用加减乘除做加法</h1><p>不用加减乘除，那就肯定是位运算了。然后就只能看题解了。</p><p><code>^</code>表示两个数无进位的求和，比如<code>16 + 4</code>,无进位求和就是等于<code>10</code>的，相当于进位没有了。</p><p><code>&amp;</code>表示两个数进位的个数，比如<code>6 + 4</code> ，答案是等于<code>1</code>的，因为进了一位，需要在左移一位才能等于<code>10</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="keyword">class</span> <span class="title class_">Solution</span> &#123;</span><br><span class="line">    <span class="keyword">public</span> <span class="type">int</span> <span class="title function_">add</span><span class="params">(<span class="type">int</span> a, <span class="type">int</span> b)</span> &#123;</span><br><span class="line">        <span class="comment">// 直到进位数为0，就停止循环</span></span><br><span class="line">        <span class="keyword">while</span>(b != <span class="number">0</span>) &#123; </span><br><span class="line">            <span class="type">int</span> <span class="variable">c</span> <span class="operator">=</span> (a &amp; b) &lt;&lt; <span class="number">1</span>;  </span><br><span class="line">            a ^= b; </span><br><span class="line">            b = c; </span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">return</span> a;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h1 id="66-构建乘积数组"><a href="#66-构建乘积数组" class="headerlink" title="66. 构建乘积数组"></a>66. 构建乘积数组</h1><p>这题主要是除了当前元素其他元素都相乘，其实就算数左边的元素的乘积与右边元素的乘积的乘积。</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">class</span> <span class="title class_">Solution</span> &#123;</span><br><span class="line">    <span class="keyword">public</span> <span class="type">int</span>[] constructArr(<span class="type">int</span>[] a) &#123;</span><br><span class="line">        <span class="keyword">if</span> (a.length &lt;= <span class="number">1</span>) <span class="keyword">return</span> a;</span><br><span class="line">        <span class="type">int</span>[] res = <span class="keyword">new</span> <span class="title class_">int</span>[a.length];</span><br><span class="line">        <span class="type">int</span> <span class="variable">left</span> <span class="operator">=</span> <span class="number">1</span>;</span><br><span class="line">        <span class="type">int</span> <span class="variable">right</span> <span class="operator">=</span> <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; a.length; i++) &#123;</span><br><span class="line">            res[i] = left;</span><br><span class="line">            left *= a[i];</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">for</span>(<span class="type">int</span> <span class="variable">i</span> <span class="operator">=</span> a.length - <span class="number">1</span>; i &gt; -<span class="number">1</span>; i--) &#123;</span><br><span class="line">            res[i] *= right;</span><br><span class="line">            right *= a[i];</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">return</span> res;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h1 id="68-1-二叉搜索树的最近公共祖先"><a href="#68-1-二叉搜索树的最近公共祖先" class="headerlink" title="68.1 二叉搜索树的最近公共祖先"></a>68.1 二叉搜索树的最近公共祖先</h1><p>这题主要总结节点的情况，该题的节点应该是分为三种的。</p><ol><li>p和q都在左子树</li><li>p和q都在右子树</li><li>p和q一个在左子树，一个在右子树（这种情况的节点就算二叉树的最近公共祖先）</li></ol><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">class</span> <span class="title class_">Solution</span> &#123;</span><br><span class="line">    <span class="keyword">public</span> TreeNode <span class="title function_">lowestCommonAncestor</span><span class="params">(TreeNode root, TreeNode p, TreeNode q)</span> &#123;</span><br><span class="line">        <span class="keyword">if</span> (root == <span class="literal">null</span>) <span class="keyword">return</span> <span class="literal">null</span>;</span><br><span class="line">        <span class="keyword">if</span> (root.val &gt; p.val &amp;&amp; root.val &gt; q.val)</span><br><span class="line">            <span class="keyword">return</span> lowestCommonAncestor(root.left,p,q);</span><br><span class="line">        <span class="keyword">if</span> (root.val &lt; p.val &amp;&amp; root.val &lt; q.val)</span><br><span class="line">            <span class="keyword">return</span> lowestCommonAncestor(root.right,p,q);</span><br><span class="line">        <span class="keyword">return</span> root;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h1 id="68-2-二叉树的最近公共祖先"><a href="#68-2-二叉树的最近公共祖先" class="headerlink" title="68.2 二叉树的最近公共祖先"></a>68.2 二叉树的最近公共祖先</h1><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">class</span> <span class="title class_">Solution</span> &#123;</span><br><span class="line">    <span class="keyword">public</span> TreeNode <span class="title function_">lowestCommonAncestor</span><span class="params">(TreeNode root, TreeNode p, TreeNode q)</span> &#123;</span><br><span class="line">        <span class="keyword">if</span> (root == p || root == q || root == <span class="literal">null</span>) <span class="keyword">return</span> root;</span><br><span class="line">        <span class="type">TreeNode</span> <span class="variable">left</span> <span class="operator">=</span> lowestCommonAncestor(root.left,p,q);</span><br><span class="line">        <span class="type">TreeNode</span> <span class="variable">right</span> <span class="operator">=</span> lowestCommonAncestor(root.right,p,q);</span><br><span class="line">        <span class="keyword">if</span> (left == <span class="literal">null</span>) <span class="keyword">return</span> right;</span><br><span class="line">        <span class="keyword">if</span> (right == <span class="literal">null</span>) <span class="keyword">return</span> left;</span><br><span class="line">        <span class="keyword">if</span> (left != <span class="literal">null</span> &amp;&amp; right != <span class="literal">null</span>) <span class="keyword">return</span> root;</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>]]></content:encoded>
            
            
            <category domain="https://yww52.com/categories/%E6%88%91%E7%9A%84%E6%8A%80%E6%9C%AF%E5%AD%A6%E4%B9%A0%E7%89%A9%E8%AF%AD%E6%9E%9C%E7%84%B6%E6%9C%89%E9%97%AE%E9%A2%98/">我的技术学习物语果然有问题</category>
            
            <category domain="https://yww52.com/categories/%E6%88%91%E7%9A%84%E6%8A%80%E6%9C%AF%E5%AD%A6%E4%B9%A0%E7%89%A9%E8%AF%AD%E6%9E%9C%E7%84%B6%E6%9C%89%E9%97%AE%E9%A2%98/%E7%AE%97%E6%B3%95/">算法</category>
            
            
            
            <comments>https://yww52.com/posts/dd921d98/#disqus_thread</comments>
            
        </item>
        
        <item>
            <title>Redis</title>
            <link>https://yww52.com/posts/bae4ff13/</link>
            <guid>https://yww52.com/posts/bae4ff13/</guid>
            <pubDate>Sun, 14 Feb 2021 16:00:00 GMT</pubDate>
            
            <description>学习Redis的笔记</description>
            
            
            
            <content:encoded><![CDATA[<img src="https://img.yww52.com/2021/2/2021-2-15/top_img.jpg" alt="Redis" style="max-width:100%;height:auto;margin-bottom:15px;"
  /><h1 id="什么是NoSQL"><a href="#什么是NoSQL" class="headerlink" title="什么是NoSQL"></a>什么是NoSQL</h1><p>先来看看百度百科对NoSQL的描述。</p><blockquote><p>NoSQL，泛指非关系型的数据库。随着互联网web2.0网站的兴起，传统的关系数据库在处理web2.0网站，特别是超大规模和高并发的SNS类型的web2.0纯动态网站已经显得力不从心，出现了很多难以克服的问题，而非关系型的数据库则由于其本身的特点得到了非常迅速的发展。NoSQL数据库的产生就是为了解决大规模数据集合多重数据种类带来的挑战，特别是大数据应用难题。</p></blockquote><p>NoSQL维基百科称其为<code>Not Only SQL</code>,在当今的大数据年代，仅仅是关系型数据库已经不能满足当前一些流量较大的Web应用，所以NoSQL就应运而生，就是用来解决数据量较大的问题。</p><p>NoSQL可以分成四个大类。</p><ol><li><p>键值存储数据库</p></li><li><p>列存储数据库</p></li><li><p>文档型数据库</p></li><li><p>图形数据库</p></li></ol><p>这里我就用百度百科上的表格，来简单了解一下这四个分类。</p><div class="table-container"><table><thead><tr><th style="text-align:center">分类</th><th style="text-align:center">Examples举例</th><th style="text-align:center">典型应用场景</th><th style="text-align:center">数据模型</th><th style="text-align:center">优点</th><th style="text-align:center">缺点</th></tr></thead><tbody><tr><td style="text-align:center">键值存储数据库</td><td style="text-align:center">Tokyo Cabinet/Tyrant， Redis， Voldemort， Oracle BDB</td><td style="text-align:center">内容缓存，主要用于处理大量数据的高访问负载，也用于一些日志系统等等。</td><td style="text-align:center">Key 指向 Value 的键值对，通常用hash table来实现</td><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">Cassandra， HBase， Riak</td><td style="text-align:center">分布式的文件系统</td><td style="text-align:center">以列簇式存储，将同一列数据存在一起</td><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">CouchDB， MongoDb</td><td style="text-align:center">Web应用（与Key-Value类似，Value是结构化的，不同的是数据库能够了解Value的内容）</td><td style="text-align:center">Key-Value对应的键值对，Value为结构化数据</td><td style="text-align:center">数据结构要求不严格，表结构可变，不需要像关系型数据库一样需要预先定义表结构</td><td style="text-align:center">查询性能不高，而且缺乏统一的查询语法。</td></tr><tr><td style="text-align:center">图形(Graph)数据库</td><td style="text-align:center">Neo4J， InfoGrid， Infinite Graph</td><td style="text-align:center">社交网络，推荐系统等。专注于构建关系图谱</td><td style="text-align:center">图结构</td><td style="text-align:center">利用图结构相关算法。比如最短路径寻址，N度关系查找等</td><td style="text-align:center">很多时候需要对整个图做计算才能得出需要的信息，而且这种结构不太好做分布式的集群方案。</td></tr></tbody></table></div><p>这次学习的就是键值存储数据库中的Redis。</p><h1 id="什么是Redis"><a href="#什么是Redis" class="headerlink" title="什么是Redis"></a>什么是Redis</h1><p>Redis是<code>Remote Dictionary Server</code>的缩写，翻译为远程字典调用，是一个开源的使用ANSI C语言编写、支持网络、可基于内存亦可持久化的日志型、Key-Value数据库，并提供多种语言的API。</p><ul><li><a href="https://redis.io/">官网</a></li><li><a href="https://github.com/redis/redis">Github</a></li></ul><h1 id="安装"><a href="#安装" class="headerlink" title="安装"></a>安装</h1><h2 id="windows"><a href="#windows" class="headerlink" title="windows"></a>windows</h2><p>windows的只需要下载他们提供的压缩包使用就好了。</p><p><img src="https://img.yww52.com/2021/2/2021-2-15/img1.png" alt=""></p><p>打开服务后就能看到服务的信息，默认端口为6379</p><p><img src="https://img.yww52.com/2021/2/2021-2-15/img2.png" alt=""></p><p>接下来来确认一下服务是否真的开启了，打开客户端，使用<code>ping</code>命令来测试，出现<code>PONG</code>表示已经连接到了Redis的服务。</p><p><img src="https://img.yww52.com/2021/2/2021-2-15/img3.png" alt=""></p><div class="note info flat"><p>windows的版本活跃度很低，而且官网也不推荐使用windows，所以还是尽量使用Linux，之后的使用我也是使用Linux来测试。</p></div><h2 id="Linux"><a href="#Linux" class="headerlink" title="Linux"></a>Linux</h2><p>去到redis的github就能下载到压缩包使用。</p><p>因为Redis是使用C/C++写的，所以要想使用，还是需要先安装一下C/C++的环境。</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></pre></td><td class="code"><pre><span class="line"><span class="comment"># 安装环境</span></span><br><span class="line">yum install gcc-c++</span><br><span class="line"><span class="comment"># 配置</span></span><br><span class="line">make</span><br><span class="line">make install</span><br></pre></td></tr></table></figure><blockquote><p>要是make命令时出现了很多的error，大概率就是因为redis6版本是需要gcc版本是5以上的才行，所以需要先升级gcc的版本，这里是升级到了9。</p></blockquote><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></pre></td><td class="code"><pre><span class="line">    yum -y install centos-release-scl</span><br><span class="line">    yum -y install devtoolset-9-gcc devtoolset-9-gcc-c++ devtoolset-9-binutils </span><br><span class="line">    scl <span class="built_in">enable</span> devtoolset-9 bash</span><br><span class="line"><span class="comment"># scl命令是临时启动，长期使用就需要配置环境</span></span><br><span class="line"><span class="built_in">echo</span> <span class="string">&quot;source /opt/rh/devtoolset-9/enable&quot;</span> &gt;&gt;/etc/profile</span><br></pre></td></tr></table></figure><p>Redis默认会安装到<code>/usr/local/bin</code>的目录里。</p><p>Redis不是默认启动的，为了方便学习，要设置成默认启动的，所以去修改<code>redis.conf</code>这个文件。</p><figure class="highlight plaintext"><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"># 将这个选项设置为yes,默认为no</span><br><span class="line">daemonize yes</span><br></pre></td></tr></table></figure><p>启动Redis的服务。</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># 以哪个配置文件来启动redis的服务</span></span><br><span class="line">redis-server [conf的路径]</span><br><span class="line"><span class="comment"># 打开redis的客户端，连接6379端口</span></span><br><span class="line">redis-cli -p 6379</span><br><span class="line"><span class="comment"># 出现PONG表示连通</span></span><br><span class="line">ping</span><br></pre></td></tr></table></figure><p>关闭Redis的服务</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></pre></td><td class="code"><pre><span class="line"><span class="comment"># 在客户端连接6379端口中</span></span><br><span class="line">SHUTDOWN</span><br><span class="line"><span class="comment"># 退出客户端</span></span><br><span class="line"><span class="built_in">exit</span></span><br></pre></td></tr></table></figure><h1 id="性能测试"><a href="#性能测试" class="headerlink" title="性能测试"></a>性能测试</h1><p>Redis的目录下还会有一个命令<code>redis-benchmark</code>。</p><p>这个命令是官方自带的性能测试的命令，用来测试Redis的一些基础命令读写的速度，以下是命令的参数。</p><div class="table-container"><table><thead><tr><th style="text-align:center">参数选项</th><th style="text-align:center">描述</th><th style="text-align:center">默认值</th></tr></thead><tbody><tr><td style="text-align:center">-h</td><td style="text-align:center">指定服务器主机名</td><td style="text-align:center">127.0.0.1</td></tr><tr><td style="text-align:center">-p</td><td style="text-align:center">指定服务器端口</td><td style="text-align:center">6379</td></tr><tr><td style="text-align:center">-s</td><td style="text-align:center">指定服务器 socket</td><td style="text-align:center"></td></tr><tr><td style="text-align:center">-c</td><td style="text-align:center">指定并发连接数</td><td style="text-align:center">50</td></tr><tr><td style="text-align:center">-n</td><td style="text-align:center">指定请求数</td><td style="text-align:center">10000</td></tr><tr><td style="text-align:center">-d</td><td style="text-align:center">以字节的形式指定 SET/GET 值的数据大小</td><td style="text-align:center">2</td></tr><tr><td style="text-align:center">-k</td><td style="text-align:center">1=keep alive 0=reconnect</td><td style="text-align:center">1</td></tr><tr><td style="text-align:center">-r</td><td style="text-align:center">SET/GET/INCR 使用随机 key, SADD 使用随机值</td><td style="text-align:center"></td></tr><tr><td style="text-align:center">-P</td><td style="text-align:center">通过管道传输 <numreq> 请求</td><td style="text-align:center">1</td></tr><tr><td style="text-align:center">-q</td><td style="text-align:center">强制退出 redis。仅显示 query/sec 值</td><td style="text-align:center"></td></tr><tr><td style="text-align:center">—csv</td><td style="text-align:center">以 CSV 格式输出</td><td style="text-align:center"></td></tr><tr><td style="text-align:center">-l</td><td style="text-align:center">生成循环，永久执行测试</td><td style="text-align:center"></td></tr><tr><td style="text-align:center">-t</td><td style="text-align:center">仅运行以逗号分隔的测试命令列表</td><td style="text-align:center"></td></tr><tr><td style="text-align:center">-I</td><td style="text-align:center">Idle 模式。仅打开 N 个 idle 连接并等待。</td></tr></tbody></table></div><h1 id="数据类型"><a href="#数据类型" class="headerlink" title="数据类型"></a>数据类型</h1><p>Redis支持的数据类型有很多，官网也写的很清楚了。</p><blockquote><p>Redis provides data structures such as strings, hashes, lists, sets, sorted sets with range queries, bitmaps, hyperloglogs, geospatial indexes, and streams.</p></blockquote><h2 id="String-字符串"><a href="#String-字符串" class="headerlink" title="String(字符串)"></a>String(字符串)</h2><div class="note info flat"><p>String一般使用是用来计数，比如用户的访问次数，热点文章的点击数，转发数等。</p></div><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br></pre></td><td class="code"><pre><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="built_in">set</span> [key] [value]</span><br><span class="line"><span class="comment"># 获取值</span></span><br><span class="line">get [key]</span><br><span class="line"><span class="comment"># 查看所有的键值对</span></span><br><span class="line">keys *</span><br><span class="line"><span class="comment"># 切换数据库（redis默认16个数据库，从0开始）</span></span><br><span class="line"><span class="keyword">select</span> [index]</span><br><span class="line"><span class="comment"># 清空数据库</span></span><br><span class="line">flushdb <span class="comment"># 当前数据库</span></span><br><span class="line">flushall <span class="comment"># 所有的数据库 </span></span><br><span class="line"><span class="comment"># 判断键值对是否存在</span></span><br><span class="line">exists [key]</span><br><span class="line"><span class="comment"># 往值追加字符串</span></span><br><span class="line">append [key] [value]</span><br><span class="line"><span class="comment"># 获取字符串的长度</span></span><br><span class="line">strlen [key]</span><br><span class="line"><span class="comment"># 自增,注意该字符串是要是数字</span></span><br><span class="line">incr [key]<span class="comment"># 步长为1</span></span><br><span class="line">incrby [key] [length]</span><br><span class="line"><span class="comment"># 自减</span></span><br><span class="line">decr [key]</span><br><span class="line">decrby [key] [length]</span><br><span class="line"><span class="comment"># 获取区间的值</span></span><br><span class="line">getrange [key] [index1] [index2]</span><br><span class="line"><span class="comment"># 替换值,从第几个开始替换值</span></span><br><span class="line">setrange [key] [index] [value]</span><br><span class="line"><span class="comment"># 给给定键设置过期时间</span></span><br><span class="line">expire [key] [秒]</span><br><span class="line"><span class="comment"># 创建一个键值对并设置过期时间</span></span><br><span class="line">setex [key] [秒] [value]</span><br><span class="line"><span class="comment"># 查看是否过期，负数表示已过期的时间</span></span><br><span class="line">ttl [key]</span><br><span class="line"><span class="comment">#  如果不存在该键就设置键值对</span></span><br><span class="line">setnx [key] [value]</span><br><span class="line"><span class="comment"># 批量创建键值对</span></span><br><span class="line">mset [key1] [value1] [key2] [value3] ...</span><br><span class="line"><span class="comment"># 批量获取值</span></span><br><span class="line">mget [key1] [key2] ...</span><br><span class="line"><span class="comment"># 批量创建键值对,原子操作，若是有一个键已经存在，那就全部都会创建失败</span></span><br><span class="line">msetnx [key1] [value1] [key2] [value3] ...</span><br><span class="line"><span class="comment"># 先获取键值对在创建键值对</span></span><br><span class="line">getset [key] [value]</span><br></pre></td></tr></table></figure><h2 id="List-列表"><a href="#List-列表" class="headerlink" title="List(列表)"></a>List(列表)</h2><div class="note info flat"><p>List一般用来发布与订阅或者说消息队列、慢查询等场景。</p></div><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># 值为类似一个双向的链表</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># lpush表示从头部增加值，rpush从韦部增加值</span></span><br><span class="line">lpush [key] [value]</span><br><span class="line">rpush [key] [value]</span><br><span class="line"><span class="comment"># 获取列表中的值,从index1到index2区间获取值</span></span><br><span class="line">lrange [key] [index1] [index2]</span><br><span class="line">lrange [key] 0 -1 <span class="comment"># 表示查询列表所有值</span></span><br><span class="line"><span class="comment"># lpop移除第一个值，rpop移除最后一个值</span></span><br><span class="line">lpop [key]</span><br><span class="line">rpop [key]</span><br><span class="line"><span class="comment"># 按下标获取值</span></span><br><span class="line">lindex [key] [index]</span><br><span class="line"><span class="comment"># 查询键的长度，即有多少个值</span></span><br><span class="line">llen [key]</span><br><span class="line"><span class="comment"># 移除指定的值</span></span><br><span class="line">lrem [key] [个数] [value]</span><br><span class="line"><span class="comment"># 截取列表,即修改列表中的值</span></span><br><span class="line">ltrim [key] [index1] [index2]</span><br><span class="line"><span class="comment"># 移除列表最后一个值，移动到另一个列表中</span></span><br><span class="line">rpoplpush [key1] [value] [key2]</span><br><span class="line"><span class="comment"># 替换列表中指定的值</span></span><br><span class="line">lset [key] [index] [value]</span><br><span class="line"><span class="comment"># 判断列表是否存在</span></span><br><span class="line">exists [key]</span><br><span class="line"><span class="comment"># 插入值</span></span><br><span class="line"><span class="comment"># 在列表的value1值的前面插入一个value2的值</span></span><br><span class="line">linsert [key] before [value1] [value2]</span><br><span class="line"><span class="comment"># 在列表的value1值的后面插入一个value2的值</span></span><br><span class="line">linsert [key] after [value1] [value2]</span><br></pre></td></tr></table></figure><h2 id="Set-集合"><a href="#Set-集合" class="headerlink" title="Set(集合)"></a>Set(集合)</h2><div class="note info flat"><p>Set的使用场景一般为，需要存放的数据不能重复以及需要获取多个数据源交集和并集等场景</p></div><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># set不能重复添加相同的元素，类似于Java的HashSet，是一种无序集合</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># 往集合中添加值</span></span><br><span class="line">sadd [key] [value]</span><br><span class="line"><span class="comment"># 查看集合的所有值</span></span><br><span class="line">smembers [key]</span><br><span class="line"><span class="comment"># 判断集合是否存在该元素</span></span><br><span class="line">sismember [key] [value]</span><br><span class="line"><span class="comment"># 查看集合中元素的个数</span></span><br><span class="line">scard [key]</span><br><span class="line"><span class="comment"># 移除指定元素</span></span><br><span class="line">srem [key] [value]</span><br><span class="line"><span class="comment"># 随机抽选元素</span></span><br><span class="line">srandmember [key] <span class="comment"># 默认一个</span></span><br><span class="line">srandmember [key] [length]</span><br><span class="line"><span class="comment"># 随机移除一个元素</span></span><br><span class="line">spop [key]</span><br><span class="line"><span class="comment"># 将一个元素移动到另一个集合</span></span><br><span class="line">smove [key1] [value] [key2]</span><br><span class="line"><span class="comment"># 差集</span></span><br><span class="line">sdiff [key1] [key2]</span><br><span class="line"><span class="comment"># 交集</span></span><br><span class="line">sinter [key1] [key2]</span><br><span class="line"><span class="comment"># 并集</span></span><br><span class="line">sunion [key1] [key2]</span><br></pre></td></tr></table></figure><h2 id="Hash-哈希"><a href="#Hash-哈希" class="headerlink" title="Hash(哈希)"></a>Hash(哈希)</h2><div class="note info flat"><p>Hash一般是用来进行对象的存储</p></div><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># hash的结构类似于Java8时候的HashMap</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># 设置一个键值对</span></span><br><span class="line">hset [key] [key1] [value1]</span><br><span class="line"><span class="comment"># 获取值</span></span><br><span class="line">hget [key] [key1]</span><br><span class="line"><span class="comment"># 批量设置键值对</span></span><br><span class="line">hmset [key] [key1] [value1] [key2] [value2]...</span><br><span class="line"><span class="comment"># 批量获取值</span></span><br><span class="line">hmget [key] [key1] [key2]...</span><br><span class="line"><span class="comment"># 获取全部键值对</span></span><br><span class="line">hgetall [key]</span><br><span class="line"><span class="comment"># 删除指定键值对</span></span><br><span class="line">hdel [key] [key1]</span><br><span class="line"><span class="comment"># 查看键值对的个数</span></span><br><span class="line">hlen [key]</span><br><span class="line"><span class="comment"># 判断一个键值对是否存在</span></span><br><span class="line">hexists [key] [key1]</span><br><span class="line"><span class="comment"># 获取所有的键</span></span><br><span class="line">hkeys [key]</span><br><span class="line"><span class="comment"># 获取所有的值</span></span><br><span class="line">hvals [key]</span><br><span class="line"><span class="comment"># 不存在可以创建</span></span><br><span class="line">hsetnx [key] [key1] [value1]</span><br></pre></td></tr></table></figure><h2 id="Zset-有序的集合"><a href="#Zset-有序的集合" class="headerlink" title="Zset(有序的集合)"></a>Zset(有序的集合)</h2><div class="note info flat"><p>Zset一般使用在那些需要排序的场景。比如礼物排行榜，弹幕消息等。</p></div><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># 在Set的基础上增加了权重参数，从而可以通过权重来进行排序</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># 在指定位置增加值</span></span><br><span class="line">zadd [key] [index] [value]</span><br><span class="line">zadd [key] [index1] [value1] [index2] [value2]..</span><br><span class="line"><span class="comment"># 有顺序地显示区间内的值，可以说是增序排序 </span></span><br><span class="line">zrangebyscore [key] [min] [max]</span><br><span class="line">zrangebyscore [key] -inf +inf <span class="comment"># 表示按增序顺序显示所有的值</span></span><br><span class="line"><span class="comment"># 有顺序地显示区间内的值，可以说是降序排序</span></span><br><span class="line">zrevrange [key] 0 -1<span class="comment"># 表示降序顺序显示所有的值</span></span><br><span class="line"><span class="comment"># 获取有序集合的个数</span></span><br><span class="line">zcrad [key]</span><br><span class="line"><span class="comment"># 获取指定区间的元素的个数</span></span><br><span class="line">zount [key] [index1] [index2]</span><br></pre></td></tr></table></figure><h2 id="geospatial-地理位置"><a href="#geospatial-地理位置" class="headerlink" title="geospatial(地理位置)"></a>geospatial(地理位置)</h2><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># 用来表示地理位置，经纬度，实现底层就是Zset，所以一些命令也适用</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># 添加一个地理位置</span></span><br><span class="line">geoadd [key] [经度] [纬度] [value]</span><br><span class="line"><span class="comment"># 获取指定值的经纬度</span></span><br><span class="line">geopos [key] [value]</span><br><span class="line"><span class="comment"># 获取两个位置之间的距离</span></span><br><span class="line">geodist [key] [value1] [value2] [单位]</span><br><span class="line"><span class="comment"># 指定某个位置和某个半径，返回半径内的值</span></span><br><span class="line">georadius [key] [经度] [纬度] [半径] [单位]</span><br><span class="line">georadius [key] [经度] [纬度] [半径] [单位] withdist<span class="comment"># 还会显示到中心位置的距离</span></span><br><span class="line">georadius [key] [经度] [纬度] [半径] [单位] withcoord<span class="comment"># 会显示值当前的经纬度</span></span><br><span class="line"><span class="comment"># 指定一个元素和某个半径，返回离这个元素位置半径内的值</span></span><br><span class="line">georadiusbymember [key] [value] [半径] [单位]</span><br><span class="line"><span class="comment"># 将指定位置经纬度转换为字符串</span></span><br><span class="line">geohash [key] [value]</span><br></pre></td></tr></table></figure><h2 id="Hyperloglog-基数统计"><a href="#Hyperloglog-基数统计" class="headerlink" title="Hyperloglog(基数统计)"></a>Hyperloglog(基数统计)</h2><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># 基数，表示不重复元素的个数，存在误差</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># 添加键值对,值为一个集合</span></span><br><span class="line">pfadd [key] [value1] [value2] [value3]...</span><br><span class="line"><span class="comment"># 合并集合,不会重复</span></span><br><span class="line">pfmerge [key] [key1] [key2]</span><br><span class="line"><span class="comment"># 统计基数数量</span></span><br><span class="line">pfcount [key]</span><br></pre></td></tr></table></figure><h2 id="Bitmaps-位图"><a href="#Bitmaps-位图" class="headerlink" title="Bitmaps(位图)"></a>Bitmaps(位图)</h2><div class="note info flat"><p>一般用来用户签到，统计活跃的用户和用户在线情况。</p></div><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># 使用二进制来表示状态，用于那些只有两个状态</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># 添加键值对</span></span><br><span class="line">setbit [key] [index] [1或0]</span><br><span class="line"><span class="comment"># 获取值的状态</span></span><br><span class="line">getbit [key] [index]</span><br><span class="line"><span class="comment"># 统计1的个数</span></span><br><span class="line">bitcount sign</span><br></pre></td></tr></table></figure><h2 id="总结"><a href="#总结" class="headerlink" title="总结"></a>总结</h2><p>对Redis的数据类型进行一个小的总结。</p><p>Redis的数据类型其实大概分为两类。</p><ul><li><p>五大数据类型</p><ol><li>String</li><li>Set</li><li>Hash</li><li>List</li><li>Zset</li></ol></li><li><p>三个特殊的数据类型</p><ol><li>geospatial</li><li>hyperloglog</li><li>bitmaps</li></ol></li></ul><h1 id="事务"><a href="#事务" class="headerlink" title="事务"></a>事务</h1><ul><li>Redis的事务其实就是一组命令的队列</li><li>Redis事务没有隔离级别的概念</li><li>一次性，顺序性，排他性</li><li>Redis的单条命令存在原子性，但是事务是多条命令的集合，所以事务不具备原子性</li><li><p>Redis的事务分为三个阶段</p><ol><li>开启事务</li><li>命令入队</li><li>执行事务</li></ol></li><li><p>在开启事务后，输入的命令会进入队列，但是没有直接执行，要等所有命令入队后，发出执行命令才会一次性，依次执行</p></li></ul><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><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">multi</span><br><span class="line"></span><br><span class="line"><span class="comment"># 输入multi命令出现ok，表示开启事务，之后输入命令，命令会入队</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># 执行队列中的命令</span></span><br><span class="line"><span class="built_in">exec</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># 要是想放弃执行事务，可以直接放弃事务</span></span><br><span class="line">discard</span><br><span class="line"></span><br></pre></td></tr></table></figure><div class="note warning flat"><ol><li>若是事务中存在错误的命令，那么整个事务的命令都不会被执行</li><li>若是事务中只是命令语法有问题，那么该命令会抛出异常，但是其他命令会正常执行</li></ol></div><h1 id="监视"><a href="#监视" class="headerlink" title="监视"></a>监视</h1><p>首先了解悲观锁和乐观锁这两个概念。</p><ul><li><p>悲观锁</p><p>悲观锁，顾名思义，什么时候都很悲观，每次访问公共数据，都会觉得别人会进行修改，所以每次获取该数据的时候，都会对数据加锁</p></li><li><p>乐观锁</p><p>乐观锁，顾名思义，什么时候都很乐观，每次访问公共数据，都觉得别人不会进行修改，所以每次都不会去上锁，只有在更新该数据后会判断一下使用期间其他线程有没有修改该数据。可以使用版本号来实现。每次更新数据后，判断该数据的版本号是否发生变更，发生了变更，就会导致修改失效，若是没有就会修改成功并修改版本号。</p></li></ul><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># Redis的监控就是使用了乐观锁的操作</span></span><br><span class="line">watch [key]</span><br><span class="line">multi ... <span class="built_in">exec</span></span><br><span class="line"><span class="comment"># 使用watch来监视键值，一组事务执行后查看数据是否变更</span></span><br><span class="line"><span class="comment"># 若是已经变更，该事务就会失效，即执行失败</span></span><br><span class="line"><span class="comment"># 失败后就需要放弃监视,然后重新监视，执行事务，保证上锁的值是最新的值</span></span><br><span class="line">unwatch [key]</span><br><span class="line">watch [key]</span><br></pre></td></tr></table></figure><h1 id="持久化"><a href="#持久化" class="headerlink" title="持久化"></a>持久化</h1><p>存储在内存中的数据，一旦机器出现问题，那么内存中的数据就会丢失，所以为了尽量避免这个问题，Redis提供了两种持久化的方法，接下来来了解一下这两种持久化的方式。</p><h2 id="RDB"><a href="#RDB" class="headerlink" title="RDB"></a>RDB</h2><p>RDB持久化机制是将某个时刻的数据快照写入磁盘，也就是将某个时刻的数据保存下来，等到Redis服务启动，就会自动加载这个快照文件进行数据恢复。</p><h3 id="手动触发"><a href="#手动触发" class="headerlink" title="手动触发"></a>手动触发</h3><ul><li><p>save命令（不建议使用）</p><p>save是一个同步的命令，也就是说执行save命令，会让Redis的服务器发生阻塞，直到RDB持久化完成，其他命令才能正常进行。</p><p><img src="https://img.yww52.com/2021/2/2021-2-15/img4.png" alt=""></p></li><li><p>bgsave命令</p><p>bgsave是一个异步的命令，执行bgsave命令，Redis会<code>fork</code>一个子进程来进行RDB持久化，只有fork时才会阻塞，其他时间Redis正常运行。</p><p><img src="https://img.yww52.com/2021/2/2021-2-15/img5.png" alt=""></p></li></ul><h3 id="自动触发"><a href="#自动触发" class="headerlink" title="自动触发"></a>自动触发</h3><p><img src="https://img.yww52.com/2021/2/2021-2-15/img6.png" alt=""></p><ul><li><p>满足配置文件中save配置的文件，默认配置如下，可以自己配置</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># 当900秒内至少有1个键值对发生变动，触发持久化</span></span><br><span class="line">save 900 1</span><br><span class="line"><span class="comment"># 当300秒内至少有10个键值对发生变动，触发持久化</span></span><br><span class="line">save 300 10</span><br><span class="line"><span class="comment"># 当60秒内至少有10000个键值对发生变动，触发持久化</span></span><br><span class="line">save 60 10000</span><br></pre></td></tr></table></figure></li><li><p>执行<code>flushall</code>命令清空数据库时，触发持久化</p></li><li><p>执行<code>shutdown</code>命令等手段退出Redis时，触发持久化</p></li></ul><h3 id="RDB文件"><a href="#RDB文件" class="headerlink" title="RDB文件"></a>RDB文件</h3><p>从配置文件可以看到配置文件的默认路径和默认名。</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></pre></td><td class="code"><pre><span class="line"><span class="comment"># rdb文件的默认文件名</span></span><br><span class="line">dbfilename dump.rdb</span><br><span class="line"></span><br><span class="line"><span class="comment"># rdb文件保存的目录</span></span><br><span class="line"><span class="built_in">dir</span> ./</span><br></pre></td></tr></table></figure><h2 id="AOF"><a href="#AOF" class="headerlink" title="AOF"></a>AOF</h2><p>AOF持久化全称<code>Append Only File</code>，当我们执行的改变数据的操作时就会将该命令追加到一个AOF文件的末尾，当Redis服务重新启动的时候，就会重新执行AOF文件内的命令，用来同步数据。</p><div class="note info flat"><p>AOF不是默认的持久化方式，故默认关闭的，需要去配置文件手动开启。</p></div><p><img src="https://img.yww52.com/2021/2/2021-2-15/img7.png" alt=""></p><h3 id="AOF触发策略"><a href="#AOF触发策略" class="headerlink" title="AOF触发策略"></a>AOF触发策略</h3><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># appendfsync always</span></span><br><span class="line">appendfsync everysec</span><br><span class="line"><span class="comment"># appendfsync no</span></span><br></pre></td></tr></table></figure><ul><li><p>always</p><p>每次有新的修改数据的命令，就会将缓冲区内的命令同步追加到AOF文件，十分安全，但是效率低</p></li><li><p>everysec</p><p>默认的策略，每秒将缓冲区内的命令同步追加到AOF文件，但是无法做到实时持久化，还是会可能丢失一秒的数据</p></li><li><p>no</p><p>交给操作系统来决定什么时候去同步追加数据</p></li></ul><h3 id="重写机制"><a href="#重写机制" class="headerlink" title="重写机制"></a>重写机制</h3><p>当命令不断被追加到AOF文件内，文件会越来越大，这对使用来说很不好，所以Redis提供了一个AOF的重写机制来解决这个问题，将AOF文件内的命令优化，重写为可以恢复到当前数据的最小指令集，从而减少文件的大小，达到压缩AOF文件的目的。</p><p>触发流程如下。</p><p><img src="https://img.yww52.com/2021/2/2021-2-15/img8.png" alt=""></p><h3 id="手动触发-1"><a href="#手动触发-1" class="headerlink" title="手动触发"></a>手动触发</h3><p>手动输入<code>bgrewriteaof</code>命令触发重写机制。</p><h3 id="自动触发-1"><a href="#自动触发-1" class="headerlink" title="自动触发"></a>自动触发</h3><p>自动触发就需要自行修改配置文件内AOF重写的配置。</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></pre></td><td class="code"><pre><span class="line"><span class="comment"># 重写触发条件</span></span><br><span class="line"><span class="comment"># 当AOF文件大于64mb而且比上一次重写的文件体积大了至少一倍，就会AOF重写</span></span><br><span class="line">auto-aof-rewrite-percentage 100</span><br><span class="line">auto-aof-rewrite-min-size 64mb</span><br></pre></td></tr></table></figure><h2 id="持久化流程"><a href="#持久化流程" class="headerlink" title="持久化流程"></a>持久化流程</h2><p><img src="https://img.yww52.com/2021/2/2021-2-15/img9.png" alt=""></p><h1 id="过期Key"><a href="#过期Key" class="headerlink" title="过期Key"></a>过期Key</h1><h2 id="为什么要设置过期Key"><a href="#为什么要设置过期Key" class="headerlink" title="为什么要设置过期Key"></a>为什么要设置过期Key</h2><p>之前有提到过设置过期时间，那么就会有一个问题，为什么我们要多此一举去设置一个过期时间呢？</p><p>简而言之那就是，内存有限。</p><p>如果所有的Key都存在内存，数据量一大就会内存溢出了。</p><p>还有一个原因是用户希望存一个有过期时间的Key，就比如在一些场景下，过期Key就会显示很方便。Token的状态登陆或者是验证码的使用，都可以在Redis存储一个过期时间从而达到过期了就无法使用的目的。</p><p>不适用过期时间又想要实现这个功能，那就只能查询数据库获得记录，然后在判断是否过期，那也会有点麻烦。</p><h2 id="如何判断Key过期"><a href="#如何判断Key过期" class="headerlink" title="如何判断Key过期"></a>如何判断Key过期</h2><p>Redis 通过一个叫做过期字典（可以看作是hash表）来保存数据过期的时间。过期字典的键指向Redis数据库中的某个key(键)，过期字典的值是一个<code>long long</code>类型的整数，这个整数保存了key所指向的数据库键的过期时间（毫秒精度的UNIX时间戳）。</p><h2 id="过期Key的删除策略"><a href="#过期Key的删除策略" class="headerlink" title="过期Key的删除策略"></a>过期Key的删除策略</h2><ol><li><p>定期删除</p><p>每隔一段时间就会抽取一批Key来进行过期检查，过期了就进行删除。</p></li><li><p>惰性删除</p><p>每次从数据库获取Key时会判断该Key是否过期，要是过期了就会进行删除，没有过期就会返回数据给用户。</p></li><li><p>定时删除</p><p>在设置key的过期时间的同时，为该key创建一个定时器，让定时器在key的过期时间来临时，对key进行删除。</p></li></ol><p>Redis默认使用的就是定期删除和惰性删除的配合。</p><h1 id="Redis的内存淘汰策略"><a href="#Redis的内存淘汰策略" class="headerlink" title="Redis的内存淘汰策略"></a>Redis的内存淘汰策略</h1><p>刚才有提到过过期Key，从Redis的过期Key删除策略中可以看出这些删除策略并不能将每一个过期的Key都删掉，所以在数据量大的时候又加上过期的Key，就会出现内存满了的情况，那这种时候就需要淘汰掉一些内存数据了。</p><p>Redis提供有8种淘汰的策略。</p><ol><li><strong>volatile-lru</strong>：淘汰掉设置过期时间的数据集里最近最少使用的数据</li><li><strong>volatile-ttl</strong>：淘汰掉设置过期时间的数据集里的将要过期的数据</li><li><strong>volatile-random</strong>：随机选择设置过期时间的数据集里的数据来进行淘汰</li><li><strong>volatile-lfu</strong>：淘汰掉设置过期时间的数据集里最不经常使用的数据</li><li><strong>allkeys-lru</strong>：移除空间里最近最少使用的 key（这个是最常用的）</li><li><strong>allkeys-random</strong>：从空间中任意选择数据来进行淘汰</li><li><strong>allkeys-lfu</strong>：移除空间里最不经常使用的数据</li><li><strong>no-eviction</strong>：不允许淘汰数据，当内存满了之后在进行新数据的写入就会报错</li></ol><h1 id="发布订阅"><a href="#发布订阅" class="headerlink" title="发布订阅"></a>发布订阅</h1><p>发布订阅是一种消息通信模式。</p><p>发送者发送消息，订阅者接受发送者的消息。</p><p>接下来进行一个简单的测试。</p><ol><li><p>订阅一个频道，这里订阅<code>yww</code>这个频道</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></pre></td><td class="code"><pre><span class="line">127.0.0.1:6379&gt; subscribe yww</span><br><span class="line">Reading messages... (press Ctrl-C to quit)</span><br><span class="line">1) <span class="string">&quot;subscribe&quot;</span></span><br><span class="line">2) <span class="string">&quot;yww&quot;</span></span><br><span class="line">3) (<span class="built_in">integer</span>) 1</span><br></pre></td></tr></table></figure></li><li><p>在打开一个Redis的客户端，然后往<code>yww</code>频道发送两条消息</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></pre></td><td class="code"><pre><span class="line">127.0.0.1:6379&gt; publish yww <span class="string">&quot;Hello Redis&quot;</span></span><br><span class="line">(<span class="built_in">integer</span>) 1</span><br><span class="line">127.0.0.1:6379&gt; publish yww <span class="string">&quot;Hello World&quot;</span></span><br><span class="line">(<span class="built_in">integer</span>) 1</span><br><span class="line">127.0.0.1:6379&gt; </span><br></pre></td></tr></table></figure></li><li><p>在订阅频道的订阅者的客户端就能接收到频道的消息了</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line">127.0.0.1:6379&gt; subscribe yww</span><br><span class="line">Reading messages... (press Ctrl-C to quit)</span><br><span class="line">1) <span class="string">&quot;subscribe&quot;</span></span><br><span class="line">2) <span class="string">&quot;yww&quot;</span></span><br><span class="line">3) (<span class="built_in">integer</span>) 1</span><br><span class="line">1) <span class="string">&quot;message&quot;</span></span><br><span class="line">2) <span class="string">&quot;yww&quot;</span></span><br><span class="line">3) <span class="string">&quot;Hello Redis&quot;</span></span><br><span class="line">1) <span class="string">&quot;message&quot;</span></span><br><span class="line">2) <span class="string">&quot;yww&quot;</span></span><br><span class="line">3) <span class="string">&quot;Hello World&quot;</span></span><br></pre></td></tr></table></figure></li></ol><p>以下是一些常用的API</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># 订阅一个或多个符和给定模式的频道</span></span><br><span class="line">psubscribe pattern[pattern...]</span><br><span class="line"></span><br><span class="line"><span class="comment"># 退订一个或多个给定模式的频道</span></span><br><span class="line">punsubscribe [pattern[pattern]]</span><br><span class="line"></span><br><span class="line"><span class="comment"># 查看订阅与发布系统状态</span></span><br><span class="line">pubsub subcommand [argument[argument...]]</span><br><span class="line"></span><br><span class="line"><span class="comment"># 将消息发送到指定的频道</span></span><br><span class="line">publish channel message</span><br><span class="line"></span><br><span class="line"><span class="comment"># 订阅一个或多个频道的信息</span></span><br><span class="line">subscribe channel[channel ...]</span><br><span class="line"></span><br><span class="line"><span class="comment"># 退订一个或多个频道的信息</span></span><br><span class="line">unsubscribe [channel[channel...]]</span><br></pre></td></tr></table></figure><h1 id="主从复制，读写分离"><a href="#主从复制，读写分离" class="headerlink" title="主从复制，读写分离"></a>主从复制，读写分离</h1><p>当数据量过大的时候，服务器的压力就会提高，为了解决这个问题，Redis提供了<code>主从复制，读写分离</code>的方案，因为大部分的压力是读操作，所以可以搭建一个Redis集群（最低要求三台，一主二从)，主节点负责写操作，从节点用来提供读的服务，这样就能减少服务器的压力了。</p><p><img src="https://img.yww52.com/2021/2/2021-2-15/img10.png" alt=""></p><p>当然还可以有以下这种情况</p><p><img src="https://img.yww52.com/2021/2/2021-2-15/img11.png" alt=""></p><p>节点是既可以当主节点，又可以当子节点的。</p><p>Redis的主从复制表示只有主节点能进行写操作，从节点是不能进行写操作的，只能进行读操作，主节点的数据会同步到子节点，达到整个集群的数据一致。</p><h2 id="同步机制"><a href="#同步机制" class="headerlink" title="同步机制"></a>同步机制</h2><p>上边说到的同步方式根据是否是全量来分为<code>全量同步</code>和<code>增量同步</code>。</p><ul><li><p>全量同步</p><p>全量同步的数据复制一般只会发生在<code>slave从节点</code>连接主节点的时候，这时从节点会将主节点中的所有数据都复制到从节点中，从而从节点连接主节点后数据通同步。</p><p>期间发生的具体步骤如下。</p><ol><li>从节点连接主节点后，向主节点发出<code>SYNC</code>命令</li><li>主节点收到<code>SYNC</code>命令后，开始执行<code>BGSAVE</code>生成RDB文件，因为是异步的操作，所以主节点继续处理命令，并将被执行的命令放入缓冲区</li><li>生成RDB文件后，就会向从节点发送快照文件</li><li>从节点收到主节点发送的RDB文件后，就会放弃原来存在的旧数据，然后载入RDB文件同步数据</li><li>触发增量同步</li></ol><p>这样主节点的数据旧全量同步复制到了从节点上。</p></li><li><p>增量同步</p><p>除了首次从节点的全量同步，一般数据的同步都是使用增量同步的方式</p><ol><li>主节点将缓冲区中的命令发送给从节点</li><li>从节点接收到命令请求，就会执行这些命令，完成部分写命令带来的数据同步</li></ol></li></ul><h2 id="模拟Redis集群"><a href="#模拟Redis集群" class="headerlink" title="模拟Redis集群"></a>模拟Redis集群</h2><p>这里使用同一个服务器，不同端口搭建的Redis集群进行学习。</p><p>当然你有多台服务器可以使用不同服务器来搭建集群来学习。</p><p>再不然可以直接开三个容器来搭建Redis集群来学习。</p><p>首先是了解一个基本的命令。</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># 查看当前服务的信息</span></span><br><span class="line">info replication</span><br><span class="line"></span><br><span class="line">127.0.0.1:6379&gt; info replication</span><br><span class="line"><span class="comment"># Replication</span></span><br><span class="line">role:master<span class="comment"># 角色为主节点，没有配置，默认每个服务都是主机</span></span><br><span class="line">connected_slaves:0<span class="comment"># 连接从节点的个数</span></span><br><span class="line">master_replid:fb2dce24da3d78a04f01a6f55abdd48e1751dda0</span><br><span class="line">master_replid2:0000000000000000000000000000000000000000</span><br><span class="line">master_repl_offset:0</span><br><span class="line">second_repl_offset:-1</span><br><span class="line">repl_backlog_active:0</span><br><span class="line">repl_backlog_size:1048576</span><br><span class="line">repl_backlog_first_byte_offset:0</span><br><span class="line">repl_backlog_histlen:0</span><br><span class="line">127.0.0.1:6379&gt; </span><br></pre></td></tr></table></figure><p>创建三个配置文件，用来开启三个服务。</p><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">redis6379.conf  redis6380.conf  redis6381.conf</span><br></pre></td></tr></table></figure><p>然后配置这三个文件。</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># redis6379.conf主节点的配置</span></span><br><span class="line">logfile <span class="string">&quot;6379log.log&quot;</span></span><br><span class="line">dbfilename dump6379.rdb</span><br><span class="line"></span><br><span class="line"><span class="comment"># redis6380.conf</span></span><br><span class="line">port 6380</span><br><span class="line">pidfile /var/run/redis_6380.pid</span><br><span class="line">logfile <span class="string">&quot;6380log.log&quot;</span></span><br><span class="line">dbfilename dump80.rdb</span><br><span class="line"></span><br><span class="line"><span class="comment"># redis6381.conf</span></span><br><span class="line">port 6381</span><br><span class="line">pidfile /var/run/redis_6381.pid</span><br><span class="line">logfile <span class="string">&quot;6381log.log</span></span><br><span class="line"><span class="string">dbfilename dump6381.rdb</span></span><br><span class="line"><span class="string"></span></span><br><span class="line"><span class="string"># 开启redis集群服务</span></span><br><span class="line"><span class="string"># redis-server ./myconf/redis6379.conf </span></span><br><span class="line"><span class="string"># redis-server ./myconf/redis6380.conf </span></span><br><span class="line"><span class="string"># redis-server ./myconf/redis6381.conf </span></span><br><span class="line"><span class="string"># ps -ef |grep redis</span></span><br><span class="line"><span class="string">root     19536 19481  0 20:19 pts/1    00:00:00 redis-cli</span></span><br><span class="line"><span class="string">root     32040     1  0 22:30 ?        00:00:00 redis-server 127.0.0.1:6379</span></span><br><span class="line"><span class="string">root     32052     1  0 22:30 ?        00:00:00 redis-server 127.0.0.1:6380</span></span><br><span class="line"><span class="string">root     32062     1  0 22:30 ?        00:00:00 redis-server 127.0.0.1:6381</span></span><br><span class="line"><span class="string">root     32106 30264  0 22:30 pts/2    00:00:00 grep --color=auto redis</span></span><br><span class="line"><span class="string"></span></span><br><span class="line"><span class="string"># 开启三个客户端，这里使用主节点的客户端演示，子节点客户端另开窗口</span></span><br><span class="line"><span class="string"># redis-cli -p 6379</span></span><br></pre></td></tr></table></figure><p>然后是建立主从联系，这里使用6379的服务当成主节点，建立联系有两种方法。</p><ul><li><p>在从节点中使用命令连接主机，这是简单的连接，要是从节点服务关闭了，之后重启旧连接不到主节点了，可以说是一次性的</p><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">SLAVEOF [host] [port]</span><br></pre></td></tr></table></figure></li><li><p>使用配置设置，这种情况，从节点服务重启后就会继续连接主机</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></pre></td><td class="code"><pre><span class="line"><span class="comment">################################# REPLICATION #################################</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># 在这下面配置主节点服务地址就好了，样例已经给出</span></span><br><span class="line"><span class="comment"># replicaof &lt;masterip&gt; &lt;masterport&gt;</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># 要是主节点服务有密码可以在这里配置</span></span><br><span class="line"><span class="comment"># masterauth &lt;master-password&gt;</span></span><br></pre></td></tr></table></figure></li></ul><p>这里连接6379当成主节点。</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># 配置6380从节点</span></span><br><span class="line">127.0.0.1:6380&gt; SLAVEOF 127.0.0.1 6379</span><br><span class="line">OK</span><br><span class="line">127.0.0.1:6380&gt; info replication</span><br><span class="line"><span class="comment"># Replication</span></span><br><span class="line">role:slave</span><br><span class="line">master_host:127.0.0.1</span><br><span class="line">master_port:6379</span><br><span class="line">master_link_status:up</span><br><span class="line">master_last_io_seconds_ago:3</span><br><span class="line">master_sync_in_progress:0</span><br><span class="line">slave_repl_offset:28</span><br><span class="line">slave_priority:100</span><br><span class="line">slave_read_only:1</span><br><span class="line">connected_slaves:0</span><br><span class="line">master_replid:8ed59f23d41ec553e33eae6b5fb13cfd1d807e80</span><br><span class="line">master_replid2:0000000000000000000000000000000000000000</span><br><span class="line">master_repl_offset:28</span><br><span class="line">second_repl_offset:-1</span><br><span class="line">repl_backlog_active:1</span><br><span class="line">repl_backlog_size:1048576</span><br><span class="line">repl_backlog_first_byte_offset:1</span><br><span class="line">repl_backlog_histlen:28</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="comment"># 配置6381从节点</span></span><br><span class="line">127.0.0.1:6381&gt; SLAVEOF 127.0.0.1 6379</span><br><span class="line">OK</span><br><span class="line">127.0.0.1:6381&gt; info replication</span><br><span class="line"><span class="comment"># Replication</span></span><br><span class="line">role:slave</span><br><span class="line">master_host:127.0.0.1</span><br><span class="line">master_port:6379</span><br><span class="line">master_link_status:up</span><br><span class="line">master_last_io_seconds_ago:9</span><br><span class="line">master_sync_in_progress:0</span><br><span class="line">slave_repl_offset:42</span><br><span class="line">slave_priority:100</span><br><span class="line">slave_read_only:1</span><br><span class="line">connected_slaves:0</span><br><span class="line">master_replid:8ed59f23d41ec553e33eae6b5fb13cfd1d807e80</span><br><span class="line">master_replid2:0000000000000000000000000000000000000000</span><br><span class="line">master_repl_offset:42</span><br><span class="line">second_repl_offset:-1</span><br><span class="line">repl_backlog_active:1</span><br><span class="line">repl_backlog_size:1048576</span><br><span class="line">repl_backlog_first_byte_offset:15</span><br><span class="line">repl_backlog_histlen:28 </span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="comment"># 查看6379主节点信息</span></span><br><span class="line">127.0.0.1:6379&gt; info replication</span><br><span class="line"><span class="comment"># Replication</span></span><br><span class="line">role:master</span><br><span class="line">connected_slaves:2</span><br><span class="line">slave0:ip=127.0.0.1,port=6380,state=online,offset=70,lag=1</span><br><span class="line">slave1:ip=127.0.0.1,port=6381,state=online,offset=70,lag=1</span><br><span class="line">master_replid:8ed59f23d41ec553e33eae6b5fb13cfd1d807e80</span><br><span class="line">master_replid2:0000000000000000000000000000000000000000</span><br><span class="line">master_repl_offset:70</span><br><span class="line">second_repl_offset:-1</span><br><span class="line">repl_backlog_active:1</span><br><span class="line">repl_backlog_size:1048576</span><br><span class="line">repl_backlog_first_byte_offset:1</span><br><span class="line">repl_backlog_histlen:70</span><br><span class="line"></span><br></pre></td></tr></table></figure><p>然后在主机设置的数据，可以在从机中获取数据。</p><h2 id="哨兵模式（Sentinel）"><a href="#哨兵模式（Sentinel）" class="headerlink" title="哨兵模式（Sentinel）"></a>哨兵模式（Sentinel）</h2><p>通过配置连接的子节点出现故障，重启服务后，因为配置连接的缘故那还是子节点，所以子节点出现故障的情况很简单。</p><div class="note info flat"><p>要是这个子节点有子节点，那么主机故障后，子节点的role依旧是salve，所以还是不能进行写操作。</p></div><p>接下来重点了解一下主节点出现故障的情况。</p><p>当主节点出现故障后，它的子节点身份是不会变的，当主节点的服务重新启动后，集群依旧存在。</p><p>当我们不清楚主节点何时恢复，总不能一直不进行写操作，所以就需要重新推出一个主节点。</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></pre></td><td class="code"><pre><span class="line"><span class="comment"># 令从节点恢复master</span></span><br><span class="line">SLAVEOF no one</span><br><span class="line"><span class="comment"># 然后在配置其他节点连接至该节点</span></span><br></pre></td></tr></table></figure><p> 这是手动配置主节点，这其实还是挺麻烦的，所以为了解决这个问题，Redis从2.8的版本后就提供了<code>哨兵模式</code>这个方案。</p><hr><p>哨兵模式其实就是自动推选主节点的一种方案。</p><p>先来简单了解一下哨兵模式。</p><p>哨兵是一个独立的进程，在Redis的命令中也能看到它<code>redis-sentinel</code>。</p><p>该进程通过向各个节点发送命令，通过节点返回的信息来监控节点的使用情况。</p><p><img src="https://img.yww52.com/2021/2/2021-2-15/img12.png" alt=""></p><p>当主节点宕机了，哨兵向主节点发送命令却得不到返回信息，过段时间哨兵确认主节点宕机后，就会随机给从节点随机投票，获得投票的节点就会当选为主节点，然后通过发布订阅模式通知其他的从节点修改配置，让它们切换主节点对象，从而实现集群正常服务。</p><p>那要是哨兵宕机了怎么办呢？这样的设置还是会出现问题。所以哨兵不能只有一台，哨兵也要形成一个集群（最好三个起步），于是最终的解决方案就如下图。</p><p><img src="https://img.yww52.com/2021/2/2021-2-15/img13.png" alt=""></p><p>多个哨兵进行监控，当有一个哨兵检测到主节点宕机，并不会马上切换，因为有可能是哨兵的问题，所以哨兵只会主观认为主节点宕机（这种情况叫主观下线）。</p><p>当一定个数（在哨兵的配置文件可以配置）的哨兵都认为主节点宕机后，那大概率就不是哨兵的问题了，那么哨兵之前就会对剩余的从节点投票，投票结束后，随机一个哨兵进行<code>failover</code>操作（这种情况叫客观下线）。</p><p><code>failover</code>又称故障转移，它会从从节点中挑选一个作为Redis集群中的新的主节点。</p><ol><li>选择票数高的从节点当为主节点，若是不存在（同票的情况），就继续判断。</li><li>选择主从复制，同步数据最完整的节点成为主节点，若是不存在就继续判断。</li><li>选择启动最早的子节点当为新的主节点。</li></ol><p>对选出来的主节点执行<code>slaveof no one</code>将身份转换成主节点，然后向其他的从节点发送订阅模式通知，各个哨兵就会让他们的节点切换主节点的对象为新的主节点。最后更新之前宕掉的节点的身份为从节点，当宕机恢复后，就自动成为该集群的从节点。</p><hr><p>哨兵的启动需要先配置哨兵启动的配置文件<code>sentinel.conf</code>。</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># Example sentinel.conf</span></span><br><span class="line"> </span><br><span class="line"><span class="comment"># 哨兵sentinel运行的端口，默认26379</span></span><br><span class="line">port 26379</span><br><span class="line"> </span><br><span class="line"><span class="comment"># 哨兵sentinel的工作目录</span></span><br><span class="line"><span class="built_in">dir</span> /tmp</span><br><span class="line"> </span><br><span class="line"><span class="comment"># 最主要的一个配置，这个是必定配置的</span></span><br><span class="line"><span class="comment"># master-name为主节点名称,ip为主节点的ip，port主节点Redis服务断开</span></span><br><span class="line"><span class="comment"># 哨兵对主节点主观下线的个数达到quorum个后，就会执行故障转移。(三个节点就用2)</span></span><br><span class="line"><span class="comment"># sentinel monitor &lt;master-name&gt; &lt;ip&gt; &lt;redis-port&gt; &lt;quorum&gt;</span></span><br><span class="line">sentinel monitor mymaster 127.0.0.1 6379 2</span><br><span class="line"> </span><br><span class="line"><span class="comment"># 设置哨兵sentinel 连接主从的密码 注意必须为主从设置一样的验证密码</span></span><br><span class="line"><span class="comment"># sentinel auth-pass &lt;master-name&gt; &lt;password&gt;</span></span><br><span class="line"></span><br><span class="line"> </span><br><span class="line"><span class="comment"># 当哨兵向主节点发送命令后，超过times毫秒没得到回应，该哨兵就会认为主节点宕机，默认30秒</span></span><br><span class="line"><span class="comment"># sentinel down-after-milliseconds &lt;master-name&gt; &lt;milliseconds&gt;</span></span><br><span class="line"></span><br><span class="line"> </span><br><span class="line"><span class="comment"># 这个配置项指定了在发生failover主备切换时最多可以有多少个slave同时对新的master进行同步</span></span><br><span class="line"><span class="comment"># sentinel parallel-syncs &lt;master-name&gt; &lt;numslaves&gt;</span></span><br><span class="line"></span><br><span class="line"> </span><br><span class="line"></span><br><span class="line"><span class="comment"># 故障转移的超时时间 failover-timeout，默认三分钟</span></span><br><span class="line"><span class="comment"># sentinel failover-timeout &lt;master-name&gt; &lt;milliseconds&gt;</span></span><br><span class="line"></span><br><span class="line"> </span><br><span class="line"><span class="comment"># SCRIPTS EXECUTION</span></span><br><span class="line"><span class="comment"># 配置当某一事件发生时所需要执行的脚本，对于脚本的运行结果有以下规则：</span></span><br><span class="line"><span class="comment"># 若脚本执行后返回1，那么该脚本稍后将会被再次执行，重复次数默认为10</span></span><br><span class="line"><span class="comment"># 若脚本执行后返回2，或者比2更高的一个返回值，脚本将不会重复执行。</span></span><br><span class="line"><span class="comment"># 如果脚本在执行过程中由于收到系统中断信号被终止了，则同返回值为1时的行为相同。</span></span><br><span class="line"><span class="comment"># 一个脚本的最大执行时间为60s，如果超过这个时间，脚本将会被一个SIGKILL信号终止，之后重新执行。</span></span><br><span class="line"><span class="comment"># </span></span><br><span class="line"><span class="comment">#</span></span><br><span class="line"><span class="comment"># 通知型脚本，主要用于通知故障的情况</span></span><br><span class="line"><span class="comment"># sentinel notification-script &lt;master-name&gt; &lt;script-path&gt;</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># 客户端重新配置主节点参数脚本</span></span><br><span class="line"><span class="comment"># 当一个master由于failover而发生改变时，这个脚本将会被调用，通知相关的客户端关于master地址已经发生改变的信息。</span></span><br><span class="line"><span class="comment"># 以下参数将会在调用脚本时传给脚本:</span></span><br><span class="line"><span class="comment"># &lt;master-name&gt; &lt;role&gt; &lt;state&gt; &lt;from-ip&gt; &lt;from-port&gt; &lt;to-ip&gt; &lt;to-port&gt;</span></span><br><span class="line"><span class="comment"># 目前&lt;state&gt;总是“failover”,</span></span><br><span class="line"><span class="comment"># &lt;role&gt;是“leader”或者“observer”中的一个。 </span></span><br><span class="line"><span class="comment"># 参数 from-ip, from-port, to-ip, to-port是用来和旧的master和新的master(即旧的slave)通信的</span></span><br><span class="line"><span class="comment"># 这个脚本应该是通用的，能被多次调用，不是针对性的。</span></span><br><span class="line"><span class="comment"># sentinel client-reconfig-script &lt;master-name&gt; &lt;script-path&gt;</span></span><br><span class="line"></span><br></pre></td></tr></table></figure><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># 开启哨兵线程</span></span><br><span class="line">redis-sentinel ./sentinel.conf</span><br></pre></td></tr></table></figure><h1 id="redis-conf"><a href="#redis-conf" class="headerlink" title="redis.conf"></a>redis.conf</h1><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br><span class="line">80</span><br><span class="line">81</span><br><span class="line">82</span><br><span class="line">83</span><br><span class="line">84</span><br><span class="line">85</span><br><span class="line">86</span><br><span class="line">87</span><br><span class="line">88</span><br><span class="line">89</span><br><span class="line">90</span><br><span class="line">91</span><br><span class="line">92</span><br><span class="line">93</span><br><span class="line">94</span><br><span class="line">95</span><br><span class="line">96</span><br><span class="line">97</span><br><span class="line">98</span><br><span class="line">99</span><br><span class="line">100</span><br><span class="line">101</span><br><span class="line">102</span><br><span class="line">103</span><br><span class="line">104</span><br><span class="line">105</span><br><span class="line">106</span><br><span class="line">107</span><br><span class="line">108</span><br><span class="line">109</span><br><span class="line">110</span><br><span class="line">111</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">################################## INCLUDES ###################################</span></span><br><span class="line"><span class="comment"># include /path/to/local.conf</span></span><br><span class="line"><span class="comment"># include /path/to/other.conf</span></span><br><span class="line"><span class="comment"># 包含多个配置文件</span></span><br><span class="line"></span><br><span class="line"><span class="comment">################################## NETWORK #####################################</span></span><br><span class="line"><span class="comment"># 绑定IP</span></span><br><span class="line"><span class="built_in">bind</span> 127.0.0.1</span><br><span class="line"><span class="comment"># 打开安全模式，默认是打开的</span></span><br><span class="line">protected-mode <span class="built_in">yes</span></span><br><span class="line"><span class="comment"># 绑定端口</span></span><br><span class="line">port 6379</span><br><span class="line"></span><br><span class="line"><span class="comment">################################# GENERAL #####################################</span></span><br><span class="line"><span class="comment"># 是否以守护线程方式开启服务，默认为no,建议改为yes</span></span><br><span class="line">daemonize no</span><br><span class="line"><span class="comment"># 如果以后台方式运行，需要指定pid文件</span></span><br><span class="line">pidfile /var/run/redis_6379.pid</span><br><span class="line"></span><br><span class="line"><span class="comment"># 日志形式</span></span><br><span class="line"><span class="comment"># This can be one of:</span></span><br><span class="line"><span class="comment"># debug (a lot of information, useful for development/testing)</span></span><br><span class="line"><span class="comment"># verbose (many rarely useful info, but not a mess like the debug level)</span></span><br><span class="line"><span class="comment"># notice (moderately verbose, what you want in production probably)</span></span><br><span class="line"><span class="comment"># warning (only very important / critical messages are logged)</span></span><br><span class="line">loglevel notice</span><br><span class="line"></span><br><span class="line"><span class="comment"># 指定日志文件路径</span></span><br><span class="line">logfile <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">databases 16</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="comment">################################# REPLICATION #################################</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># 在这下面配置主节点服务地址就好了，样例已经给出</span></span><br><span class="line"><span class="comment"># replicaof &lt;masterip&gt; &lt;masterport&gt;</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># 要是主节点服务有密码可以在这里配置</span></span><br><span class="line"><span class="comment"># masterauth &lt;master-password&gt;</span></span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="comment">################################ SNAPSHOTTING  ################################</span></span><br><span class="line"><span class="comment"># 持久化设置</span></span><br><span class="line"><span class="comment"># save s k</span></span><br><span class="line"><span class="comment"># 表示在s秒内，若是有至少修改了k个键值对，就会进行持久化操作</span></span><br><span class="line">save 900 1</span><br><span class="line">save 300 10</span><br><span class="line">save 60 10000</span><br><span class="line"></span><br><span class="line"><span class="comment"># 持久化出错是否继续工作</span></span><br><span class="line">stop-writes-on-bgsave-error <span class="built_in">yes</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># 是否压缩rdb文件</span></span><br><span class="line">rdbcompression <span class="built_in">yes</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># 对保存的rdb文件时进行校验检测</span></span><br><span class="line">rdbchecksum <span class="built_in">yes</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># rdb文件的默认文件名</span></span><br><span class="line">dbfilename dump.rdb</span><br><span class="line"></span><br><span class="line"><span class="comment"># rdb文件保存的目录</span></span><br><span class="line"><span class="built_in">dir</span> ./</span><br><span class="line"></span><br><span class="line"><span class="comment">################################## SECURITY ###################################</span></span><br><span class="line"><span class="comment"># 设置密码，默认是没有设置该选项的</span></span><br><span class="line"><span class="comment"># requirepass foobared</span></span><br><span class="line"></span><br><span class="line"><span class="comment">################################### CLIENTS ####################################</span></span><br><span class="line"><span class="comment"># 设置最大连接的客户端数</span></span><br><span class="line"><span class="comment"># maxclients 10000</span></span><br><span class="line"></span><br><span class="line"><span class="comment">############################## MEMORY MANAGEMENT ################################</span></span><br><span class="line"><span class="comment"># 设置redis最大的内存容量</span></span><br><span class="line"><span class="comment"># maxmemory &lt;bytes&gt;</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># 内存满了的处理策略</span></span><br><span class="line"><span class="comment"># maxmemory-policy noeviction</span></span><br><span class="line"><span class="comment"># 1. noeviction：当内存使用超过配置的时候会返回错误，不会驱逐任何键</span></span><br><span class="line"><span class="comment"># 2. allkeys-lru：加入键的时候，如果过限，首先通过LRU算法驱逐最久没有使用的键</span></span><br><span class="line"><span class="comment"># 3. volatile-lru：加入键的时候如果过限，首先从设置了过期时间的键集合中驱逐最久没有使用的键</span></span><br><span class="line"><span class="comment"># 4. allkeys-random：加入键的时候如果过限，从所有key随机删除</span></span><br><span class="line"><span class="comment"># 5. volatile-random：加入键的时候如果过限，从过期键的集合中随机驱逐</span></span><br><span class="line"><span class="comment"># 6. volatile-ttl：从配置了过期时间的键中驱逐马上就要过期的键</span></span><br><span class="line"><span class="comment"># 7. volatile-lfu：从所有配置了过期时间的键中驱逐使用频率最少的键</span></span><br><span class="line"><span class="comment"># 8. allkeys-lfu：从所有键中驱逐使用频率最少的键</span></span><br><span class="line"></span><br><span class="line"><span class="comment">############################## APPEND ONLY MODE ###############################</span></span><br><span class="line"><span class="comment"># 是否开启aof模式，默认不开启，使用rdb方式持久化</span></span><br><span class="line">appendonly no</span><br><span class="line"></span><br><span class="line"><span class="comment"># 持久化的文件名字</span></span><br><span class="line">appendfilename <span class="string">&quot;appendonly.aof&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"># appendfsync always</span></span><br><span class="line"><span class="comment"># 每秒进行一次同步</span></span><br><span class="line">appendfsync everysec</span><br><span class="line"><span class="comment"># 不执行同步，交给操作系统自己同步</span></span><br><span class="line"><span class="comment"># appendfsync no</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># 执行重写时同步数据到AOF文件，默认为no，yes表示不同步直接写入新的AOF文件</span></span><br><span class="line">no-appendfsync-on-rewrite no</span><br><span class="line"></span><br><span class="line"><span class="comment"># 重写触发条件</span></span><br><span class="line"><span class="comment"># 当AOF文件大于64mb而且比上一次重写的文件体积大了至少一倍，就会AOF重写</span></span><br><span class="line">auto-aof-rewrite-percentage 100</span><br><span class="line">auto-aof-rewrite-min-size 64mb</span><br></pre></td></tr></table></figure><h1 id="一些参考链接"><a href="#一些参考链接" class="headerlink" title="一些参考链接"></a>一些参考链接</h1><ul><li><a href="https://zhuanlan.zhihu.com/p/105587132">https://zhuanlan.zhihu.com/p/105587132</a></li><li><a href="https://www.bilibili.com/video/BV1S54y1R7SB?p=1">https://www.bilibili.com/video/BV1S54y1R7SB?p=1</a></li><li><a href="https://baijiahao.baidu.com/s?id=1654694618189745916&amp;wfr=spider&amp;for=pc">https://baijiahao.baidu.com/s?id=1654694618189745916&amp;wfr=spider&amp;for=pc</a></li><li><a href="https://blog.csdn.net/weixin_39040059/article/details/79120444">https://blog.csdn.net/weixin_39040059/article/details/79120444</a></li><li><a href="https://www.runoob.com/redis/redis-tutorial.html">https://www.runoob.com/redis/redis-tutorial.html</a></li><li><a href="https://www.cnblogs.com/daofaziran/p/10978628.html">https://www.cnblogs.com/daofaziran/p/10978628.html</a></li><li><a href="https://www.jianshu.com/p/06ab9daf921d">https://www.jianshu.com/p/06ab9daf921d</a></li></ul>]]></content:encoded>
            
            
            <category domain="https://yww52.com/categories/%E6%88%91%E7%9A%84%E6%8A%80%E6%9C%AF%E5%AD%A6%E4%B9%A0%E7%89%A9%E8%AF%AD%E6%9E%9C%E7%84%B6%E6%9C%89%E9%97%AE%E9%A2%98/">我的技术学习物语果然有问题</category>
            
            <category domain="https://yww52.com/categories/%E6%88%91%E7%9A%84%E6%8A%80%E6%9C%AF%E5%AD%A6%E4%B9%A0%E7%89%A9%E8%AF%AD%E6%9E%9C%E7%84%B6%E6%9C%89%E9%97%AE%E9%A2%98/%E5%BC%80%E5%8F%91%E6%8A%80%E8%83%BD/">开发技能</category>
            
            
            
            <comments>https://yww52.com/posts/bae4ff13/#disqus_thread</comments>
            
        </item>
        
        <item>
            <title>常用的工具类</title>
            <link>https://yww52.com/posts/b0ab3ebd/</link>
            <guid>https://yww52.com/posts/b0ab3ebd/</guid>
            <pubDate>Tue, 02 Feb 2021 16:00:00 GMT</pubDate>
            
            <description>记录一下开发中经常使用的工具类。</description>
            
            
            
            <content:encoded><![CDATA[<img src="https://img.yww52.com/2021/2/2021-2-3top_img.jpg" alt="常用的工具类" style="max-width:100%;height:auto;margin-bottom:15px;"
  /><h1 id="前言"><a href="#前言" class="headerlink" title="前言"></a>前言</h1><p> 总结记录一下在开发中经常使用到的一些工具类。</p><h1 id="MD5加密"><a href="#MD5加密" class="headerlink" title="MD5加密"></a>MD5加密</h1><p>用户的密码一般都会进行加密后在存储到数据库当中，所以一般用到MD5这种加密算法。</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><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><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">public</span> <span class="keyword">class</span> <span class="title class_">Md5Utils</span> &#123;</span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">static</span> String <span class="title function_">encrypt</span><span class="params">(String strSrc)</span> &#123;</span><br><span class="line">        <span class="keyword">try</span> &#123;</span><br><span class="line">            <span class="type">char</span> hexChars[] = &#123; <span class="string">&#x27;0&#x27;</span>, <span class="string">&#x27;1&#x27;</span>, <span class="string">&#x27;2&#x27;</span>, <span class="string">&#x27;3&#x27;</span>, <span class="string">&#x27;4&#x27;</span>, <span class="string">&#x27;5&#x27;</span>, <span class="string">&#x27;6&#x27;</span>, <span class="string">&#x27;7&#x27;</span>, <span class="string">&#x27;8&#x27;</span>,</span><br><span class="line">                    <span class="string">&#x27;9&#x27;</span>, <span class="string">&#x27;a&#x27;</span>, <span class="string">&#x27;b&#x27;</span>, <span class="string">&#x27;c&#x27;</span>, <span class="string">&#x27;d&#x27;</span>, <span class="string">&#x27;e&#x27;</span>, <span class="string">&#x27;f&#x27;</span> &#125;;</span><br><span class="line">            <span class="type">byte</span>[] bytes = strSrc.getBytes();</span><br><span class="line">            <span class="type">MessageDigest</span> <span class="variable">md</span> <span class="operator">=</span> MessageDigest.getInstance(<span class="string">&quot;MD5&quot;</span>);</span><br><span class="line">            md.update(bytes);</span><br><span class="line">            bytes = md.digest();</span><br><span class="line">            <span class="type">int</span> <span class="variable">j</span> <span class="operator">=</span> bytes.length;</span><br><span class="line">            <span class="type">char</span>[] chars = <span class="keyword">new</span> <span class="title class_">char</span>[j * <span class="number">2</span>];</span><br><span class="line">            <span class="type">int</span> <span class="variable">k</span> <span class="operator">=</span> <span class="number">0</span>;</span><br><span class="line">            <span class="keyword">for</span> (<span class="type">int</span> <span class="variable">i</span> <span class="operator">=</span> <span class="number">0</span>; i &lt; bytes.length; i++) &#123;</span><br><span class="line">                <span class="type">byte</span> <span class="variable">b</span> <span class="operator">=</span> bytes[i];</span><br><span class="line">                chars[k++] = hexChars[b &gt;&gt;&gt; <span class="number">4</span> &amp; <span class="number">0xf</span>];</span><br><span class="line">                chars[k++] = hexChars[b &amp; <span class="number">0xf</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_">String</span>(chars);</span><br><span class="line">        &#125; <span class="keyword">catch</span> (NoSuchAlgorithmException e) &#123;</span><br><span class="line">            e.printStackTrace();</span><br><span class="line">            <span class="keyword">throw</span> <span class="keyword">new</span> <span class="title class_">RuntimeException</span>(<span class="string">&quot;MD5加密出错+&quot;</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><h1 id="雪花算法"><a href="#雪花算法" class="headerlink" title="雪花算法"></a>雪花算法</h1><p>对于MySQL的主键问题，一般都需要使用自增的主键。但是只是从1自增就会因为分库分表产生主键相同的情况。</p><p>所以这次使用雪花算法生成一个分布式全局唯一ID</p><p>首先要知道雪花算法生成的ID是怎么样的。</p><p>分布式ID一般都是Long类型，所以会有64位。</p><ul><li>第一部分是一个bit位，是一个标识部分，在java中由于long的最高位是符号位，正数是0，负数是1，一般生成的ID为正数，所以固定为0</li><li>第二部分是时间戳，该部分占41bit，这个是毫秒级的时间</li><li>第三部分是工作机器ID，占10bit</li><li>第四部分是序列号，占12bit，支持同一毫秒内同一个节点可以生成4096个ID</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><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></pre></td><td class="code"><pre><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * &lt;p&gt;</span></span><br><span class="line"><span class="comment"> *     雪花算法，分布式全局唯一ID</span></span><br><span class="line"><span class="comment"> * &lt;/p&gt;</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@ClassName</span> SnowFlake</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@Author</span> yww</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@date</span> 2021/4/24 18:39</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_">SnowFlake</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">private</span> <span class="keyword">final</span> <span class="keyword">static</span> <span class="type">long</span> <span class="variable">START_STMP</span> <span class="operator">=</span> <span class="number">1480166465631L</span>;</span><br><span class="line">    <span class="comment">/**</span></span><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> <span class="keyword">final</span> <span class="keyword">static</span> <span class="type">long</span> <span class="variable">SEQUENCE_BIT</span> <span class="operator">=</span> <span class="number">12</span>;</span><br><span class="line">    <span class="comment">/**</span></span><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> <span class="keyword">final</span> <span class="keyword">static</span> <span class="type">long</span> <span class="variable">MACHINE_BIT</span> <span class="operator">=</span> <span class="number">5</span>;</span><br><span class="line">    <span class="comment">/**</span></span><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> <span class="keyword">final</span> <span class="keyword">static</span> <span class="type">long</span> <span class="variable">DATACENTER_BIT</span> <span class="operator">=</span> <span class="number">5</span>;</span><br><span class="line">    <span class="comment">/**</span></span><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> <span class="keyword">final</span> <span class="keyword">static</span> <span class="type">long</span> <span class="variable">MAX_DATACENTER_NUM</span> <span class="operator">=</span> -<span class="number">1L</span> ^ (-<span class="number">1L</span> &lt;&lt; DATACENTER_BIT);</span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">final</span> <span class="keyword">static</span> <span class="type">long</span> <span class="variable">MAX_MACHINE_NUM</span> <span class="operator">=</span> -<span class="number">1L</span> ^ (-<span class="number">1L</span> &lt;&lt; MACHINE_BIT);</span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">final</span> <span class="keyword">static</span> <span class="type">long</span> <span class="variable">MAX_SEQUENCE</span> <span class="operator">=</span> -<span class="number">1L</span> ^ (-<span class="number">1L</span> &lt;&lt; SEQUENCE_BIT);</span><br><span class="line">    <span class="comment">/**</span></span><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> <span class="keyword">final</span> <span class="keyword">static</span> <span class="type">long</span> <span class="variable">MACHINE_LEFT</span> <span class="operator">=</span> SEQUENCE_BIT;</span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">final</span> <span class="keyword">static</span> <span class="type">long</span> <span class="variable">DATACENTER_LEFT</span> <span class="operator">=</span> SEQUENCE_BIT + MACHINE_BIT;</span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">final</span> <span class="keyword">static</span> <span class="type">long</span> <span class="variable">TIMESTMP_LEFT</span> <span class="operator">=</span> DATACENTER_LEFT + DATACENTER_BIT;</span><br><span class="line">    <span class="comment">/**</span></span><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> <span class="type">long</span> datacenterId;</span><br><span class="line">    <span class="comment">/**</span></span><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> <span class="type">long</span> machineId;</span><br><span class="line">    <span class="comment">/**</span></span><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> <span class="type">long</span> <span class="variable">sequence</span> <span class="operator">=</span> <span class="number">0L</span>;</span><br><span class="line">    <span class="comment">/**</span></span><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> <span class="type">long</span> <span class="variable">lastStmp</span> <span class="operator">=</span> -<span class="number">1L</span>;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">public</span> <span class="title function_">SnowFlake</span><span class="params">(<span class="type">long</span> datacenterId, <span class="type">long</span> machineId)</span> &#123;</span><br><span class="line">        <span class="keyword">if</span> (datacenterId &gt; MAX_DATACENTER_NUM || datacenterId &lt; <span class="number">0</span>) &#123;</span><br><span class="line">            <span class="keyword">throw</span> <span class="keyword">new</span> <span class="title class_">IllegalArgumentException</span>(<span class="string">&quot;datacenterId can&#x27;t be greater than MAX_DATACENTER_NUM or less than 0&quot;</span>);</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">if</span> (machineId &gt; MAX_MACHINE_NUM || machineId &lt; <span class="number">0</span>) &#123;</span><br><span class="line">            <span class="keyword">throw</span> <span class="keyword">new</span> <span class="title class_">IllegalArgumentException</span>(<span class="string">&quot;machineId can&#x27;t be greater than MAX_MACHINE_NUM or less than 0&quot;</span>);</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="built_in">this</span>.datacenterId = datacenterId;</span><br><span class="line">        <span class="built_in">this</span>.machineId = machineId;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     *  产生下一个ID</span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">synchronized</span> <span class="type">long</span> <span class="title function_">nextId</span><span class="params">()</span> &#123;</span><br><span class="line">        <span class="type">long</span> <span class="variable">currStmp</span> <span class="operator">=</span> getNewstmp();</span><br><span class="line">        <span class="keyword">if</span> (currStmp &lt; lastStmp) &#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;Clock moved backwards.  Refusing to generate id&quot;</span>);</span><br><span class="line">        &#125;</span><br><span class="line"></span><br><span class="line">        <span class="keyword">if</span> (currStmp == lastStmp) &#123;</span><br><span class="line">            <span class="comment">//相同毫秒内，序列号自增</span></span><br><span class="line">            sequence = (sequence + <span class="number">1</span>) &amp; MAX_SEQUENCE;</span><br><span class="line">            <span class="comment">//同一毫秒的序列数已经达到最大</span></span><br><span class="line">            <span class="keyword">if</span> (sequence == <span class="number">0L</span>) &#123;</span><br><span class="line">                currStmp = getNextMill();</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="comment">//不同毫秒内，序列号置为0</span></span><br><span class="line">            sequence = <span class="number">0L</span>;</span><br><span class="line">        &#125;</span><br><span class="line"></span><br><span class="line">        lastStmp = currStmp;</span><br><span class="line"></span><br><span class="line">        <span class="keyword">return</span>          <span class="comment">// 时间戳部分</span></span><br><span class="line">                (currStmp - START_STMP) &lt;&lt; TIMESTMP_LEFT</span><br><span class="line">                        <span class="comment">// 数据中心部分</span></span><br><span class="line">                | datacenterId &lt;&lt; DATACENTER_LEFT</span><br><span class="line">                        <span class="comment">// 机器标识部分</span></span><br><span class="line">                | machineId &lt;&lt; MACHINE_LEFT</span><br><span class="line">                        <span class="comment">// 序列号部分</span></span><br><span class="line">                | sequence;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">private</span> <span class="type">long</span> <span class="title function_">getNextMill</span><span class="params">()</span> &#123;</span><br><span class="line">        <span class="type">long</span> <span class="variable">mill</span> <span class="operator">=</span> getNewstmp();</span><br><span class="line">        <span class="keyword">while</span> (mill &lt;= lastStmp) &#123;</span><br><span class="line">            mill = getNewstmp();</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">return</span> mill;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">private</span> <span class="type">long</span> <span class="title function_">getNewstmp</span><span class="params">()</span> &#123;</span><br><span class="line">        <span class="keyword">return</span> System.currentTimeMillis();</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     *  测试</span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title function_">main</span><span class="params">(String[] args)</span> &#123;</span><br><span class="line"></span><br><span class="line">        <span class="type">SnowFlake</span> <span class="variable">snowFlake</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">SnowFlake</span>(<span class="number">2</span>, <span class="number">3</span>);</span><br><span class="line"></span><br><span class="line">        <span class="keyword">for</span> (<span class="type">int</span> <span class="variable">i</span> <span class="operator">=</span> <span class="number">0</span>; i &lt; (<span class="number">1</span> &lt;&lt; <span class="number">12</span>); i++) &#123;</span><br><span class="line">            System.out.println(snowFlake.nextId());</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"></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>参考于:<a href="https://github.com/beyondfengyu/SnowFlake">https://github.com/beyondfengyu/SnowFlake</a></p><h1 id="JWT工具类"><a href="#JWT工具类" class="headerlink" title="JWT工具类"></a>JWT工具类</h1><h2 id="引入依赖"><a href="#引入依赖" class="headerlink" title="引入依赖"></a>引入依赖</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></pre></td><td class="code"><pre><span class="line"><span class="comment">&lt;!--  jwt  --&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.auth0<span class="tag">&lt;/<span class="name">groupId</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">artifactId</span>&gt;</span>java-jwt<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;jwt.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></pre></td></tr></table></figure><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><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><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></pre></td><td class="code"><pre><span class="line"><span class="keyword">package</span> com.yww.management.utils;</span><br><span class="line"></span><br><span class="line"><span class="keyword">import</span> com.auth0.jwt.JWT;</span><br><span class="line"><span class="keyword">import</span> com.auth0.jwt.algorithms.Algorithm;</span><br><span class="line"><span class="keyword">import</span> com.auth0.jwt.exceptions.*;</span><br><span class="line"><span class="keyword">import</span> com.auth0.jwt.interfaces.DecodedJWT;</span><br><span class="line"><span class="keyword">import</span> com.auth0.jwt.interfaces.JWTVerifier;</span><br><span class="line"></span><br><span class="line"><span class="keyword">import</span> java.util.Date;</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"> * &lt;p&gt;</span></span><br><span class="line"><span class="comment"> *      Token工具类</span></span><br><span class="line"><span class="comment"> * 1. Header,记录令牌类型和签名算法</span></span><br><span class="line"><span class="comment"> * 2. payload,携带用户信息</span></span><br><span class="line"><span class="comment"> *      (1) iss(issuer), 签发者</span></span><br><span class="line"><span class="comment"> *      (2) sub(subject), 面向的主体</span></span><br><span class="line"><span class="comment"> *      (3) aud(audience), 接收方</span></span><br><span class="line"><span class="comment"> *      (4) nbf(notBefore), 开始生效生效时间戳</span></span><br><span class="line"><span class="comment"> *      (5) exp(expiresAt), 过期时间戳</span></span><br><span class="line"><span class="comment"> *      (6) iat(issuedAt ), 签发时间</span></span><br><span class="line"><span class="comment"> *      (7) jti(jwtId), 唯一标识</span></span><br><span class="line"><span class="comment"> * 3. signature, 签名，防止Token被篡改</span></span><br><span class="line"><span class="comment"> * &lt;/p&gt;</span></span><br><span class="line"><span class="comment"> *</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@ClassName</span> TokenUtil</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@Author</span> yww</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@Date</span> 2022/10/15 14:31</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_">TokenUtil</span> &#123;</span><br><span class="line"></span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     * 生成Token</span></span><br><span class="line"><span class="comment">     *</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@return</span>  Token</span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">static</span> String <span class="title function_">genToken</span><span class="params">(String secret)</span> &#123;</span><br><span class="line">        <span class="comment">// 设置Token头部（不设置也会默认有这两个值）</span></span><br><span class="line">        Map&lt;String, Object&gt; header = <span class="keyword">new</span> <span class="title class_">HashMap</span>&lt;String, Object&gt;(<span class="number">2</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">            &#123;</span><br><span class="line">                put(<span class="string">&quot;alg&quot;</span>, <span class="string">&quot;HMAC512&quot;</span>);</span><br><span class="line">                put(<span class="string">&quot;typ&quot;</span>, <span class="string">&quot;JWT&quot;</span>);</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;;</span><br><span class="line">        <span class="comment">// 设置需要放在Token的有效载荷</span></span><br><span class="line">        Map&lt;String, Object&gt; payload = <span class="keyword">new</span> <span class="title class_">HashMap</span>&lt;String, Object&gt;(<span class="number">3</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">            &#123;</span><br><span class="line">                put(<span class="string">&quot;name&quot;</span>, <span class="string">&quot;yww&quot;</span>);</span><br><span class="line">                put(<span class="string">&quot;num&quot;</span>, <span class="number">1141950370</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="type">Date</span> <span class="variable">now</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">Date</span>();</span><br><span class="line">        <span class="type">Date</span> <span class="variable">exp</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">Date</span>(now.getTime() + <span class="number">10</span>);</span><br><span class="line"></span><br><span class="line">        <span class="keyword">return</span> JWT.create()</span><br><span class="line">                <span class="comment">// 设置header</span></span><br><span class="line">                .withHeader(header)</span><br><span class="line">                <span class="comment">// 设置payload</span></span><br><span class="line">                .withIssuer(<span class="string">&quot;yww&quot;</span>)</span><br><span class="line">                .withSubject(<span class="string">&quot;management&quot;</span>)</span><br><span class="line">                .withAudience(<span class="string">&quot;vue-management&quot;</span>)</span><br><span class="line">                .withNotBefore(now)</span><br><span class="line">                .withExpiresAt(exp)</span><br><span class="line">                .withIssuedAt(now)</span><br><span class="line">                .withJWTId(<span class="string">&quot;123456&quot;</span>)</span><br><span class="line">                .withPayload(payload)</span><br><span class="line">                <span class="comment">// 签名</span></span><br><span class="line">                .sign(Algorithm.HMAC512(secret));</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     * 解析Token</span></span><br><span class="line"><span class="comment">     *</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@param</span> token Token</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_">parse</span><span class="params">(String token)</span> &#123;</span><br><span class="line">        <span class="keyword">try</span> &#123;</span><br><span class="line">            <span class="type">JWTVerifier</span> <span class="variable">jwtVerifier</span> <span class="operator">=</span> JWT.require(Algorithm.HMAC512(<span class="string">&quot;1&quot;</span>)).build();</span><br><span class="line">            <span class="type">DecodedJWT</span> <span class="variable">decode</span> <span class="operator">=</span> jwtVerifier.verify(token);</span><br><span class="line">            System.out.println(decode.getHeader());</span><br><span class="line">            System.out.println(decode.getPayload());</span><br><span class="line">            System.out.println(decode.getSignature());</span><br><span class="line">        &#125; <span class="keyword">catch</span> (AlgorithmMismatchException e) &#123;</span><br><span class="line">            System.out.println(<span class="string">&quot;签名算法不匹配&quot;</span>);</span><br><span class="line">        &#125; <span class="keyword">catch</span> (SignatureVerificationException e) &#123;</span><br><span class="line">            System.out.println(<span class="string">&quot;签名无效&quot;</span>);</span><br><span class="line">        &#125; <span class="keyword">catch</span> (TokenExpiredException e) &#123;</span><br><span class="line">            System.out.println(<span class="string">&quot;令牌已过期&quot;</span>);</span><br><span class="line">        &#125; <span class="keyword">catch</span> (MissingClaimException e) &#123;</span><br><span class="line">            System.out.println(<span class="string">&quot;缺少要验证的声明&quot;</span>);</span><br><span class="line">        &#125; <span class="keyword">catch</span> (IncorrectClaimException e) &#123;</span><br><span class="line">            System.out.println(<span class="string">&quot;声明包含的值和预期不符合&quot;</span>);</span><br><span class="line">        &#125; <span class="keyword">catch</span> (JWTVerificationException 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"></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h1 id="响应工具类"><a href="#响应工具类" class="headerlink" title="响应工具类"></a>响应工具类</h1><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">import</span> cn.hutool.json.JSONUtil;</span><br><span class="line"><span class="keyword">import</span> com.yww.management.common.Result;</span><br><span class="line"></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.IOException;</span><br><span class="line"></span><br><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * &lt;p&gt;</span></span><br><span class="line"><span class="comment"> *      用于接口外响应JSON的响应工具类</span></span><br><span class="line"><span class="comment"> * &lt;/p&gt;</span></span><br><span class="line"><span class="comment"> *</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@ClassName</span> ResponseUtil</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@Author</span> yww</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@Date</span> 2022/10/12 21:30</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_">ResponseUtil</span> &#123;</span><br><span class="line"></span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     * 用于返回JSON数据</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> result   响应封装</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_">response</span><span class="params">(HttpServletResponse response, Result&lt;Object&gt; result)</span> <span class="keyword">throws</span> IOException &#123;</span><br><span class="line">        <span class="comment">// 设置响应的Header</span></span><br><span class="line">        response.setHeader(<span class="string">&quot;Access-Control-Allow-Origin&quot;</span>, <span class="string">&quot;*&quot;</span>);</span><br><span class="line">        response.setHeader(<span class="string">&quot;Cache-Control&quot;</span>,<span class="string">&quot;no-cache&quot;</span>);</span><br><span class="line">        response.setCharacterEncoding(<span class="string">&quot;UTF-8&quot;</span>);</span><br><span class="line">        response.setContentType(<span class="string">&quot;application/json&quot;</span>);</span><br><span class="line">        <span class="comment">// 设置响应码</span></span><br><span class="line">        response.setStatus(result.getCode());</span><br><span class="line">        <span class="comment">// 设置响应内容</span></span><br><span class="line">        response.getWriter().write(JSONUtil.toJsonStr(result));</span><br><span class="line">        <span class="comment">// 将缓存信息刷新到页面</span></span><br><span class="line">        response.getWriter().flush();</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h1 id="IP工具类"><a href="#IP工具类" class="headerlink" title="IP工具类"></a>IP工具类</h1><p>这个工具类主要是用于获取请求的IP地址。</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><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">import</span> cn.hutool.core.util.StrUtil;</span><br><span class="line"></span><br><span class="line"><span class="keyword">import</span> javax.servlet.http.HttpServletRequest;</span><br><span class="line"></span><br><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * &lt;p&gt;</span></span><br><span class="line"><span class="comment"> *      IP相关工具类</span></span><br><span class="line"><span class="comment"> * &lt;/p&gt;</span></span><br><span class="line"><span class="comment"> *</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@ClassName</span> IpUtil</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@Author</span> yww</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@Date</span> 2022/10/16 14:27</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_">IpUtil</span> &#123;</span><br><span class="line"></span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     * 获取客户端IP</span></span><br><span class="line"><span class="comment">     *</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@param</span> request   请求</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@return</span>          IP</span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">static</span> String <span class="title function_">getIpAddr</span><span class="params">(HttpServletRequest request)</span> &#123;</span><br><span class="line">        <span class="keyword">if</span> (request == <span class="literal">null</span>) &#123;</span><br><span class="line">            <span class="keyword">return</span> <span class="string">&quot;unknown&quot;</span>;</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="comment">// 判断x-forwarded-for是否有IP</span></span><br><span class="line">        <span class="type">String</span> <span class="variable">ip</span> <span class="operator">=</span> request.getHeader(<span class="string">&quot;x-forwarded-for&quot;</span>);</span><br><span class="line">        <span class="keyword">if</span> (isNotUnknown(ip)) &#123;</span><br><span class="line">            ip = request.getHeader(<span class="string">&quot;Proxy-Client-IP&quot;</span>);</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="comment">// 判断Proxy-Client-IP是否有IP</span></span><br><span class="line">        <span class="keyword">if</span> (isNotUnknown(ip)) &#123;</span><br><span class="line">            ip = request.getHeader(<span class="string">&quot;X-Forwarded-For&quot;</span>);</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="comment">// 判断X-Forwarded-For是否有IP</span></span><br><span class="line">        <span class="keyword">if</span> (isNotUnknown(ip)) &#123;</span><br><span class="line">            ip = request.getHeader(<span class="string">&quot;WL-Proxy-Client-IP&quot;</span>);</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="comment">// 判断WL-Proxy-Client-IP是否有IP</span></span><br><span class="line">        <span class="keyword">if</span> (isNotUnknown(ip)) &#123;</span><br><span class="line">            ip = request.getHeader(<span class="string">&quot;X-Real-IP&quot;</span>);</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="comment">// 判断X-Real-IP是否有IP</span></span><br><span class="line">        <span class="keyword">if</span> (isNotUnknown(ip)) &#123;</span><br><span class="line">            ip = request.getRemoteAddr();</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="comment">// 若是IP最后为0:0:0:0:0:0:0:1，就是本地回环地址，否则第一个非unknown的IP地址就是真实地址</span></span><br><span class="line">        <span class="keyword">return</span> <span class="string">&quot;0:0:0:0:0:0:0:1&quot;</span>.equals(ip) ? <span class="string">&quot;127.0.0.1&quot;</span> : getMultistageReverseProxyIp(ip);</span><br><span 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">     * 从多级反向代理中获得第一个非unknown IP地址</span></span><br><span class="line"><span class="comment">     *</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@param</span> ip 获得的IP地址</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@return</span> 第一个非unknown IP地址</span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    <span class="meta">@SuppressWarnings(&quot;all&quot;)</span></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">static</span> String <span class="title function_">getMultistageReverseProxyIp</span><span class="params">(String ip)</span> &#123;</span><br><span class="line">        <span class="comment">// 多级反向代理检测</span></span><br><span class="line">        <span class="keyword">if</span> (ip != <span class="literal">null</span> &amp;&amp; ip.indexOf(<span class="string">&quot;,&quot;</span>) &gt; <span class="number">0</span>) &#123;</span><br><span class="line">            <span class="keyword">final</span> String[] ips = ip.trim().split(<span class="string">&quot;,&quot;</span>);</span><br><span class="line">            <span class="keyword">for</span> (String subIp : ips) &#123;</span><br><span class="line">                <span class="keyword">if</span> (isNotUnknown(subIp)) &#123;</span><br><span class="line">                    ip = subIp;</span><br><span class="line">                    <span class="keyword">break</span>;</span><br><span class="line">                &#125;</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">return</span> ip;</span><br><span 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">     * 检测给定字符串是否为未知，多用于检测HTTP请求相关</span></span><br><span class="line"><span class="comment">     *</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@param</span> checkString 被检测的字符串</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> <span class="type">boolean</span> <span class="title function_">isNotUnknown</span><span class="params">(String checkString)</span> &#123;</span><br><span class="line">        <span class="keyword">return</span> StrUtil.isNotBlank(checkString) &amp;&amp; StrUtil.isNotEmpty(checkString) &amp;&amp; !<span class="string">&quot;unknown&quot;</span>.equalsIgnoreCase(checkString);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h1 id="BigDecimal工具类"><a href="#BigDecimal工具类" class="headerlink" title="BigDecimal工具类"></a>BigDecimal工具类</h1><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><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><br><span class="line"><span class="keyword">import</span> java.math.BigDecimal;</span><br><span class="line"><span class="keyword">import</span> java.math.RoundingMode;</span><br><span class="line"></span><br><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * &lt;p&gt;</span></span><br><span class="line"><span class="comment"> *      BigDecimal工具类</span></span><br><span class="line"><span class="comment"> *  用来提供浮点数的精确运算的工具类</span></span><br><span class="line"><span class="comment"> * &lt;/p&gt;</span></span><br><span class="line"><span class="comment"> *</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@ClassName</span> BigDecimalUtil</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@Author</span> yww</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@Date</span> 2022/10/25 16:47</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_">BigDecimalUtil</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">private</span> <span class="keyword">static</span> <span class="keyword">final</span> <span class="type">int</span> <span class="variable">DEFAULT_PRECISION</span> <span class="operator">=</span> <span class="number">10</span>;</span><br><span class="line"></span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     *  私有化无参构造</span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    <span class="keyword">private</span> <span class="title function_">BigDecimalUtil</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="comment">     * <span class="doctag">@param</span> value1    浮点数1</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@param</span> value2    浮点数2</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@return</span>          浮点数1 + 浮点数2</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_">addUp</span><span class="params">(<span class="type">double</span> value1, <span class="type">double</span> value2)</span> &#123;</span><br><span class="line">        <span class="type">BigDecimal</span> <span class="variable">b1</span> <span class="operator">=</span> BigDecimal.valueOf(value1);</span><br><span class="line">        <span class="type">BigDecimal</span> <span class="variable">b2</span> <span class="operator">=</span> BigDecimal.valueOf(value2);</span><br><span class="line">        <span class="keyword">return</span> b1.add(b2).doubleValue();</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     *  精确减法</span></span><br><span class="line"><span class="comment">     *</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@param</span> value1    浮点数1</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@param</span> value2    浮点数2</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@return</span>          浮点数1 - 浮点数2</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_">subtract</span><span class="params">(<span class="type">double</span> value1, <span class="type">double</span> value2)</span> &#123;</span><br><span class="line">        <span class="type">BigDecimal</span> <span class="variable">b1</span> <span class="operator">=</span> BigDecimal.valueOf(value1);</span><br><span class="line">        <span class="type">BigDecimal</span> <span class="variable">b2</span> <span class="operator">=</span> BigDecimal.valueOf(value2);</span><br><span class="line">        <span class="keyword">return</span> b1.subtract(b2).doubleValue();</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     *  精确乘法</span></span><br><span class="line"><span class="comment">     *</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@param</span> value1    浮点数1</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@param</span> value2    浮点数2</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@return</span>          浮点数1 * 浮点数2</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_">multiply</span><span class="params">(<span class="type">double</span> value1, <span class="type">double</span> value2)</span> &#123;</span><br><span class="line">        <span class="type">BigDecimal</span> <span class="variable">b1</span> <span class="operator">=</span> BigDecimal.valueOf(value1);</span><br><span class="line">        <span class="type">BigDecimal</span> <span class="variable">b2</span> <span class="operator">=</span> BigDecimal.valueOf(value2);</span><br><span class="line">        <span class="keyword">return</span> b1.multiply(b2).doubleValue();</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     *  精确除法(使用默认精度)</span></span><br><span class="line"><span class="comment">     *</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@param</span> value1    浮点数1</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@param</span> value2    浮点数2</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@return</span>          浮点数1 / 浮点数2</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_">div</span><span class="params">(<span class="type">double</span> value1, <span class="type">double</span> value2)</span> <span class="keyword">throws</span> IllegalAccessException &#123;</span><br><span class="line">        <span class="keyword">return</span> div(value1, value2, DEFAULT_PRECISION);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     *  精确除法(指定精度)</span></span><br><span class="line"><span class="comment">     *  四舍五入</span></span><br><span class="line"><span class="comment">     *</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@param</span> value1        浮点数1</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@param</span> value2        浮点数2</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@param</span> precision     精度</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@return</span>              浮点数1 / 浮点数2</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_">div</span><span class="params">(<span class="type">double</span> value1, <span class="type">double</span> value2, <span class="type">int</span> precision)</span> <span class="keyword">throws</span> IllegalAccessException &#123;</span><br><span class="line">        <span class="keyword">if</span> (precision &lt; <span class="number">0</span>) &#123;</span><br><span class="line">            <span class="keyword">throw</span> <span class="keyword">new</span> <span class="title class_">IllegalAccessException</span>(<span class="string">&quot;除法的精度不能小于0&quot;</span>);</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">return</span> div(value1, value2, precision, RoundingMode.HALF_UP);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     *  精确除法(指定精度)</span></span><br><span class="line"><span class="comment">     *  指定舍入的策略</span></span><br><span class="line"><span class="comment">     *</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@param</span> value1        浮点数1</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@param</span> value2        浮点数2</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@param</span> precision     精度</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@param</span> mode          舍入策略</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@return</span>              浮点数1 / 浮点数2</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_">div</span><span class="params">(<span class="type">double</span> value1, <span class="type">double</span> value2, <span class="type">int</span> precision, RoundingMode mode)</span> <span class="keyword">throws</span> IllegalAccessException &#123;</span><br><span class="line">        <span class="keyword">if</span> (precision &lt; <span class="number">0</span>) &#123;</span><br><span class="line">            <span class="keyword">throw</span> <span class="keyword">new</span> <span class="title class_">IllegalAccessException</span>(<span class="string">&quot;除法的精度不能小于0&quot;</span>);</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="type">BigDecimal</span> <span class="variable">b1</span> <span class="operator">=</span> BigDecimal.valueOf(value1);</span><br><span class="line">        <span class="type">BigDecimal</span> <span class="variable">b2</span> <span class="operator">=</span> BigDecimal.valueOf(value2);</span><br><span class="line">        <span class="keyword">return</span> b1.divide(b2, precision, mode).doubleValue();</span><br><span class="line">    &#125;</span><br><span class="line">    </span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h1 id="断言工具类"><a href="#断言工具类" class="headerlink" title="断言工具类"></a>断言工具类</h1><blockquote><p>hutool里也有很多断言的工具类，但是抛出的异常不是自己定义的，所以为了指定抛出的异常，需要自己在定义一个断言工具类。</p></blockquote><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><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><br><span class="line"><span class="keyword">import</span> cn.hutool.core.util.StrUtil;</span><br><span class="line"><span class="keyword">import</span> com.yww.management.common.exception.GlobalException;</span><br><span class="line"></span><br><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * &lt;p&gt;</span></span><br><span class="line"><span class="comment"> *      断言工具类</span></span><br><span class="line"><span class="comment"> * &lt;/p&gt;</span></span><br><span class="line"><span class="comment"> *</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@ClassName</span> AssertUtils</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@Author</span> yww</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@Date</span> 2022/10/20 11:27</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_">AssertUtils</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">private</span> <span class="title function_">AssertUtils</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">     * 如果对象为&#123;<span class="doctag">@code</span> null&#125;, 则抛出异常</span></span><br><span class="line"><span class="comment">     *</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@param</span> object 要判断的对象</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@throws</span> GlobalException  全局异常类</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_">notNull</span><span class="params">(Object object)</span> <span class="keyword">throws</span> GlobalException &#123;</span><br><span class="line">        notNull(object, <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">     * 如果对象为&#123;<span class="doctag">@code</span> null&#125;, 则抛出异常</span></span><br><span class="line"><span class="comment">     *</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@param</span> object  要判断的对象</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 class="doctag">@throws</span> GlobalException  全局异常类</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_">notNull</span><span class="params">(Object object, String message)</span> <span class="keyword">throws</span> GlobalException &#123;</span><br><span class="line">        <span class="keyword">if</span> (object == <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_">GlobalException</span>(message);</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     * 如果字符串为&#123;<span class="doctag">@code</span> null&#125;、空字符串或仅包含空白字符, 则抛出异常</span></span><br><span class="line"><span class="comment">     *</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@param</span> text 要进行检查的字符串</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@throws</span> GlobalException  全局异常类</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_">hasText</span><span class="params">(String text)</span> <span class="keyword">throws</span> GlobalException &#123;</span><br><span class="line">        hasText(text, <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">     * 如果字符串为&#123;<span class="doctag">@code</span> null&#125;、空字符串或仅包含空白字符, 则抛出异常</span></span><br><span class="line"><span class="comment">     *</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@param</span> text    要进行检查的字符串</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 class="doctag">@throws</span> GlobalException  全局异常类</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_">hasText</span><span class="params">(String text, String message)</span> <span class="keyword">throws</span> GlobalException &#123;</span><br><span class="line">        <span class="keyword">if</span> (StrUtil.isBlank(text)) &#123;</span><br><span class="line">            <span class="keyword">throw</span> <span class="keyword">new</span> <span class="title class_">GlobalException</span>(message);</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><h1 id="ThreadLocal工具类"><a href="#ThreadLocal工具类" class="headerlink" title="ThreadLocal工具类"></a>ThreadLocal工具类</h1><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><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><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"> * &lt;p&gt;</span></span><br><span class="line"><span class="comment"> *      ThreadLocal工具类</span></span><br><span class="line"><span class="comment"> * &lt;/p&gt;</span></span><br><span class="line"><span class="comment"> *</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@ClassName</span> ThreadLocalUtil</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@Author</span> yww</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@Date</span> 2022/10/21 14:27</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="meta">@SuppressWarnings(&quot;unchecked&quot;)</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">ThreadLocalUtil</span> &#123;</span><br><span class="line"></span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     *  ThreadLocalMap</span></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> ThreadLocal&lt;Map&lt;String, Object&gt;&gt; THREAD_LOCAL_MAP = ThreadLocal.withInitial(HashMap::<span class="keyword">new</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">private</span> <span class="title function_">ThreadLocalUtil</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">     * 根据指定的key获取当前线程相关的变量值</span></span><br><span class="line"><span class="comment">     *</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@param</span> key 变量的key</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 class="doctag">@return</span> 若无此key对应的变量值, 则返回&#123;<span class="doctag">@code</span> null&#125;</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@throws</span> ClassCastException 若接收此返回值的变量类型与上下文保存的值的实际类型不匹配, 则抛出异常</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> &lt;T&gt; T <span class="title function_">get</span><span class="params">(String key)</span> &#123;</span><br><span class="line">        <span class="keyword">return</span> (T) THREAD_LOCAL_MAP.get().get(key);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     * 根据指定的key获取当前线程相关的变量值, 若为&#123;<span class="doctag">@code</span> null&#125;则返回指定的默认值</span></span><br><span class="line"><span class="comment">     *</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@param</span> key          变量的key</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@param</span> defaultValue 默认值</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 class="doctag">@return</span> 若无此key对应的变量值, 则返回defaultValue</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@throws</span> ClassCastException 若接收此返回值的变量类型与上下文保存的值的实际类型不匹配, 则抛出异常</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> &lt;T&gt; T <span class="title function_">get</span><span class="params">(String key, T defaultValue)</span> &#123;</span><br><span class="line">        <span class="type">T</span> <span class="variable">value</span> <span class="operator">=</span> get(key);</span><br><span class="line">        <span class="keyword">return</span> value == <span class="literal">null</span> ? defaultValue : value;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     * 设置线程相关上下文的变量值</span></span><br><span class="line"><span class="comment">     *</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@param</span> key   变量的key</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@param</span> value 变量值</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_">set</span><span class="params">(String key, Object value)</span> &#123;</span><br><span class="line">        THREAD_LOCAL_MAP.get().put(key, value);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     * 删除指定key的变量</span></span><br><span class="line"><span class="comment">     *</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@param</span> key 变量的key</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_">remove</span><span class="params">(String key)</span> &#123;</span><br><span class="line">        THREAD_LOCAL_MAP.get().remove(key);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     * 清除当前线程相关的上下文</span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title function_">close</span><span class="params">()</span> &#123;</span><br><span class="line">        THREAD_LOCAL_MAP.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><h1 id="分页工具类"><a href="#分页工具类" class="headerlink" title="分页工具类"></a>分页工具类</h1><h2 id="封装分页请求对象"><a href="#封装分页请求对象" class="headerlink" title="封装分页请求对象"></a>封装分页请求对象</h2><blockquote><p>如果需要添加其他的属性筛选，只需要继承该封装对象即可。</p></blockquote><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><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></pre></td><td class="code"><pre><span class="line"></span><br><span class="line"><span class="keyword">import</span> com.fasterxml.jackson.annotation.JsonIgnore;</span><br><span class="line"><span class="keyword">import</span> com.fasterxml.jackson.annotation.JsonIgnoreProperties;</span><br><span class="line"></span><br><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * &lt;p&gt;</span></span><br><span class="line"><span class="comment"> *      分页查询请求对象</span></span><br><span class="line"><span class="comment"> * &lt;/p&gt;</span></span><br><span class="line"><span class="comment"> *</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@ClassName</span> PageVo</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@Author</span> yww</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@Date</span> 2022/10/24 21:18</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="meta">@SuppressWarnings(&quot;all&quot;)</span></span><br><span class="line"><span class="meta">@JsonIgnoreProperties(ignoreUnknown = true)</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">PageVo</span> &#123;</span><br><span class="line"></span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     * 每页显示行数默认为10</span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">static</span> <span class="keyword">final</span> <span class="type">int</span> <span class="variable">DEFAULT_SIZE</span> <span class="operator">=</span> <span class="number">10</span>;</span><br><span class="line"></span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     *  当前页码, 首页为1</span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    <span class="keyword">private</span> <span class="type">int</span> <span class="variable">page</span> <span class="operator">=</span> <span class="number">1</span>;</span><br><span class="line"></span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     *  每页记录条数</span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    <span class="keyword">private</span> <span class="type">int</span> <span class="variable">size</span> <span class="operator">=</span> DEFAULT_SIZE;</span><br><span class="line"></span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     *  排序字段名,asc,desc</span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    <span class="keyword">private</span> String sort;</span><br><span class="line"></span><br><span class="line">    <span class="comment">/**</span></span><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> String dir;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">static</span> PageVo <span class="title function_">of</span><span class="params">(<span class="type">int</span> page, <span class="type">int</span> size)</span> &#123;</span><br><span class="line">        <span class="type">PageVo</span> <span class="variable">pageReqVo</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">PageVo</span>();</span><br><span class="line">        pageReqVo.setPage(page);</span><br><span class="line">        pageReqVo.setSize(size);</span><br><span class="line">        <span class="keyword">return</span> pageReqVo;</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_">PageVo</span><span class="params">()</span> &#123;&#125;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@JsonIgnore</span></span><br><span class="line">    <span class="keyword">public</span> <span class="type">int</span> <span class="title function_">getOffset</span><span class="params">()</span> &#123;</span><br><span class="line">        <span class="keyword">return</span> (getPage() - <span class="number">1</span>) * getSize();</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_">getPage</span><span class="params">()</span> &#123;</span><br><span class="line">        <span class="keyword">return</span> page &gt; <span class="number">0</span> ? page : <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="keyword">void</span> <span class="title function_">setPage</span><span class="params">(<span class="type">int</span> page)</span> &#123;</span><br><span class="line">        <span class="built_in">this</span>.page = page;</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_">getSize</span><span class="params">()</span> &#123;</span><br><span class="line">        <span class="keyword">return</span> size &gt; <span class="number">0</span> ? size : DEFAULT_SIZE;</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_">setSize</span><span class="params">(<span class="type">int</span> size)</span> &#123;</span><br><span class="line">        <span class="built_in">this</span>.size = size;</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_">getSort</span><span class="params">()</span> &#123;</span><br><span class="line">        <span class="keyword">return</span> sort;</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_">setSort</span><span class="params">(String sort)</span> &#123;</span><br><span class="line">        <span class="built_in">this</span>.sort = sort;</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_">getDir</span><span class="params">()</span> &#123;</span><br><span class="line">        <span class="keyword">return</span> dir;</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_">setDir</span><span class="params">(String dir)</span> &#123;</span><br><span class="line">        <span class="built_in">this</span>.dir = dir;</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><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><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="keyword">import</span> com.fasterxml.jackson.annotation.JsonPropertyOrder;</span><br><span class="line"></span><br><span class="line"><span class="keyword">import</span> java.util.List;</span><br><span class="line"></span><br><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * &lt;p&gt;</span></span><br><span class="line"><span class="comment"> *      分页查询结果</span></span><br><span class="line"><span class="comment"> * &lt;/p&gt;</span></span><br><span class="line"><span class="comment"> *</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@ClassName</span> PageResultVo</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@Author</span> yww</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@Date</span> 2022/10/24 21:15</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="meta">@JsonPropertyOrder(&#123;&quot;start&quot;, &quot;size&quot;, &quot;total&quot;, &quot;rows&quot;&#125;)</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">PageResultVo</span>&lt;T&gt; &#123;</span><br><span class="line"></span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     *  本页记录在所有记录中的起始位置</span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    <span class="keyword">private</span> <span class="type">int</span> start;</span><br><span class="line"></span><br><span class="line">    <span class="comment">/**</span></span><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> <span class="type">int</span> size;</span><br><span class="line"></span><br><span class="line">    <span class="comment">/**</span></span><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> <span class="type">int</span> total;</span><br><span class="line"></span><br><span class="line">    <span class="comment">/**</span></span><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> List&lt;T&gt; rows;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">static</span> &lt;E&gt; PageResultVo&lt;E&gt; <span class="title function_">ofReqVo</span><span class="params">(PageVo reqVo, List&lt;E&gt; rows, <span class="type">int</span> total)</span> &#123;</span><br><span class="line">        PageResultVo&lt;E&gt; pageResultVo = <span class="keyword">new</span> <span class="title class_">PageResultVo</span>&lt;&gt;();</span><br><span class="line">        pageResultVo.setSize(reqVo.getSize());</span><br><span class="line">        pageResultVo.setStart(reqVo.getOffset());</span><br><span class="line">        pageResultVo.setTotal(total);</span><br><span class="line">        pageResultVo.setRows(rows);</span><br><span class="line">        <span class="keyword">return</span> pageResultVo;</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_">PageResultVo</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="type">int</span> <span class="title function_">getStart</span><span class="params">()</span> &#123;</span><br><span class="line">        <span class="keyword">return</span> start;</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_">setStart</span><span class="params">(<span class="type">int</span> start)</span> &#123;</span><br><span class="line">        <span class="built_in">this</span>.start = start;</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_">getSize</span><span class="params">()</span> &#123;</span><br><span class="line">        <span class="keyword">return</span> size;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">setSize</span><span class="params">(<span class="type">int</span> size)</span> &#123;</span><br><span class="line">        <span class="built_in">this</span>.size = 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_">getTotal</span><span class="params">()</span> &#123;</span><br><span class="line">        <span class="keyword">return</span> total;</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_">setTotal</span><span class="params">(<span class="type">int</span> total)</span> &#123;</span><br><span class="line">        <span class="built_in">this</span>.total = total;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">public</span> List&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">    <span class="keyword">public</span> <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="built_in">this</span>.rows = rows;</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="分页工具类-1"><a href="#分页工具类-1" 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><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br><span class="line">80</span><br><span class="line">81</span><br><span class="line">82</span><br><span class="line">83</span><br><span class="line">84</span><br><span class="line">85</span><br><span class="line">86</span><br><span class="line">87</span><br><span class="line">88</span><br><span class="line">89</span><br><span class="line">90</span><br><span class="line">91</span><br><span class="line">92</span><br><span class="line">93</span><br><span class="line">94</span><br><span class="line">95</span><br><span class="line">96</span><br><span class="line">97</span><br><span class="line">98</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">import</span> cn.hutool.core.bean.BeanUtil;</span><br><span class="line"><span class="keyword">import</span> cn.hutool.core.util.StrUtil;</span><br><span class="line"><span class="keyword">import</span> com.baomidou.mybatisplus.core.conditions.ISqlSegment;</span><br><span class="line"><span class="keyword">import</span> com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;</span><br><span class="line"><span class="keyword">import</span> com.baomidou.mybatisplus.core.conditions.segments.MergeSegments;</span><br><span class="line"><span class="keyword">import</span> com.baomidou.mybatisplus.core.conditions.segments.NormalSegmentList;</span><br><span class="line"><span class="keyword">import</span> com.baomidou.mybatisplus.core.mapper.BaseMapper;</span><br><span class="line"><span class="keyword">import</span> com.baomidou.mybatisplus.core.metadata.IPage;</span><br><span class="line"><span class="keyword">import</span> com.baomidou.mybatisplus.core.toolkit.Wrappers;</span><br><span class="line"><span class="keyword">import</span> com.baomidou.mybatisplus.extension.plugins.pagination.Page;</span><br><span class="line"><span class="keyword">import</span> com.yww.management.common.exception.GlobalException;</span><br><span class="line"></span><br><span class="line"><span class="keyword">import</span> java.util.ArrayList;</span><br><span class="line"><span class="keyword">import</span> java.util.List;</span><br><span class="line"></span><br><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * &lt;p&gt;</span></span><br><span class="line"><span class="comment"> *      分页工具类</span></span><br><span class="line"><span class="comment"> * &lt;/p&gt;</span></span><br><span class="line"><span class="comment"> *</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@ClassName</span> PageUtil</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@Author</span> yww</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@Date</span> 2022/10/24 21:21</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="meta">@SuppressWarnings(&quot;all&quot;)</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">PageUtil</span> &#123;</span><br><span class="line"></span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     * mybatis-plus自带分页插件</span></span><br><span class="line"><span class="comment">     *</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@param</span> pageVo</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@param</span> mapper</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@param</span> clazz</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@param</span> &lt;R&gt;</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@param</span> &lt;Q&gt;</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> &lt;R, Q <span class="keyword">extends</span> <span class="title class_">PageVo</span>&gt; PageResultVo&lt;R&gt; <span class="title function_">paging</span><span class="params">(Q pageVo, BaseMapper&lt;R&gt; mapper, Class&lt;R&gt; clazz)</span> &#123;</span><br><span class="line">        R entity;</span><br><span class="line">        <span class="keyword">try</span> &#123;</span><br><span class="line">            entity = clazz.newInstance();</span><br><span class="line">        &#125; <span class="keyword">catch</span> (InstantiationException | IllegalAccessException e) &#123;</span><br><span class="line">            <span class="keyword">throw</span> <span class="keyword">new</span> <span class="title class_">GlobalException</span>(<span class="string">&quot;分页类型转换错误&quot;</span>);</span><br><span class="line">        &#125;</span><br><span class="line">        BeanUtil.copyProperties(pageVo, entity);</span><br><span class="line">        IPage&lt;R&gt; page = <span class="keyword">new</span> <span class="title class_">Page</span>&lt;&gt;(pageVo.getPage(), pageVo.getSize());</span><br><span class="line">        page = mapper.selectPage(page, Wrappers.lambdaQuery(entity));</span><br><span class="line">        <span class="keyword">return</span> PageResultVo.ofReqVo(pageVo, page.getRecords(), Long.valueOf(page.getTotal()).intValue());</span><br><span 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">     * mybatis-plus自带分页插件</span></span><br><span class="line"><span class="comment">     *</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@param</span> pageVo</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@param</span> mapper</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@param</span> &lt;R&gt;</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@param</span> &lt;Q&gt;</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> &lt;R, Q <span class="keyword">extends</span> <span class="title class_">PageVo</span>&gt; PageResultVo&lt;R&gt; <span class="title function_">paging</span><span class="params">(Q pageVo, BaseMapper&lt;R&gt; mapper, LambdaQueryWrapper&lt;R&gt; lambdaQuery, Class&lt;R&gt; clazz)</span> &#123;</span><br><span class="line">        R entity;</span><br><span class="line">        <span class="keyword">try</span> &#123;</span><br><span class="line">            entity = clazz.newInstance();</span><br><span class="line">        &#125; <span class="keyword">catch</span> (InstantiationException | IllegalAccessException e) &#123;</span><br><span class="line">            <span class="keyword">throw</span> <span class="keyword">new</span> <span class="title class_">GlobalException</span>(<span class="string">&quot;分页类型转换错误&quot;</span>);</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="comment">// 已设置在lambdaQuery的字段实体类中不应该重复设置</span></span><br><span class="line">        String[] whereFields = getWhereFields(lambdaQuery);</span><br><span class="line">        BeanUtil.copyProperties(pageVo, entity, whereFields);</span><br><span class="line">        lambdaQuery.setEntity(entity);</span><br><span class="line">        IPage&lt;R&gt; page = <span class="keyword">new</span> <span class="title class_">Page</span>&lt;&gt;(pageVo.getPage(), pageVo.getSize());</span><br><span class="line">        page = mapper.selectPage(page, lambdaQuery);</span><br><span class="line">        <span class="keyword">return</span> PageResultVo.ofReqVo(pageVo, page.getRecords(), Long.valueOf(page.getTotal()).intValue());</span><br><span 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> lambdaQuery</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@param</span> &lt;R&gt;</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> &lt;R&gt; String[] getWhereFields(LambdaQueryWrapper&lt;R&gt; lambdaQuery) &#123;</span><br><span class="line">        List&lt;String&gt; fields = <span class="keyword">new</span> <span class="title class_">ArrayList</span>&lt;&gt;();</span><br><span class="line">        <span class="type">MergeSegments</span> <span class="variable">expression</span> <span class="operator">=</span> lambdaQuery.getExpression();</span><br><span class="line">        <span class="type">NormalSegmentList</span> <span class="variable">normal</span> <span class="operator">=</span> expression.getNormal();</span><br><span class="line">        <span class="type">int</span> <span class="variable">index</span> <span class="operator">=</span> <span class="number">0</span>;</span><br><span class="line">        <span class="keyword">for</span> (ISqlSegment iSqlSegment : normal) &#123;</span><br><span class="line">            <span class="keyword">if</span> (index % <span class="number">4</span> == <span class="number">0</span>) &#123;</span><br><span class="line">                <span class="type">String</span> <span class="variable">field</span> <span class="operator">=</span> StrUtil.toCamelCase(iSqlSegment.getSqlSegment());</span><br><span class="line">                fields.add(field);</span><br><span class="line">            &#125;</span><br><span class="line">            index++;</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">return</span> fields.toArray(<span class="keyword">new</span> <span class="title class_">String</span>[]&#123;&#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><h1 id="文件工具类"><a href="#文件工具类" class="headerlink" title="文件工具类"></a>文件工具类</h1><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br><span class="line">80</span><br><span class="line">81</span><br><span class="line">82</span><br><span class="line">83</span><br><span class="line">84</span><br><span class="line">85</span><br><span class="line">86</span><br><span class="line">87</span><br><span class="line">88</span><br><span class="line">89</span><br><span class="line">90</span><br><span class="line">91</span><br><span class="line">92</span><br><span class="line">93</span><br><span class="line">94</span><br><span class="line">95</span><br><span class="line">96</span><br><span class="line">97</span><br><span class="line">98</span><br><span class="line">99</span><br><span class="line">100</span><br><span class="line">101</span><br><span class="line">102</span><br><span class="line">103</span><br><span class="line">104</span><br><span class="line">105</span><br><span class="line">106</span><br><span class="line">107</span><br><span class="line">108</span><br><span class="line">109</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">package</span> com.yww.filebackend.utils;</span><br><span class="line"></span><br><span class="line"><span class="keyword">import</span> cn.hutool.core.util.IdUtil;</span><br><span class="line"><span class="keyword">import</span> cn.hutool.core.util.StrUtil;</span><br><span class="line"><span class="keyword">import</span> com.yww.filebackend.entity.SysFile;</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.web.multipart.MultipartFile;</span><br><span class="line"></span><br><span class="line"><span class="keyword">import</span> java.io.File;</span><br><span class="line"><span class="keyword">import</span> java.io.IOException;</span><br><span class="line"><span class="keyword">import</span> java.io.InputStream;</span><br><span class="line"><span class="keyword">import</span> java.security.MessageDigest;</span><br><span class="line"><span class="keyword">import</span> java.security.NoSuchAlgorithmException;</span><br><span class="line"><span class="keyword">import</span> java.text.SimpleDateFormat;</span><br><span class="line"><span class="keyword">import</span> java.util.Date;</span><br><span class="line"></span><br><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * &lt;p&gt;</span></span><br><span class="line"><span class="comment"> *      文件工具类</span></span><br><span class="line"><span class="comment"> * &lt;/p&gt;</span></span><br><span class="line"><span class="comment"> *</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@author</span> yww</span></span><br><span class="line"><span class="comment"> */</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_">FileUtil</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">private</span> <span class="keyword">final</span> <span class="keyword">static</span> <span class="type">String</span> <span class="variable">FILE_SAVE_PATH</span> <span class="operator">=</span> <span class="string">&quot;D:\\file&quot;</span>;</span><br><span class="line"></span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     * 文件日期部分路径格式</span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">final</span> <span class="keyword">static</span> <span class="type">String</span> <span class="variable">DATE_FORMAT</span> <span class="operator">=</span> <span class="string">&quot;/yyyy/MM/dd/&quot;</span>;</span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     * windows下文件日期部分路径格式</span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">final</span> <span class="keyword">static</span> <span class="type">String</span> <span class="variable">DATE_FORMAT_WIN</span> <span class="operator">=</span> <span class="string">&quot;\\yyyy\\MM\\dd\\&quot;</span>;</span><br><span class="line"></span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     * 保存文件</span></span><br><span class="line"><span class="comment">     *</span></span><br><span class="line"><span class="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="keyword">public</span> <span class="keyword">static</span> SysFile <span class="title function_">saveFile</span><span class="params">(MultipartFile file)</span> &#123;</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">ext</span> <span class="operator">=</span> getExtName(originalFilename);</span><br><span class="line">        <span class="comment">// 生成新的文件名称</span></span><br><span class="line">        <span class="type">String</span> <span class="variable">newName</span> <span class="operator">=</span> IdUtil.fastSimpleUUID() + <span class="string">&quot;.&quot;</span> + ext;</span><br><span class="line">        <span class="type">String</span> <span class="variable">dateFormat</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">SimpleDateFormat</span>(getDateFormat()).format(<span class="keyword">new</span> <span class="title class_">Date</span>());</span><br><span class="line">        <span class="type">String</span> <span class="variable">path</span> <span class="operator">=</span> FILE_SAVE_PATH + dateFormat + newName;</span><br><span class="line">        <span class="type">File</span> <span class="variable">dest</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">File</span>(path);</span><br><span class="line">        <span class="comment">// 若是路径不存在就先创建父级目录</span></span><br><span class="line">        <span class="keyword">if</span> (!dest.exists()) &#123;</span><br><span class="line">            cn.hutool.core.io.FileUtil.touch(dest);</span><br><span class="line">        &#125;</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">            file.transferTo(dest);</span><br><span class="line">        &#125; <span class="keyword">catch</span> (IOException e) &#123;</span><br><span class="line">            log.warn(<span class="string">&quot;保存文件出错，保存的文件名称为：&quot;</span> + originalFilename);</span><br><span class="line">            log.warn(<span class="string">&quot;保存文件出错，保存的路径为：&quot;</span> + path);</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> SysFile.builder()</span><br><span class="line">                .fileName(originalFilename)</span><br><span class="line">                .path(path)</span><br><span class="line">                .size(file.getSize())</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">     * 参考自hutool的FileUtil.extName()方法</span></span><br><span class="line"><span class="comment">     *</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@param</span> fileName  文件名称</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> String <span class="title function_">getExtName</span><span class="params">(String fileName)</span> &#123;</span><br><span class="line">        <span class="keyword">if</span> (fileName == <span class="literal">null</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 class="type">int</span> <span class="variable">index</span> <span class="operator">=</span> fileName.lastIndexOf(<span class="string">&quot;.&quot;</span>);</span><br><span class="line">        <span class="keyword">if</span> (index == -<span class="number">1</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 class="comment">// 可能会有两个.符号</span></span><br><span class="line">        <span class="type">int</span> <span class="variable">secondToLastIndex</span> <span class="operator">=</span> fileName.substring(<span class="number">0</span>, index).lastIndexOf(<span class="string">&quot;.&quot;</span>);</span><br><span class="line">        <span class="keyword">return</span> fileName.substring(secondToLastIndex == -<span class="number">1</span> ? index + <span class="number">1</span> : secondToLastIndex + <span class="number">1</span>);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     * 获取日期格式</span></span><br><span 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> <span class="keyword">static</span> String <span class="title function_">getDateFormat</span><span class="params">()</span> &#123;</span><br><span class="line">        <span class="type">String</span> <span class="variable">osName</span> <span class="operator">=</span> System.getProperty(<span class="string">&quot;os.name&quot;</span>).toLowerCase();</span><br><span class="line">        <span class="keyword">if</span> (StrUtil.isNotBlank(osName) &amp;&amp; osName.contains(<span class="string">&quot;win&quot;</span>)) &#123;</span><br><span class="line">            <span class="keyword">return</span> DATE_FORMAT_WIN;</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">return</span> DATE_FORMAT;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>]]></content:encoded>
            
            
            <category domain="https://yww52.com/categories/%E6%88%91%E7%9A%84%E6%8A%80%E6%9C%AF%E5%AD%A6%E4%B9%A0%E7%89%A9%E8%AF%AD%E6%9E%9C%E7%84%B6%E6%9C%89%E9%97%AE%E9%A2%98/">我的技术学习物语果然有问题</category>
            
            <category domain="https://yww52.com/categories/%E6%88%91%E7%9A%84%E6%8A%80%E6%9C%AF%E5%AD%A6%E4%B9%A0%E7%89%A9%E8%AF%AD%E6%9E%9C%E7%84%B6%E6%9C%89%E9%97%AE%E9%A2%98/%E5%BC%80%E5%8F%91%E6%8A%80%E5%B7%A7/">开发技巧</category>
            
            
            
            <comments>https://yww52.com/posts/b0ab3ebd/#disqus_thread</comments>
            
        </item>
        
    </channel>
</rss>
