<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom">
  <title>CloudBillow</title>
  
  
  <link href="https://cloudbillow.cn/atom.xml" rel="self"/>
  
  <link href="https://cloudbillow.cn/"/>
  <updated>2024-06-14T03:16:00.000Z</updated>
  <id>https://cloudbillow.cn/</id>
  
  <author>
    <name>CloudBillow</name>
    
  </author>
  
  <generator uri="https://hexo.io/">Hexo</generator>
  
  <entry>
    <title>当OpenFeign遇到全局异常处理</title>
    <link href="https://cloudbillow.cn/2024/06/14/2024-06-14-%E5%BD%93OpenFeign%E9%81%87%E5%88%B0%E5%85%A8%E5%B1%80%E5%BC%82%E5%B8%B8%E5%A4%84%E7%90%86/"/>
    <id>https://cloudbillow.cn/2024/06/14/2024-06-14-%E5%BD%93OpenFeign%E9%81%87%E5%88%B0%E5%85%A8%E5%B1%80%E5%BC%82%E5%B8%B8%E5%A4%84%E7%90%86/</id>
    <published>2024-06-14T03:16:00.000Z</published>
    <updated>2024-06-14T03:16:00.000Z</updated>
    
    <content type="html"><![CDATA[<blockquote><p><strong>环境信息</strong>: <code>Java 8</code>、<code>Spring Boot 2.x</code>、<code>Spring Cloud 2021.x</code></p></blockquote><h1 id="引言"><a href="#引言" class="headerlink" title="引言"></a>引言</h1><h2 id="微服务中的异常传递"><a href="#微服务中的异常传递" class="headerlink" title="微服务中的异常传递"></a>微服务中的异常传递</h2><p>在使用Feign进行远程调用、开启了熔断降级且存在全局异常处理时，异常的传递就变得尤为重要。</p><p>在微服务架构中，服务之间的调用是通过网络进行的，因此在调用过程中可能会出现各种异常，如网络超时、服务不可用等。</p><p>为了保证服务的稳定性，我们需要对这些异常进行处理，以避免异常在服务之间传递，导致整个系统的不稳定。</p><h2 id="调用的几种情况"><a href="#调用的几种情况" class="headerlink" title="调用的几种情况"></a>调用的几种情况</h2><blockquote><p>假设此时A服务正在调用B服务，通常会涉及以下几种情况：</p></blockquote><ol><li>B服务成功返回</li><li>B服务抛出异常，被全局异常处理器拦截后正常返回，因为全局异常拦截会将异常封装为统一结果对象返回，此时是不会走降级逻辑的</li><li>B服务抛出异常，被全局异常处理器拦截后，继续抛出异常，此时会走降级逻辑并返回结果</li></ol><h1 id="详细分析"><a href="#详细分析" class="headerlink" title="详细分析"></a>详细分析</h1><h2 id="基础准备"><a href="#基础准备" class="headerlink" title="基础准备"></a>基础准备</h2><ul><li><p>创建一个SpringCloud项目，并引入OpenFeign、Sentinel或Hystrix依赖，以实现服务间的调用和降级。</p></li><li><p>自定义返回状态码</p></li></ul><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * 返回码</span></span><br><span class="line"><span class="comment"> *</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@author</span> XieYT</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="meta">@SuppressWarnings(&quot;unused&quot;)</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">enum</span> <span class="title class_">ReturnCode</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">    RC200(<span class="number">200</span>, <span class="string">&quot;成功&quot;</span>),</span><br><span class="line">    </span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     * 失败</span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    RC500(<span class="number">500</span>, <span class="string">&quot;网络异常&quot;</span>);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><ul><li>创建统一返回结果类，用于对返回结果进行封装，这里使用<code>R</code>类</li></ul><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br><span class="line">80</span><br><span class="line">81</span><br><span class="line">82</span><br><span class="line">83</span><br><span class="line">84</span><br><span class="line">85</span><br><span class="line">86</span><br><span class="line">87</span><br><span class="line">88</span><br><span class="line">89</span><br><span class="line">90</span><br><span class="line">91</span><br><span class="line">92</span><br><span class="line">93</span><br><span class="line">94</span><br><span class="line">95</span><br><span class="line">96</span><br><span class="line">97</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * 统一结果返回类</span></span><br><span class="line"><span class="comment"> *</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@author</span> XieYT</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">@Accessors(chain = true)</span></span><br><span class="line"><span class="meta">@SuppressWarnings(&quot;unused&quot;)</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">R</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> 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> 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> 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="type">long</span> timestamp;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">static</span> &lt;T&gt; R&lt;T&gt; <span class="title function_">success</span><span class="params">()</span> &#123;</span><br><span class="line">        <span class="keyword">return</span> R.&lt;T&gt;builder()</span><br><span class="line">                .code(ReturnCode.RC200.getCode())</span><br><span class="line">                .message(ReturnCode.RC200.getMessage())</span><br><span class="line">                .timestamp(System.currentTimeMillis())</span><br><span class="line">                .build();</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">static</span> &lt;T&gt; R&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> R.&lt;T&gt;builder()</span><br><span class="line">                .code(ReturnCode.RC200.getCode())</span><br><span class="line">                .message(ReturnCode.RC200.getMessage())</span><br><span class="line">                .data(data)</span><br><span class="line">                .timestamp(System.currentTimeMillis())</span><br><span class="line">                .build();</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     * 自定义消息的成功返回</span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">static</span> &lt;T&gt; R&lt;T&gt; <span class="title function_">success</span><span class="params">(T data, String message)</span> &#123;</span><br><span class="line">        <span class="keyword">return</span> R.&lt;T&gt;builder()</span><br><span class="line">                .code(ReturnCode.RC200.getCode())</span><br><span class="line">                .message(message)</span><br><span class="line">                .data(data)</span><br><span class="line">                .timestamp(System.currentTimeMillis())</span><br><span class="line">                .build();</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     * 失败</span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">static</span> &lt;T&gt; R&lt;T&gt; <span class="title function_">fail</span><span class="params">(Integer code, String message)</span> &#123;</span><br><span class="line">        <span class="keyword">return</span> R.&lt;T&gt;builder()</span><br><span class="line">                .code(code)</span><br><span class="line">                .message(message)</span><br><span class="line">                .timestamp(System.currentTimeMillis())</span><br><span class="line">                .build();</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     * 失败</span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">static</span> &lt;T&gt; R&lt;T&gt; <span class="title function_">fail</span><span class="params">(ReturnCode returnCode)</span> &#123;</span><br><span class="line">        <span class="keyword">return</span> R.&lt;T&gt;builder()</span><br><span class="line">                .code(returnCode.getCode())</span><br><span class="line">                .message(returnCode.getMessage())</span><br><span class="line">                .timestamp(System.currentTimeMillis())</span><br><span class="line">                .build();</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     * 失败</span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">static</span> &lt;T&gt; R&lt;T&gt; <span class="title function_">fail</span><span class="params">(ReturnCode returnCode, String message)</span> &#123;</span><br><span class="line">        <span class="keyword">return</span> R.&lt;T&gt;builder()</span><br><span class="line">                .code(returnCode.getCode())</span><br><span class="line">                .message(message)</span><br><span class="line">                .timestamp(System.currentTimeMillis())</span><br><span class="line">                .build();</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line"></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><ul><li>自定义业务异常类，用于抛出业务异常</li></ul><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/**</span></span><br><span class="line"><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> XieYT</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="meta">@SuppressWarnings(&quot;unused&quot;)</span></span><br><span class="line"><span class="meta">@Data</span></span><br><span class="line"><span class="meta">@EqualsAndHashCode(callSuper = true)</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">ServiceException</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="keyword">private</span> Integer code;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">private</span> String message;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">public</span> <span class="title function_">ServiceException</span><span class="params">(ReturnCode codeEnum)</span> &#123;</span><br><span class="line">        <span class="built_in">super</span>(codeEnum.getMessage());</span><br><span class="line">        <span class="built_in">this</span>.code = codeEnum.getCode();</span><br><span class="line">        <span class="built_in">this</span>.message = codeEnum.getMessage();</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_">ServiceException</span><span class="params">(Integer code, String message)</span> &#123;</span><br><span class="line">        <span class="built_in">super</span>(message);</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">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><ul><li>定义全局异常处理器</li></ul><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/**</span></span><br><span class="line"><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> XieYT</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_">GlobalExceptionHandler</span> &#123;</span><br><span class="line"></span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     * 业务异常</span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    <span class="meta">@ExceptionHandler(ServiceException.class)</span></span><br><span class="line">    <span class="keyword">public</span> R&lt;String&gt; <span class="title function_">serviceExceptionHandler</span><span class="params">(ServiceException e)</span> &#123;</span><br><span class="line">        log.error(<span class="string">&quot;业务异常: &#123;&#125; -&gt; &#123;&#125;&quot;</span>, Arrays.stream(e.getStackTrace()).findFirst().orElse(<span class="literal">null</span>), e.getMessage());</span><br><span class="line">        <span class="keyword">return</span> R.fail(e.getCode(), e.getMessage());</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>B服务成功返回</p></blockquote><p>无须特殊处理，正常返回即可。</p><h2 id="情况二"><a href="#情况二" class="headerlink" title="情况二"></a>情况二</h2><blockquote><p>B服务抛出异常，被全局异常处理器拦截后正常返回</p></blockquote><p>当B服务抛出异常时，全局异常处理器会拦截异常，并将异常封装为统一结果对象返回，此时是不会走降级逻辑的。</p><p>此时我们可以直接通过<code>R</code>类中的状态码来判断是否成功返回，如下：</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line">R&lt;String&gt; result = service.method();</span><br><span class="line"><span class="type">boolean</span> <span class="variable">isSuccess</span> <span class="operator">=</span> ReturnCode.RC200.getCode().equals(result.getCode());</span><br><span class="line"></span><br><span class="line"><span class="keyword">if</span> (isSuccess) &#123;</span><br><span class="line">    <span class="comment">// do something</span></span><br><span class="line">&#125; <span class="keyword">else</span> &#123;</span><br><span class="line">    <span class="comment">// 如果想要获取异常信息，可以通过result.getMessage()获取，从而抛出</span></span><br><span class="line">    <span class="keyword">throw</span> <span class="keyword">new</span> <span class="title class_">ServiceException</span>(result.getCode(), result.getMessage());</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>在上述代码中，我们先判断了是否成功返回，如果成功则继续执行，否则抛出异常。</p><p>抛出异常的操作可能会在多个方法中用到，我们不妨将其封装到<code>R</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></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">R</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><br><span class="line">    <span class="comment">/**</span></span><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">@JsonIgnore</span></span><br><span class="line">    <span class="keyword">public</span> T <span class="title function_">getCheckData</span><span class="params">()</span> &#123;</span><br><span class="line">        checkError();</span><br><span class="line">        <span class="keyword">return</span> data;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">void</span> <span class="title function_">checkError</span><span class="params">()</span> &#123;</span><br><span class="line">        <span class="keyword">if</span> (isSuccess()) &#123;</span><br><span class="line">            <span class="keyword">return</span>;</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">throw</span> <span class="keyword">new</span> <span class="title class_">ServiceException</span>(code, message);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">private</span> <span class="type">boolean</span> <span class="title function_">isSuccess</span><span class="params">()</span> &#123;</span><br><span class="line">        <span class="keyword">return</span> ReturnCode.RC200.getCode().equals(code);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>在加入上述代码后，我们就可以直接通过<code>getCheckData()</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="keyword">public</span> <span class="keyword">void</span> <span class="title function_">getDataFromOtherService</span><span class="params">()</span> &#123;</span><br><span class="line">    R&lt;String&gt; result = service.method();</span><br><span class="line">    <span class="comment">// 如果成功则返回，否则抛出异常并被全局异常处理器拦截</span></span><br><span class="line">    <span class="type">String</span> <span class="variable">data</span> <span class="operator">=</span> result.getCheckData();</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h2 id="情况三"><a href="#情况三" class="headerlink" title="情况三"></a>情况三</h2><blockquote><p>B服务抛出异常，被全局异常处理器拦截后，继续抛出异常</p></blockquote><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></pre></td><td class="code"><pre><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * Feign异常</span></span><br><span class="line"><span class="comment"> * &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></span><br><span class="line"><span class="comment"> * <span class="doctag">@author</span> XieYT</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="meta">@SuppressWarnings(&quot;unused&quot;)</span></span><br><span class="line"><span class="meta">@Data</span></span><br><span class="line"><span class="meta">@EqualsAndHashCode(callSuper = true)</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">FeignClientException</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="keyword">private</span> String message;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">public</span> <span class="title function_">FeignClientException</span><span class="params">(String message)</span> &#123;</span><br><span class="line">        <span class="built_in">super</span>(message);</span><br><span class="line">        <span class="built_in">this</span>.message = message;</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></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">GlobalExceptionHandler</span> &#123;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 省略其他代码...</span></span><br><span class="line">    </span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     * Feign异常处理</span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    <span class="meta">@ExceptionHandler(value = FeignClientException.class)</span></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">defaultExceptionHandler</span><span class="params">(FeignClientException e)</span> &#123;</span><br><span class="line">        <span class="comment">// 在这里直接将异常抛出，以便于走降级逻辑 </span></span><br><span class="line">        <span class="keyword">throw</span> e;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>此时我们在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></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> Service &#123;</span><br><span class="line">    <span class="keyword">public</span> String <span class="title function_">method</span><span class="params">()</span> &#123;</span><br><span class="line">        <span class="comment">// 省略其他代码...</span></span><br><span class="line">        <span class="keyword">throw</span> <span class="keyword">new</span> <span class="title class_">FeignClientException</span>(<span class="string">&quot;Feign调用异常, 该异常会被继续抛出!&quot;</span>);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>此时异常会被全局异常处理器拦截，然后直接抛出，从而走降级逻辑。</p><h1 id="总结"><a href="#总结" class="headerlink" title="总结"></a>总结</h1><p>在本文中，我们探讨了在使用Spring Cloud中的OpenFeign进行远程服务调用时如何处理异常，特别是当这些异常被全局异常处理器捕获时的情况。</p><p>通过实际代码示例，我们演示了如何在微服务架构中适当地传递和封装异常，以保持系统的稳定性和响应性。</p>]]></content>
    
    
      
      
    <summary type="html">&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;环境信息&lt;/strong&gt;: &lt;code&gt;Java 8&lt;/code&gt;、&lt;code&gt;Spring Boot 2.x&lt;/code&gt;、&lt;code&gt;Spring Cloud 2021.x&lt;/code&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;</summary>
      
    
    
    
    <category term="Java开发" scheme="https://cloudbillow.cn/categories/Java%E5%BC%80%E5%8F%91/"/>
    
    
    <category term="Spring Cloud" scheme="https://cloudbillow.cn/tags/Spring-Cloud/"/>
    
  </entry>
  
  <entry>
    <title>Spring框架中最佳依赖注入方式探讨</title>
    <link href="https://cloudbillow.cn/2024/05/24/2024-05-24-Spring%E6%A1%86%E6%9E%B6%E4%B8%AD%E6%9C%80%E4%BD%B3%E4%BE%9D%E8%B5%96%E6%B3%A8%E5%85%A5%E6%96%B9%E5%BC%8F%E6%8E%A2%E8%AE%A8/"/>
    <id>https://cloudbillow.cn/2024/05/24/2024-05-24-Spring%E6%A1%86%E6%9E%B6%E4%B8%AD%E6%9C%80%E4%BD%B3%E4%BE%9D%E8%B5%96%E6%B3%A8%E5%85%A5%E6%96%B9%E5%BC%8F%E6%8E%A2%E8%AE%A8/</id>
    <published>2024-05-24T12:23:23.000Z</published>
    <updated>2024-05-24T12:23:23.000Z</updated>
    
    <content type="html"><![CDATA[<h1 id="引言"><a href="#引言" class="headerlink" title="引言"></a>引言</h1><h2 id="什么是依赖注入？"><a href="#什么是依赖注入？" class="headerlink" title="什么是依赖注入？"></a>什么是依赖注入？</h2><p>依赖注入（Dependency Injection，简称DI）是一种设计模式，用于实现软件组件之间的松耦合。在面向对象编程中，依赖指一个对象需要另一个对象来完成某项任务。依赖注入的核心思想是，将对象所依赖的其他对象，通过外部传递给它，而不是让对象自己去创建或查找这些依赖。</p><h2 id="依赖注入的优点"><a href="#依赖注入的优点" class="headerlink" title="依赖注入的优点"></a>依赖注入的优点</h2><ol><li><strong>提高代码的可测试性</strong>：依赖注入使得单元测试更加容易，因为可以轻松地使用模拟对象替换真实依赖。</li><li><strong>增强组件的可重用性</strong>：通过依赖注入，可以轻松替换组件的实现，从而提高代码的可维护性和可扩展性。</li><li><strong>减少耦合度</strong>：依赖注入促进了松耦合设计，使得组件之间的依赖关系更加清晰和可管理。</li></ol><h2 id="Spring框架中的依赖注入"><a href="#Spring框架中的依赖注入" class="headerlink" title="Spring框架中的依赖注入"></a>Spring框架中的依赖注入</h2><p>Spring框架通过其IoC（Inversion of Control，控制反转）容器实现了强大的依赖注入功能，极大地简化了对象之间的依赖管理。 IoC容器是依赖注入的核心，负责创建、配置和管理对象的生命周期。容器通过读取配置文件或注解来识别和注入依赖对象。</p><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></pre></td><td class="code"><pre><span class="line"><span class="meta">@Component</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">MyService</span> &#123;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">final</span> MyRepository repository;</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_">MyService</span><span class="params">(MyRepository repository)</span> &#123;</span><br><span class="line">        <span class="built_in">this</span>.repository = repository;</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><strong>优点</strong>：</p><ul><li><strong>强制依赖</strong>：所有依赖在对象创建时必须提供，确保对象处于完整的初始化状态。</li><li><strong>不可变性</strong>：有助于创建不可变对象，增强线程安全性。</li><li><strong>简化测试</strong>：便于使用模拟对象进行单元测试。</li></ul><p><strong>缺点</strong>：</p><ul><li><strong>复杂性</strong>：当依赖较多时，构造函数参数列表可能变得冗长且难以维护。</li><li><strong>灵活性较低</strong>：所有依赖必须在对象创建时提供，不能在对象生命周期中动态修改。</li></ul><h2 id="Setter方法注入"><a href="#Setter方法注入" class="headerlink" title="Setter方法注入"></a>Setter方法注入</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="meta">@Component</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">MyService</span> &#123;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">private</span> MyRepository repository;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Autowired</span></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">setRepository</span><span class="params">(MyRepository repository)</span> &#123;</span><br><span class="line">        <span class="built_in">this</span>.repository = repository;</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><strong>优点</strong>：</p><ul><li><strong>灵活性</strong>：允许在对象创建后设置或更改依赖，适合可选依赖或需要后期配置的场景。</li><li><strong>可读性</strong>：Setter方法使代码更具可读性，特别是当依赖较多时。</li></ul><p><strong>缺点</strong>：</p><ul><li><strong>测试复杂性</strong>：需要分别设置每个依赖，增加测试复杂度。</li><li><strong>未设置依赖的风险</strong>：在对象使用前，可能会有未设置依赖的风险。</li></ul><h2 id="字段注入"><a href="#字段注入" class="headerlink" title="字段注入"></a>字段注入</h2><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">@Component</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">MyService</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> MyRepository repository;</span><br><span class="line">    </span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p><strong>优点</strong>：</p><ul><li><strong>简单直接</strong>：无需构造函数或Setter方法，直接在字段上使用注解，代码简洁。</li><li><strong>快速开发</strong>：适合快速原型开发，减少样板代码。</li></ul><p><strong>缺点</strong>：</p><ul><li><strong>难以测试</strong>：无法通过构造函数或Setter方法注入模拟对象，单元测试较为困难。</li><li><strong>隐式依赖</strong>：依赖关系不明显，可能导致维护困难。</li></ul><h1 id="总结"><a href="#总结" class="headerlink" title="总结"></a>总结</h1><table><thead><tr><th>注入方式</th><th>适用场景</th><th>不适用场景</th><th>最佳实践</th></tr></thead><tbody><tr><td>构造函数注入</td><td>强制依赖、不可变对象、单元测试</td><td>依赖项过多导致构造函数复杂</td><td>配合辅助类或设计模式，避免过长参数列表</td></tr><tr><td>Setter方法注入</td><td>可选依赖、后期配置</td><td>需要确保所有依赖在使用前已设置</td><td>使用初始化方法，确保必要依赖已设置</td></tr><tr><td>字段注入</td><td>快速开发、少量依赖</td><td>大型项目、高测试覆盖率</td><td>限制使用，考虑构造函数或Setter方法替代</td></tr></tbody></table>]]></content>
    
    
      
      
    <summary type="html">&lt;h1 id=&quot;引言&quot;&gt;&lt;a href=&quot;#引言&quot; class=&quot;headerlink&quot; title=&quot;引言&quot;&gt;&lt;/a&gt;引言&lt;/h1&gt;&lt;h2 id=&quot;什么是依赖注入？&quot;&gt;&lt;a href=&quot;#什么是依赖注入？&quot; class=&quot;headerlink&quot; title=&quot;什么是依赖注入？&quot;</summary>
      
    
    
    
    <category term="Java开发" scheme="https://cloudbillow.cn/categories/Java%E5%BC%80%E5%8F%91/"/>
    
    
    <category term="Spring" scheme="https://cloudbillow.cn/tags/Spring/"/>
    
  </entry>
  
  <entry>
    <title>PowerShell自定义函数</title>
    <link href="https://cloudbillow.cn/2024/04/22/2024-04-22-PowerShell%E8%87%AA%E5%AE%9A%E4%B9%89%E5%87%BD%E6%95%B0/"/>
    <id>https://cloudbillow.cn/2024/04/22/2024-04-22-PowerShell%E8%87%AA%E5%AE%9A%E4%B9%89%E5%87%BD%E6%95%B0/</id>
    <published>2024-04-21T16:36:22.000Z</published>
    <updated>2024-04-21T16:36:22.000Z</updated>
    
    <content type="html"><![CDATA[<h1 id="概述"><a href="#概述" class="headerlink" title="概述"></a>概述</h1><p>Windows PowerShell 是一种命令行脚本语言和脚本环境，它是 Windows 系统中的一个强大工具，可以帮助用户更高效地管理和操作系统。在 PowerShell 中，我们可以通过定义和调用函数来实现代码的复用和模块化。</p><p>本文将介绍如何在 PowerShell 中定义和调用函数。</p><h1 id="需求说明"><a href="#需求说明" class="headerlink" title="需求说明"></a>需求说明</h1><p>在使用Git时，在 PowerShell 中定义一个函数，实现对Git的http代理进行便捷的设置和取消。</p><blockquote><p>注意：该代理为http代理，而非ssh代理。</p></blockquote><h1 id="实现步骤"><a href="#实现步骤" class="headerlink" title="实现步骤"></a>实现步骤</h1><h2 id="定义函数"><a href="#定义函数" class="headerlink" title="定义函数"></a>定义函数</h2><p>在 PowerShell 中，我们可以通过 <code>function</code> 关键字来定义函数，函数的基本语法如下：</p><figure class="highlight shell"><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">function 函数名 &#123;</span><br><span class="line">    # 函数体</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>下面是一个设置和取消 Git 代理的函数的示例，请注意端口按需修改：</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><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">function gitProxy &#123;</span><br><span class="line">    param([string]$status)</span><br><span class="line">    switch ($status) &#123;</span><br><span class="line">        &quot;on&quot; &#123;</span><br><span class="line">            git config --global http.proxy &#x27;http://127.0.0.1:7890&#x27;</span><br><span class="line">            Write-Host &quot;Git proxy has been turned ON.&quot;</span><br><span class="line">        &#125;</span><br><span class="line">        &quot;off&quot; &#123;</span><br><span class="line">            git config --global --unset http.proxy</span><br><span class="line">            Write-Host &quot;Git proxy has been turned OFF.&quot;</span><br><span class="line">        &#125;</span><br><span class="line">        default &#123;</span><br><span class="line">            Write-Host &quot;Use &#x27;on&#x27; to turn on the proxy or &#x27;off&#x27; to turn off the proxy.&quot;</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>在上面的代码中，我们定义了一个名为 <code>gitProxy</code> 的函数，该函数接受一个参数 <code>$status</code>，根据参数的值来设置或取消 Git 代理。</p><p>我们只需要将上面的代码粘贴到 PowerShell 中并回车，就可以定义一个名为 <code>gitProxy</code> 的函数。</p><h2 id="调用函数"><a href="#调用函数" class="headerlink" title="调用函数"></a>调用函数</h2><p>定义好函数之后，我们可以通过函数名来调用函数，传入参数来执行函数的逻辑。</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">gitProxy on</span><br></pre></td></tr></table></figure><p>上面的代码调用了 <code>gitProxy</code> 函数，并传入了参数 <code>on</code>，表示要设置 Git 代理。</p><p>此时，我们可以查看 Git 的配置列表，可以看到 Git 代理已经被设置。</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">git config --global --list</span><br></pre></td></tr></table></figure><p><img src="/../img/posts/2024-04-22-PowerShell%E8%87%AA%E5%AE%9A%E4%B9%89%E5%87%BD%E6%95%B0/proxy-success.png" alt="代理设置成功.PNG"></p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">gitProxy off</span><br></pre></td></tr></table></figure><p>上面的代码调用了 <code>gitProxy</code> 函数，并传入了参数 <code>off</code>，表示要取消 Git 代理。</p><h2 id="持久化函数"><a href="#持久化函数" class="headerlink" title="持久化函数"></a>持久化函数</h2><p>上面的函数是临时定义的，只在当前 PowerShell 会话中有效。如果我们希望函数在每次启动 PowerShell 时都能使用，可以将函数定义保存到 PowerShell 配置文件中。</p><blockquote><p>注意：以下命令需要使用管理员权限启动PowerShell，由于该命令属于常用命项，依据Windows官方建议，将函数保存到<code>$PROFILE.AllUsersAllHosts</code>配置文件中。</p></blockquote><p>检查配置文件是否存在，如果不存在则创建配置文件：</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">if (!(Test-Path -Path $PROFILE.AllUsersAllHosts)) &#123; New-Item -Type File -Path $PROFILE.AllUsersAllHosts -Force &#125;</span><br></pre></td></tr></table></figure><p>打开并编辑配置文件：</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">notepad $USERPROFILE.AllUsersAllHosts</span><br></pre></td></tr></table></figure><p>在打开的配置文件中，我们可以将前面函数粘贴到文件末尾，保存并关闭文件。</p><p>关闭并重新打开新的 PowerShell 窗口，我们就可以直接调用 <code>gitProxy</code> 函数了。</p><h1 id="总结"><a href="#总结" class="headerlink" title="总结"></a>总结</h1><p>本文介绍了如何在 PowerShell 中通过 <code>function</code> 关键字定义函数，并通过函数名调用函数。同时介绍了如何将函数保存到 PowerShell 配置文件中，以便在每次启动 PowerShell 时都能使用。</p>]]></content>
    
    
      
      
    <summary type="html">&lt;h1 id=&quot;概述&quot;&gt;&lt;a href=&quot;#概述&quot; class=&quot;headerlink&quot; title=&quot;概述&quot;&gt;&lt;/a&gt;概述&lt;/h1&gt;&lt;p&gt;Windows PowerShell 是一种命令行脚本语言和脚本环境，它是 Windows 系统中的一个强大工具，可以帮助用户更高效地管理和</summary>
      
    
    
    
    <category term="Shell" scheme="https://cloudbillow.cn/categories/Shell/"/>
    
    
    <category term="PowerShell" scheme="https://cloudbillow.cn/tags/PowerShell/"/>
    
    <category term="Shell" scheme="https://cloudbillow.cn/tags/Shell/"/>
    
  </entry>
  
  <entry>
    <title>MySQL字段类型注意点</title>
    <link href="https://cloudbillow.cn/2024/01/22/2024-01-22-MySQL%E5%AD%97%E6%AE%B5%E7%B1%BB%E5%9E%8B%E6%B3%A8%E6%84%8F%E7%82%B9/"/>
    <id>https://cloudbillow.cn/2024/01/22/2024-01-22-MySQL%E5%AD%97%E6%AE%B5%E7%B1%BB%E5%9E%8B%E6%B3%A8%E6%84%8F%E7%82%B9/</id>
    <published>2024-01-22T02:32:23.000Z</published>
    <updated>2024-01-22T02:32:23.000Z</updated>
    
    <content type="html"><![CDATA[<h1 id="数字类型"><a href="#数字类型" class="headerlink" title="数字类型"></a>数字类型</h1><h2 id="整形类型"><a href="#整形类型" class="headerlink" title="整形类型"></a>整形类型</h2><p>整形中分为<code>signed有符号(默认)</code>和<code>unsigned无符号</code>。</p><p><img src="/../img/posts/2024-01-22-MySQL%E5%AD%97%E6%AE%B5%E7%B1%BB%E5%9E%8B%E6%B3%A8%E6%84%8F%E7%82%B9/type-number.png" alt="type-number.png"></p><ul><li><p>一般情况下不建议设置为无符号，因为会导致一些问题</p><blockquote><p>例如：MySQL要求设置为<code>unsigned</code>的两个值相减的结果仍然为<code>unsigned</code>，否则会导致抛出<code>[22001][1690] Data truncation: BIGINT UNSIGNED value is out of range in &#39;xxx&#39;</code>异常。<br>如果确实要使用，可以通过设置<code>SET sql_mode=&#39;NO_UNSIGNED_SUBTRACTION&#39;;</code>来关闭这个检查。</p></blockquote></li><li><p>自增类型作为主键时，务必要使用<code>BIGINT</code>，而不是<code>INT</code></p><blockquote><p>因为<code>INT</code>最大值为<code>2147483647</code>，很容易达到上限，达到上限后再次插入会导致主键冲突。</p></blockquote></li><li><p>MySQL8.0之前，自增整型会有回溯问题</p><blockquote><p>当删除ID为4的记录后，如果MySQL服务器重启，那么下一个插入的ID依旧可能为4</p></blockquote></li></ul><h2 id="浮点类型和高精度类型"><a href="#浮点类型和高精度类型" class="headerlink" title="浮点类型和高精度类型"></a>浮点类型和高精度类型</h2><ul><li><p>浮点类型：<code>FLOAT</code>和<code>DOUBLE</code>在存储时会有精度问题，且在MySQL后续版本中可能会被废弃，建议使用高精度<code>DECIMAL</code>类型</p><blockquote><p><code>DECIMAL</code>类型在存储时通过<code>DECIMAL(precision,scale)</code>来设置其长度和小数点后的位数。</p></blockquote></li><li><p>涉及到金额的字段，建议使用<code>BIGINT</code>整型类型，而不是高精度<code>DECIMAL</code>类型</p><blockquote><p>使用<code>DECIMAL</code>类型时，其长度不好确定，导致存储空间碎片化，而使用整型，其长度统一，便于数据库整理。<br><img src="/../img/posts/2024-01-22-MySQL%E5%AD%97%E6%AE%B5%E7%B1%BB%E5%9E%8B%E6%B3%A8%E6%84%8F%E7%82%B9/%E5%AD%98%E5%82%A8%E7%A9%BA%E9%97%B4%E7%A2%8E%E7%89%87%E5%8C%96.png" alt="type-number.png"><br>同时，整型类型的计算速度更快。</p></blockquote></li></ul><h1 id="字符串类型"><a href="#字符串类型" class="headerlink" title="字符串类型"></a>字符串类型</h1><ul><li><p>一般情况下，建议使用<code>VARCHAR</code>类型，而不是<code>CHAR</code>类型，因为对于变长字符集utf8mb4来说，底层都是变长存储</p></li><li><p>默认情况下，<code>VARCHAR</code>类型可以设置在最大字符数为<code>65535/所选字符集下一个字符存储的字节数</code></p><blockquote><p>例如：当选用<code>utf8mb4</code>字符集时，一个字符使用4个字节存储，当设置的长度超过<code>65535/4=16383</code>时，会抛出<code>［42000]［1074］ Column length too big for column &#39;xxx&#39; (max=16383); use BLOB or TEXT instead.</code>异常。</p></blockquote></li><li><p>建议将字符集设置为<code>utf8mb4</code>，以支持emoji表情等特殊字符</p></li><li><p>排序规则一般选用大小写不敏感的<code>utf8mb4_0900_ai_ci</code></p><blockquote><p>ci：大小写不敏感(默认)<br>cs：大小写敏感<br>ai：重音不敏感<br>as：重音敏感<br>bin：二进制排序</p></blockquote></li><li><p>修改表和字段字符集的命令</p><figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">ALTER</span> <span class="keyword">TABLE</span> `table_name` <span class="keyword">CONVERT</span> <span class="keyword">TO</span> <span class="type">CHARACTER</span> <span class="keyword">SET</span> utf8mb4 <span class="keyword">COLLATE</span> utf8mb4_0900_ai_ci;</span><br></pre></td></tr></table></figure></li><li><p>MySQL高版本中，性别字段建议使用<code>char(1)</code>类型，并通过check约束来限制其值</p><figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">CONSTRAINT</span> `user_chk_1` <span class="keyword">CHECK</span> (((`sex`<span class="operator">=</span>_utf8mb4<span class="string">&#x27;F&#x27;</span>) <span class="keyword">OR</span> (`sex`<span class="operator">=</span>_utf8mb4<span class="string">&#x27;M&#x27;</span>)))</span><br></pre></td></tr></table></figure></li><li><p>密码等隐私字段，建议通过<code>动态盐+动态加密算法</code>来加密存储</p></li></ul><h1 id="日期时间类型"><a href="#日期时间类型" class="headerlink" title="日期时间类型"></a>日期时间类型</h1><ul><li><p><code>TIMESTAMP</code>最大的优点是带有时区信息，如果业务需要不同国家的时间，建议使用<code>TIMESTAMP</code>类型</p></li><li><p><code>TIMESTAMP</code>类型存储上限为<code>2038-01-19 03:14:07</code>，需要注意潜在风险</p></li><li><p>使用<code>TIMESTAMP</code>类型时，建议显式设置时区，不要使用默认系统时区，会存在较大的性能问题</p><blockquote><p>推荐在配置文件中设置<code>time_zone=&#39;+8:00&#39;</code></p></blockquote></li><li><p>如无特殊需求，日期建议使用<code>DATETIME</code>类型，而不是<code>TIMESTAMP</code>和<code>INT</code>类型</p><blockquote><p>在使用时可以通过<code>DATETIME(N)</code>中的N来设置毫秒的精度，N的范围为0-6，默认为0<br>插入或更新时的默认时间可以通过<code>CURRENT_TIMESTAMP(N)</code>来设置<br><code>TIMESTAMP</code>如果设置毫秒数则占用7个字节，而<code>DATETIME</code>无论是否设置毫秒数都占用8个字节，所以在存储空间上几乎没有太大区别</p></blockquote></li></ul>]]></content>
    
    
      
      
    <summary type="html">&lt;h1 id=&quot;数字类型&quot;&gt;&lt;a href=&quot;#数字类型&quot; class=&quot;headerlink&quot; title=&quot;数字类型&quot;&gt;&lt;/a&gt;数字类型&lt;/h1&gt;&lt;h2 id=&quot;整形类型&quot;&gt;&lt;a href=&quot;#整形类型&quot; class=&quot;headerlink&quot; title=&quot;整形类型&quot;&gt;&lt;/a</summary>
      
    
    
    
    <category term="MySQL" scheme="https://cloudbillow.cn/categories/MySQL/"/>
    
    
    <category term="MySQL" scheme="https://cloudbillow.cn/tags/MySQL/"/>
    
  </entry>
  
  <entry>
    <title>自定义SpringBoot-Starter</title>
    <link href="https://cloudbillow.cn/2023/08/14/2023-08-14-%E8%87%AA%E5%AE%9A%E4%B9%89SpringBootStarter/"/>
    <id>https://cloudbillow.cn/2023/08/14/2023-08-14-%E8%87%AA%E5%AE%9A%E4%B9%89SpringBootStarter/</id>
    <published>2023-08-14T05:39:56.000Z</published>
    <updated>2023-08-14T05:39:56.000Z</updated>
    
    <content type="html"><![CDATA[<h1 id="概述"><a href="#概述" class="headerlink" title="概述"></a>概述</h1><h2 id="什么是-SpringBoot-Starter"><a href="#什么是-SpringBoot-Starter" class="headerlink" title="什么是 SpringBoot-Starter"></a>什么是 SpringBoot-Starter</h2><p>Spring Boot Starter 是 Spring Boot 框架中的一个重要概念，它是一种用于简化项目配置和依赖管理的方式。在 Spring Boot 中，应用程序通常需要引入许多依赖库和配置，以实现特定的功能或集成特定的技术栈。Spring Boot Starter 通过预定义的依赖和配置来简化这个过程，让开发人员能够更轻松地搭建和配置应用程序。</p><h2 id="为什么要自定义-Starter"><a href="#为什么要自定义-Starter" class="headerlink" title="为什么要自定义 Starter"></a>为什么要自定义 Starter</h2><ol><li>模块化和复用性： 自定义 Starter 允许你将通用功能封装为可重用的模块。这可以使你在多个项目中轻松地使用相同的功能，减少重复代码的编写。</li><li>内部标准化： 如果你的组织内部有一些通用的技术选型、配置和最佳实践，你可以创建自定义 Starter 来强制执行这些标准，从而提高代码的一致性和可维护性。</li><li>简化团队开发： 自定义 Starter 可以帮助团队成员更容易地使用相同的技术栈和配置，减少了新成员加入或跨项目切换时的学习曲线。</li><li>定制化需求： 你可以根据项目的特定需求创建自定义 Starter，将其与通用功能集成，从而更好地满足项目的独特要求。</li><li>隐藏复杂性： 如果你的项目需要集成多个组件或依赖项，你可以通过自定义 Starter 来隐藏底层的复杂性，提供一个更简洁的界面供开发人员使用。</li><li>提高开发效率： 自定义 Starter 可以减少项目配置的工作量，使开发人员能够更快速地启动和开发应用程序。</li><li>促进规范： 自定义 Starter 可以推动项目内部遵循特定的架构、模式和规范，从而促进代码质量和一致性。</li><li>对外开放： 如果你的项目提供一些可扩展的功能或插件，你可以将其封装为自定义 Starter，方便其他开发人员在他们的项目中使用。</li></ol><h2 id="命名规范"><a href="#命名规范" class="headerlink" title="命名规范"></a>命名规范</h2><ul><li><p>SpringBoot官方提供的Starter的命名规则：<code>spring-boot-starter-&#123;name&#125;</code>，其中<code>&#123;name&#125;</code>为自定义的 Starter 名称。</p></li><li><p>自定义Starter的命名规则：<code>&#123;name&#125;-spring-boot-starter</code>，其中<code>&#123;name&#125;</code>为自定义的 Starter 名称。</p></li></ul><h2 id="自动装配原理"><a href="#自动装配原理" class="headerlink" title="自动装配原理"></a>自动装配原理</h2><ul><li>Spring Boot 会扫描类路径下的<code>META-INF/spring.factories</code>文件，这个文件列出了可用的自动配置类。</li><li>配置类通过条件注解来判断这个配置类是否生效，如果生效，就会自动配置。例如：<code>@ConditionalOnClass</code>注解表示当类路径下有指定的类时，配置类才会生效。</li></ul><h1 id="如何自定义Starter"><a href="#如何自定义Starter" class="headerlink" title="如何自定义Starter"></a>如何自定义Starter</h1><h2 id="创建-Maven-项目"><a href="#创建-Maven-项目" class="headerlink" title="创建 Maven 项目"></a>创建 Maven 项目</h2><p> 新建一个空的 Maven 项目：</p><p><img src="/../img/posts/2023-08-14-%E8%87%AA%E5%AE%9A%E4%B9%89SpringBootStarter/new-maven-project.png" alt="img.png"></p><p>Spring Boot Starter 项目无需启动类，且通常不包含任何代码，只包含一些依赖和配置。</p><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><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">&lt;?xml version=<span class="string">&quot;1.0&quot;</span> encoding=<span class="string">&quot;UTF-8&quot;</span>?&gt;</span></span><br><span class="line"><span class="tag">&lt;<span class="name">project</span> <span class="attr">xmlns</span>=<span class="string">&quot;http://maven.apache.org/POM/4.0.0&quot;</span></span></span><br><span class="line"><span class="tag">         <span class="attr">xmlns:xsi</span>=<span class="string">&quot;http://www.w3.org/2001/XMLSchema-instance&quot;</span></span></span><br><span class="line"><span class="tag">         <span class="attr">xsi:schemaLocation</span>=<span class="string">&quot;http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd&quot;</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">modelVersion</span>&gt;</span>4.0.0<span class="tag">&lt;/<span class="name">modelVersion</span>&gt;</span></span><br><span class="line"></span><br><span class="line">    <span class="tag">&lt;<span class="name">parent</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">groupId</span>&gt;</span>org.springframework.boot<span class="tag">&lt;/<span class="name">groupId</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">artifactId</span>&gt;</span>spring-boot-starter-parent<span class="tag">&lt;/<span class="name">artifactId</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">version</span>&gt;</span>2.7.14<span class="tag">&lt;/<span class="name">version</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">relativePath</span>/&gt;</span></span><br><span class="line">    <span class="tag">&lt;/<span class="name">parent</span>&gt;</span></span><br><span class="line"></span><br><span class="line">    <span class="tag">&lt;<span class="name">groupId</span>&gt;</span>com.cloud.billow<span class="tag">&lt;/<span class="name">groupId</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">artifactId</span>&gt;</span>notify-spring-boot-starter<span class="tag">&lt;/<span class="name">artifactId</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">version</span>&gt;</span>1.0.0-SNAPSHOT<span class="tag">&lt;/<span class="name">version</span>&gt;</span></span><br><span class="line"></span><br><span class="line">    <span class="tag">&lt;<span class="name">properties</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">maven.compiler.source</span>&gt;</span>8<span class="tag">&lt;/<span class="name">maven.compiler.source</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">maven.compiler.target</span>&gt;</span>8<span class="tag">&lt;/<span class="name">maven.compiler.target</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">project.build.sourceEncoding</span>&gt;</span>UTF-8<span class="tag">&lt;/<span class="name">project.build.sourceEncoding</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;/<span class="name">properties</span>&gt;</span></span><br><span class="line"></span><br><span class="line">    <span class="tag">&lt;<span class="name">dependencies</span>&gt;</span></span><br><span class="line"></span><br><span class="line">        <span class="comment">&lt;!-- Spring Boot 自动配置 --&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">dependency</span>&gt;</span></span><br><span class="line">            <span class="tag">&lt;<span class="name">groupId</span>&gt;</span>org.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-autoconfigure<span class="tag">&lt;/<span class="name">artifactId</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;/<span class="name">dependency</span>&gt;</span></span><br><span class="line"></span><br><span class="line">        <span class="comment">&lt;!-- Spring Boot 配置处理器 --&gt;</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-configuration-processor<span class="tag">&lt;/<span class="name">artifactId</span>&gt;</span></span><br><span class="line">            <span class="tag">&lt;<span class="name">optional</span>&gt;</span>true<span class="tag">&lt;/<span class="name">optional</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;/<span class="name">dependency</span>&gt;</span></span><br><span class="line"></span><br><span class="line">        <span class="comment">&lt;!-- 其他依赖项 --&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">dependency</span>&gt;</span></span><br><span class="line">            <span class="tag">&lt;<span class="name">groupId</span>&gt;</span>org.projectlombok<span class="tag">&lt;/<span class="name">groupId</span>&gt;</span></span><br><span class="line">            <span class="tag">&lt;<span class="name">artifactId</span>&gt;</span>lombok<span class="tag">&lt;/<span class="name">artifactId</span>&gt;</span></span><br><span class="line">            <span class="tag">&lt;<span class="name">optional</span>&gt;</span>true<span class="tag">&lt;/<span class="name">optional</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;/<span class="name">dependency</span>&gt;</span></span><br><span class="line"></span><br><span class="line">    <span class="tag">&lt;/<span class="name">dependencies</span>&gt;</span></span><br><span class="line"></span><br><span class="line"><span class="tag">&lt;/<span class="name">project</span>&gt;</span></span><br></pre></td></tr></table></figure><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></pre></td><td class="code"><pre><span class="line"><span class="comment">/**</span></span><br><span class="line"><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> CloudBillow</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@since</span> 2023/08/14 14:29</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">@EnableConfigurationProperties(NotifyProperties.class)</span></span><br><span class="line"><span class="meta">@ConfigurationProperties(prefix = NotifyProperties.NOTIFY_PREFIX)</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">NotifyProperties</span> &#123;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">final</span> <span class="type">String</span> <span class="variable">NOTIFY_PREFIX</span> <span class="operator">=</span> <span class="string">&quot;notify&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">private</span> String from;</span><br><span class="line"></span><br><span class="line">    <span class="comment">/**</span></span><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 to;</span><br><span class="line"></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h2 id="编写-Service-类"><a href="#编写-Service-类" class="headerlink" title="编写 Service 类"></a>编写 Service 类</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="comment">/**</span></span><br><span class="line"><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> CloudBillow</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@since</span> 2023/08/14 14:34</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_">WXNotifyService</span> <span class="keyword">implements</span> <span class="title class_">NotifyService</span> &#123;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">private</span> NotifyProperties notifyProperties;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="keyword">public</span> <span class="type">boolean</span> <span class="title function_">sendNotify</span><span class="params">(String content)</span> &#123;</span><br><span class="line">        <span class="keyword">try</span> &#123;</span><br><span class="line">            <span class="type">String</span> <span class="variable">from</span> <span class="operator">=</span> notifyProperties.getFrom();</span><br><span class="line">            <span class="type">String</span> <span class="variable">to</span> <span class="operator">=</span> notifyProperties.getTo();</span><br><span class="line">            System.out.println(<span class="string">&quot;from: &quot;</span> + from + <span class="string">&quot;, to: &quot;</span> + to + <span class="string">&quot;, content: &quot;</span> + content);</span><br><span class="line">        &#125; <span class="keyword">catch</span> (Exception e) &#123;</span><br><span class="line">            <span class="keyword">return</span> <span class="literal">false</span>;</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">return</span> <span class="literal">true</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><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * 通知自动配置类</span></span><br><span class="line"><span class="comment"> *</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@author</span> CloudBillow</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@since</span> 2023/08/14 14:32</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="meta">@Configuration(proxyBeanMethods = false)</span></span><br><span class="line"><span class="meta">@ConditionalOnClass(name = &quot;com.cloud.billow.properties.NotifyProperties&quot;)</span></span><br><span class="line"><span class="meta">@EnableConfigurationProperties(NotifyProperties.class)</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">NotifyAutoConfiguration</span> <span class="keyword">implements</span> <span class="title class_">InitializingBean</span> &#123;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">final</span> NotifyProperties notifyProperties;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">public</span> <span class="title function_">NotifyAutoConfiguration</span><span class="params">(NotifyProperties notifyProperties)</span> &#123;</span><br><span class="line">        <span class="built_in">this</span>.notifyProperties = notifyProperties;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Bean</span></span><br><span class="line">    <span class="meta">@ConditionalOnMissingBean</span></span><br><span class="line">    <span class="keyword">public</span> NotifyService <span class="title function_">notifyService</span><span class="params">()</span> &#123;</span><br><span class="line">        <span class="type">WXNotifyService</span> <span class="variable">wxNotifyService</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">WXNotifyService</span>();</span><br><span class="line">        wxNotifyService.setNotifyProperties(notifyProperties);</span><br><span class="line">        <span class="keyword">return</span> wxNotifyService;</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_">afterPropertiesSet</span><span class="params">()</span> <span class="keyword">throws</span> Exception &#123;</span><br><span class="line">        <span class="comment">// 校验参数</span></span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h2 id="编写-spring-factories-文件"><a href="#编写-spring-factories-文件" class="headerlink" title="编写 spring.factories 文件"></a>编写 spring.factories 文件</h2><p>在<code>src/main/resources</code>目录下创建<code>META-INF/spring.factories</code>文件，内容如下：</p><figure class="highlight properties"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="attr">org.springframework.boot.autoconfigure.EnableAutoConfiguration</span>=<span class="string">\</span></span><br><span class="line"><span class="string">  com.cloud.billow.configuration.NotifyAutoConfiguration</span></span><br></pre></td></tr></table></figure><h2 id="使用Maven打包"><a href="#使用Maven打包" class="headerlink" title="使用Maven打包"></a>使用Maven打包</h2><p><img src="/../img/posts/2023-08-14-%E8%87%AA%E5%AE%9A%E4%B9%89SpringBootStarter/install-maven.png" alt="install-maven.png"></p><h1 id="使用自定义-Starter"><a href="#使用自定义-Starter" class="headerlink" title="使用自定义 Starter"></a>使用自定义 Starter</h1><p>引入自定义 Starter 依赖：</p><figure class="highlight xml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="tag">&lt;<span class="name">dependency</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">groupId</span>&gt;</span>com.cloud.billow<span class="tag">&lt;/<span class="name">groupId</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">artifactId</span>&gt;</span>notify-spring-boot-starter<span class="tag">&lt;/<span class="name">artifactId</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">version</span>&gt;</span>1.0.0-SNAPSHOT<span class="tag">&lt;/<span class="name">version</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;/<span class="name">dependency</span>&gt;</span></span><br></pre></td></tr></table></figure><p>配置自定义 Starter：</p><figure class="highlight yaml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="attr">notify:</span></span><br><span class="line">  <span class="attr">to:</span> <span class="string">小明</span></span><br><span class="line">  <span class="attr">from:</span> <span class="string">小红</span></span><br></pre></td></tr></table></figure><blockquote><p>由于引入了<code>configuration-processor</code>依赖，所以在配置文件中输入<code>notify</code>前缀时，会有提示，如下图所示<br><img src="/../img/posts/2023-08-14-%E8%87%AA%E5%AE%9A%E4%B9%89SpringBootStarter/starter-config.png" alt="starter-config.png"></p></blockquote><p>使用自定义 Starter：</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">@GetMapping(value = &quot;/test&quot;)</span></span><br><span class="line"><span class="keyword">public</span> String <span class="title function_">test</span><span class="params">()</span> &#123;</span><br><span class="line">    <span class="type">boolean</span> <span class="variable">isSuccess</span> <span class="operator">=</span> notifyService.sendNotify(<span class="string">&quot;这个是消息的内容&quot;</span>);</span><br><span class="line">    <span class="keyword">return</span> String.valueOf(isSuccess);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>输出</p><p><img src="/../img/posts/2023-08-14-%E8%87%AA%E5%AE%9A%E4%B9%89SpringBootStarter/output.png" alt="output.png"></p>]]></content>
    
    
      
      
    <summary type="html">&lt;h1 id=&quot;概述&quot;&gt;&lt;a href=&quot;#概述&quot; class=&quot;headerlink&quot; title=&quot;概述&quot;&gt;&lt;/a&gt;概述&lt;/h1&gt;&lt;h2 id=&quot;什么是-SpringBoot-Starter&quot;&gt;&lt;a href=&quot;#什么是-SpringBoot-Starter&quot; class=&quot;</summary>
      
    
    
    
    <category term="Java开发" scheme="https://cloudbillow.cn/categories/Java%E5%BC%80%E5%8F%91/"/>
    
    
    <category term="Java" scheme="https://cloudbillow.cn/tags/Java/"/>
    
    <category term="Spring Boot" scheme="https://cloudbillow.cn/tags/Spring-Boot/"/>
    
  </entry>
  
  <entry>
    <title>将项目打包成Docker</title>
    <link href="https://cloudbillow.cn/2023/04/11/2023-04-11-%E5%B0%86%E9%A1%B9%E7%9B%AE%E6%89%93%E5%8C%85%E6%88%90Docker/"/>
    <id>https://cloudbillow.cn/2023/04/11/2023-04-11-%E5%B0%86%E9%A1%B9%E7%9B%AE%E6%89%93%E5%8C%85%E6%88%90Docker/</id>
    <published>2023-04-11T11:47:56.000Z</published>
    <updated>2023-04-11T11:47:56.000Z</updated>
    
    <content type="html"><![CDATA[<h1 id="概述"><a href="#概述" class="headerlink" title="概述"></a>概述</h1><p>对SpringBoot应用程序进行Docker容器化是软件开发者的一项基本技能，因为它可以在容器化环境中高效部署和管理应用程序。同时将SpringBoot应用打包到Docker容器中可以确保跨平台的一致性且便于后期扩展。</p><h1 id="准备工作"><a href="#准备工作" class="headerlink" title="准备工作"></a>准备工作</h1><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></pre></td><td class="code"><pre><span class="line"><span class="meta">@RestController</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">HelloController</span> &#123;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@GetMapping(&quot;/hello&quot;)</span></span><br><span class="line">    <span class="keyword">public</span> String <span class="title function_">hello</span><span class="params">()</span> &#123;</span><br><span class="line">        <span class="keyword">return</span> <span class="string">&quot;Hello Docker!&quot;</span>;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>启动项目，访问<code>http://localhost:8080/hello</code>，确保接口可以正确响应。</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta prompt_">$ </span><span class="language-bash">mvn clean spring-boot:run</span></span><br></pre></td></tr></table></figure><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta prompt_">$ </span><span class="language-bash">curl -v localhost:8080/hello</span></span><br></pre></td></tr></table></figure><p>执行上述命令后将看到如下输出：</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><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">curl -v localhost:8080/hello</span><br><span class="line">*   Trying [::1]:8080...</span><br><span class="line">* Connected to localhost (::1) port 8080</span><br><span class="line"><span class="meta prompt_">&gt; </span><span class="language-bash">GET /hello HTTP/1.1</span></span><br><span class="line"><span class="meta prompt_">&gt; </span><span class="language-bash">Host: localhost:8080</span></span><br><span class="line"><span class="meta prompt_">&gt; </span><span class="language-bash">User-Agent: curl/8.4.0</span></span><br><span class="line"><span class="meta prompt_">&gt; </span><span class="language-bash">Accept: */*</span></span><br><span class="line"><span class="meta prompt_">&gt; </span><span class="language-bash"></span></span><br><span class="line"><span class="language-bash">&lt; HTTP/1.1 200</span> </span><br><span class="line">&lt; Content-Type: text/plain;charset=UTF-8</span><br><span class="line">&lt; Content-Length: 13</span><br><span class="line">&lt; Date: Mon, 15 Apr 2024 06:56:56 GMT</span><br><span class="line">&lt; </span><br><span class="line">* Connection #0 to host localhost left intact</span><br><span class="line">Hello Docker!%</span><br></pre></td></tr></table></figure><h1 id="添加Dockerfile配置文件"><a href="#添加Dockerfile配置文件" class="headerlink" title="添加Dockerfile配置文件"></a>添加Dockerfile配置文件</h1><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></pre></td><td class="code"><pre><span class="line"><span class="keyword">FROM</span> openjdk:<span class="number">17</span></span><br><span class="line"><span class="keyword">VOLUME</span><span class="language-bash"> /tmp</span></span><br><span class="line"><span class="keyword">EXPOSE</span> <span class="number">8080</span></span><br><span class="line"><span class="keyword">ARG</span> JAR_FILE=target/spring-boot-to-docker-<span class="number">0.0</span>.<span class="number">1</span>-SNAPSHOT.jar</span><br><span class="line"><span class="keyword">ADD</span><span class="language-bash"> <span class="variable">$&#123;JAR_FILE&#125;</span> app.jar</span></span><br><span class="line"><span class="keyword">ENTRYPOINT</span><span class="language-bash"> [<span class="string">&quot;java&quot;</span>,<span class="string">&quot;-jar&quot;</span>,<span class="string">&quot;/app.jar&quot;</span>]</span></span><br></pre></td></tr></table></figure><p>下面是配置的简单说明：</p><ul><li><code>FROM openjdk:17</code>：使用<code>openjdk:17</code>作为基础镜像。</li><li><code>VOLUME /tmp</code>：创建一个&#x2F;tmp目录作为容器的临时目录。</li><li><code>EXPOSE 8080</code>：容器运行时监听的端口。</li><li><code>ARG JAR_FILE=target/spring-boot-docker.jar</code>：定义一个变量<code>JAR_FILE</code>，用于指定要添加到容器中的jar文件。</li><li><code>ADD $&#123;JAR_FILE&#125; app.jar</code>：将<code>JAR_FILE</code>指定的jar文件添加到容器中，并重命名为<code>app.jar</code>。</li><li><code>ENTRYPOINT [&quot;java&quot;,&quot;-jar&quot;,&quot;/app.jar&quot;]</code>：指定容器启动时执行的命令。</li></ul><h1 id="生成jar包"><a href="#生成jar包" class="headerlink" title="生成jar包"></a>生成jar包</h1><p>通过<code>maven</code>命令生成jar包：</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta prompt_">$ </span><span class="language-bash">mvn clean package</span></span><br></pre></td></tr></table></figure><p>此时在<code>target</code>目录下会生成一个<code>spring-boot-to-docker-0.0.1-SNAPSHOT.jar</code>文件。</p><h1 id="构建Docker镜像"><a href="#构建Docker镜像" class="headerlink" title="构建Docker镜像"></a>构建Docker镜像</h1><p>确保已经安装了<code>Docker</code>，然后在项目根目录下执行下面的命令构建Docker镜像：</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">docker build -t spring-boot-to-docker:spring-docker .</span><br></pre></td></tr></table></figure><p><strong>build</strong> 命令将通过我们在 Dockerfile 中的配置构建一个镜像，而 <strong>-t</strong> 标志则用于为我们的镜像添加一个标签。</p><p>等待几分钟后，我们可以通过<code>docker image ls | grep spring-boot-to-docker</code>命令查看镜像是否创建成功。</p><p><img src="/../img/posts/2023-04-11-%E5%B0%86%E9%A1%B9%E7%9B%AE%E6%89%93%E5%8C%85%E6%88%90Docker/docker-image-ls.png" alt="docker-image-ls.png"></p><h1 id="运行Docker容器"><a href="#运行Docker容器" class="headerlink" title="运行Docker容器"></a>运行Docker容器</h1><p>运行下面的命令启动Docker容器：</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">docker run -p 9090:8080 spring-boot-to-docker:spring-docker</span><br></pre></td></tr></table></figure><p><strong>run</strong> 命令用于启动一个容器，而 <strong>-p</strong> 标志则用于将容器的端口映射到主机的端口。</p><p>运行成功后，可以通过<code>http://localhost:9090/hello</code>访问接口，确保接口可以正确响应。</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta prompt_">$ </span><span class="language-bash">curl -v localhost:9090/hello</span></span><br></pre></td></tr></table></figure><p><img src="/../img/posts/2023-04-11-%E5%B0%86%E9%A1%B9%E7%9B%AE%E6%89%93%E5%8C%85%E6%88%90Docker/run-docker.png" alt="run-docker.png"></p>]]></content>
    
    
      
      
    <summary type="html">&lt;h1 id=&quot;概述&quot;&gt;&lt;a href=&quot;#概述&quot; class=&quot;headerlink&quot; title=&quot;概述&quot;&gt;&lt;/a&gt;概述&lt;/h1&gt;&lt;p&gt;对SpringBoot应用程序进行Docker容器化是软件开发者的一项基本技能，因为它可以在容器化环境中高效部署和管理应用程序。同时将Spr</summary>
      
    
    
    
    <category term="Java开发" scheme="https://cloudbillow.cn/categories/Java%E5%BC%80%E5%8F%91/"/>
    
    
    <category term="Spring Boot" scheme="https://cloudbillow.cn/tags/Spring-Boot/"/>
    
    <category term="Docker" scheme="https://cloudbillow.cn/tags/Docker/"/>
    
  </entry>
  
  <entry>
    <title>CompletableFuture的使用</title>
    <link href="https://cloudbillow.cn/2021/08/10/2021-08-10-CompletableFuture%E7%9A%84%E4%BD%BF%E7%94%A8/"/>
    <id>https://cloudbillow.cn/2021/08/10/2021-08-10-CompletableFuture%E7%9A%84%E4%BD%BF%E7%94%A8/</id>
    <published>2021-08-10T04:17:39.000Z</published>
    <updated>2021-08-10T04:17:39.000Z</updated>
    
    <content type="html"><![CDATA[<blockquote><p>CompletableFuture类提供了非常强大的Future的扩展功能，可以帮助我们简化异步编程的复杂性，提供了函数式编程的能力，可以通过回调的方式处理计算结果，并且提供了转换和组合CompletableFuture的方法。</p></blockquote><h1 id="总结"><a href="#总结" class="headerlink" title="总结"></a>总结</h1><table>    <tr>        <th>分类</th>        <th>方法名</th>        <th>描述</th>    </tr>    <tr>        <th rowspan="2">创建异步任务</th>        <td><code>runAsync</code></td>        <td>不支持返回值</td>    </tr>    <tr>        <td><code>supplyAsync</code></td>        <td>支持返回值</td>    </tr>    <tr>        <th rowspan="6">创建异步任务</th>        <td><code>thenRun</code>、<code>thenRunAsync</code></td>        <td>不接参数、无返回值</td>    </tr>    <tr>        <td><code>thenAccept</code>、<code>thenAcceptAsync</code></td>        <td>接返回值、无返回值</td>    </tr>    <tr>        <td><code>thenApply</code>、<code>thenApplyAsync</code></td>        <td>接返回值、有返回值</td>    </tr>    <tr>        <td><code>exceptionally</code>、<code>exceptionallyAsync</code></td>        <td>接异常、有返回值</td>    </tr>    <tr>        <td><code>whenComplete</code>、<code>whenCompleteAsync</code></td>        <td>接返回值和异常、无返回值</td>    </tr>    <tr>        <td><code>handle</code>、<code>handleAsync</code></td>        <td>接返回值和异常、有返回值</td>    </tr>    <tr>        <th rowspan="7">两个任务组合</th>        <td><code>thenCompose</code>、<code>thenComposeAsync</code></td>        <td>任务转化</td>    </tr>    <tr>        <td><code>thenCombine</code>、<code>thenCombineAsync</code></td>        <td>“且”组合、接返回值、有返回值</td>    </tr>    <tr>        <td><code>thenAcceptBoth</code>、<code>thenAcceptBothAsync</code></td>        <td>“且”组合、接返回值、无返回值</td>    </tr>    <tr>        <td><code>runAfterBoth</code>、<code>runAfterBothAsync</code></td>        <td>“且”组合、不接参数、无返回值</td>    </tr>    <tr>        <td><code>applyToEither</code>、<code>applyToEitherAsync</code></td>        <td>“或”组合、接返回值、有返回值</td>    </tr>    <tr>        <td><code>acceptEither</code>、<code>acceptEitherAsync</code></td>        <td>“或”组合、接返回值、无返回值</td>    </tr>    <tr>        <td><code>runAfterEither</code>、<code>runAfterEitherAsync</code></td>        <td>“或”组合、不接参数、无返回值</td>    </tr>    <tr>        <th rowspan="2">多个任务组合</th>        <td><code>anyOf</code></td>        <td>“任意一个”组合</td>    </tr>    <tr>        <td><code>allOf</code></td>        <td>“所有”组合</td>    </tr></table><blockquote><p>在CompletableFuture中如果使用没有指定线程池的方法，则会使用<code>ForkJoinPool.commonPool()</code>作为它的线程池执行异步代码。</p></blockquote><h1 id="创建异步任务"><a href="#创建异步任务" class="headerlink" title="创建异步任务"></a>创建异步任务</h1><h3 id="不支持返回值"><a href="#不支持返回值" class="headerlink" title="不支持返回值"></a>不支持返回值</h3><ul><li>方法签名<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 使用默认线程池中的线程</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">static</span> CompletableFuture&lt;Void&gt; <span class="title function_">runAsync</span><span class="params">(Runnable runnable)</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> CompletableFuture&lt;Void&gt; <span class="title function_">runAsync</span><span class="params">(Runnable runnable, Executor executor)</span>; </span><br></pre></td></tr></table></figure></li><li>示例代码<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">CompletableFuture.runAsync(() -&gt; &#123;</span><br><span class="line">    <span class="comment">// TODO 执行任务</span></span><br><span class="line">&#125;);</span><br></pre></td></tr></table></figure></li></ul><h3 id="支持返回值"><a href="#支持返回值" class="headerlink" title="支持返回值"></a>支持返回值</h3><ul><li><p>方法签名</p><figure class="highlight java"><table><tr><td class="gutter"><pre><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="keyword">public</span> <span class="keyword">static</span> &lt;U&gt; CompletableFuture&lt;U&gt; <span class="title function_">supplyAsync</span><span class="params">(Supplier&lt;U&gt; supplier)</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;U&gt; CompletableFuture&lt;U&gt; <span class="title function_">supplyAsync</span><span class="params">(Supplier&lt;U&gt; supplier, Executor executor)</span>; </span><br></pre></td></tr></table></figure></li><li><p>示例代码</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line">CompletableFuture.supplyAsync(() -&gt; &#123;</span><br><span class="line">    <span class="comment">// TODO 执行任务</span></span><br><span class="line">    <span class="keyword">return</span> <span class="literal">null</span>;</span><br><span class="line">&#125;);</span><br></pre></td></tr></table></figure></li></ul><h1 id="任务完成时回调方法"><a href="#任务完成时回调方法" class="headerlink" title="任务完成时回调方法"></a>任务完成时回调方法</h1><p>当CompletableFuture任务执行完成或者抛出异常的时候，可以执行特定的回调方法。</p><h3 id="不接参数、无返回值"><a href="#不接参数、无返回值" class="headerlink" title="不接参数、无返回值"></a>不接参数、无返回值</h3><blockquote><p>前一个任务执行完成后执行；任务之间<strong>无参数传递</strong>，回调方法<strong>无返回值</strong></p></blockquote><ul><li>方法签名<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 使用调用它的线程上运行（一般是主线程）</span></span><br><span class="line"><span class="keyword">public</span> CompletableFuture&lt;Void&gt; <span class="title function_">thenRun</span><span class="params">(Runnable action)</span>;</span><br><span class="line"><span class="comment">// 使用默认线程池中的线程</span></span><br><span class="line"><span class="keyword">public</span> CompletableFuture&lt;Void&gt; <span class="title function_">thenRunAsync</span><span class="params">(Runnable action)</span>;</span><br><span class="line"><span class="comment">// 使用自定义线程池中的线程</span></span><br><span class="line"><span class="keyword">public</span> CompletableFuture&lt;Void&gt; <span class="title function_">thenRunAsync</span><span class="params">(Runnable action, Executor executor)</span>;</span><br></pre></td></tr></table></figure></li><li>示例代码<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line">CompletableFuture.runAsync(() -&gt; &#123;</span><br><span class="line">&#125;).thenRun(() -&gt; &#123;</span><br><span class="line">    <span class="comment">// TODO 执行任务</span></span><br><span class="line">&#125;);</span><br></pre></td></tr></table></figure></li></ul><h3 id="接返回值、无返回值"><a href="#接返回值、无返回值" class="headerlink" title="接返回值、无返回值"></a>接返回值、无返回值</h3><blockquote><p>前一个任务执行完成后执行；前一个任务会将执行结果<strong>作为参数</strong>，传递到回调方法中，回调方法<strong>无返回值</strong></p></blockquote><ul><li>方法签名<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 使用调用它的线程上运行（一般是主线程）</span></span><br><span class="line"><span class="keyword">public</span> CompletableFuture&lt;Void&gt; <span class="title function_">thenAccept</span><span class="params">(Consumer&lt;? <span class="built_in">super</span> T&gt; action)</span>;</span><br><span class="line"><span class="comment">// 使用默认线程池中的线程</span></span><br><span class="line"><span class="keyword">public</span> CompletableFuture&lt;Void&gt; <span class="title function_">thenAcceptAsync</span><span class="params">(Consumer&lt;? <span class="built_in">super</span> T&gt; action)</span>;</span><br><span class="line"><span class="comment">// 使用自定义线程池中的线程</span></span><br><span class="line"><span class="keyword">public</span> CompletableFuture&lt;Void&gt; <span class="title function_">thenAcceptAsync</span><span class="params">(Consumer&lt;? <span class="built_in">super</span> T&gt; action, Executor executor)</span>;</span><br></pre></td></tr></table></figure></li><li>示例代码<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line">CompletableFuture.supplyAsync(() -&gt; &#123;</span><br><span class="line">    <span class="keyword">return</span> <span class="string">&quot;Result&quot;</span>;</span><br><span class="line">&#125;).thenAccept((result) -&gt; &#123;</span><br><span class="line">    <span class="comment">// TODO 执行任务</span></span><br><span class="line">&#125;);</span><br></pre></td></tr></table></figure></li></ul><h3 id="接返回值、有返回值"><a href="#接返回值、有返回值" class="headerlink" title="接返回值、有返回值"></a>接返回值、有返回值</h3><blockquote><p>前一个任务执行完成后执行；前一个任务会将执行结果<strong>作为参数</strong>，传递到回调方法中，并且会调方法<strong>有返回值</strong></p></blockquote><ul><li>方法签名<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 使用调用它的线程上运行（一般是主线程）</span></span><br><span class="line"><span class="keyword">public</span> &lt;U&gt; CompletableFuture&lt;U&gt; <span class="title function_">thenApply</span><span class="params">(Function&lt;? <span class="built_in">super</span> T,? extends U&gt; fn)</span>;</span><br><span class="line"><span class="comment">// 使用默认线程池中的线程</span></span><br><span class="line"><span class="keyword">public</span> &lt;U&gt; CompletableFuture&lt;U&gt; <span class="title function_">thenApplyAsync</span><span class="params">(Function&lt;? <span class="built_in">super</span> T,? extends U&gt; fn)</span>;</span><br><span class="line"><span class="comment">// 使用自定义线程池中的线程</span></span><br><span class="line"><span class="keyword">public</span> &lt;U&gt; CompletableFuture&lt;U&gt; <span class="title function_">thenApplyAsync</span><span class="params">(Function&lt;? <span class="built_in">super</span> T,? extends U&gt; fn, Executor executor)</span>;</span><br></pre></td></tr></table></figure></li><li>示例代码<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line">CompletableFuture.supplyAsync(() -&gt; &#123;</span><br><span class="line">    <span class="keyword">return</span> <span class="string">&quot;Result_1&quot;</span>;</span><br><span class="line">&#125;).thenApply(() -&gt; &#123;</span><br><span class="line">    <span class="comment">// TODO 执行任务</span></span><br><span class="line">    <span class="keyword">return</span> <span class="string">&quot;Result_2&quot;</span>;</span><br><span class="line">&#125;);</span><br></pre></td></tr></table></figure></li></ul><h3 id="接异常、有返回值"><a href="#接异常、有返回值" class="headerlink" title="接异常、有返回值"></a>接异常、有返回值</h3><blockquote><p>某个任务执行异常时执行；异常任务抛出的异常<strong>作为参数</strong>，传递到回调方法中，并且会调方法<strong>有返回值</strong></p></blockquote><ul><li>方法签名<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 使用调用它的线程上运行（一般是主线程）</span></span><br><span class="line"><span class="keyword">public</span> CompletableFuture&lt;T&gt; <span class="title function_">exceptionally</span><span class="params">(Function&lt;Throwable, ? extends T&gt; fn)</span>;</span><br><span class="line"><span class="comment">// 使用默认线程池中的线程</span></span><br><span class="line"><span class="keyword">public</span> CompletableFuture&lt;T&gt; <span class="title function_">exceptionallyAsync</span><span class="params">(Function&lt;Throwable, ? extends T&gt; fn)</span>;</span><br><span class="line"><span class="comment">// 使用自定义线程池中的线程</span></span><br><span class="line"><span class="keyword">public</span> CompletableFuture&lt;T&gt; <span class="title function_">exceptionallyAsync</span><span class="params">(Function&lt;Throwable, ? extends T&gt; fn, Executor executor)</span>;</span><br></pre></td></tr></table></figure></li><li>示例代码<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line">CompletableFuture.runAsync(() -&gt; &#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;).exceptionally((exception) -&gt; &#123;</span><br><span class="line">    <span class="comment">// TODO 处理异常</span></span><br><span class="line">    <span class="keyword">return</span> <span class="string">&quot;ERROR!&quot;</span>;</span><br><span class="line">&#125;);</span><br></pre></td></tr></table></figure></li></ul><h3 id="接返回值和异常、无返回值"><a href="#接返回值和异常、无返回值" class="headerlink" title="接返回值和异常、无返回值"></a>接返回值和异常、无返回值</h3><blockquote><p>前一个任务执行完成或出现异常时执行；前一个任务会将结果或抛出的异常<strong>作为参数</strong>，传递到回调方法中，回调方法<strong>无返回值</strong></p></blockquote><ul><li>方法签名<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 使用调用它的线程上运行（一般是主线程）</span></span><br><span class="line"><span class="keyword">public</span> CompletableFuture&lt;T&gt; <span class="title function_">whenComplete</span><span class="params">(BiConsumer&lt;? <span class="built_in">super</span> T, ? <span class="built_in">super</span> Throwable&gt; action)</span>;</span><br><span class="line"><span class="comment">// 使用默认线程池中的线程</span></span><br><span class="line"><span class="keyword">public</span> CompletableFuture&lt;T&gt; <span class="title function_">whenCompleteAsync</span><span class="params">(BiConsumer&lt;? <span class="built_in">super</span> T, ? <span class="built_in">super</span> Throwable&gt; action)</span>;</span><br><span class="line"><span class="comment">// 使用自定义线程池中的线程</span></span><br><span class="line"><span class="keyword">public</span> CompletableFuture&lt;T&gt; <span class="title function_">whenCompleteAsync</span><span class="params">(BiConsumer&lt;? <span class="built_in">super</span> T, ? <span class="built_in">super</span> Throwable&gt; action, Executor executor)</span>;</span><br></pre></td></tr></table></figure></li><li>示例代码<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line">CompletableFuture.supplyAsync(() -&gt; &#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;).whenCompleteAsync((result, exception) -&gt; &#123;</span><br><span class="line">    <span class="keyword">if</span> (exception != <span class="literal">null</span>) &#123;</span><br><span class="line">        <span class="comment">// TODO 处理异常</span></span><br><span class="line">    &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">        <span class="comment">// TODO 执行任务</span></span><br><span class="line">    &#125;</span><br><span class="line">&#125;);</span><br></pre></td></tr></table></figure></li></ul><h3 id="接返回值和异常、有返回值"><a href="#接返回值和异常、有返回值" class="headerlink" title="接返回值和异常、有返回值"></a>接返回值和异常、有返回值</h3><blockquote><p>前一个任务执行完成后执行；前一个任务会将结果或抛出的异常<strong>作为参数</strong>，传递到回调方法中，回调方法<strong>有返回值</strong></p></blockquote><ul><li>方法签名<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 使用调用它的线程上运行（一般是主线程）</span></span><br><span class="line"><span class="keyword">public</span> &lt;U&gt; CompletableFuture&lt;U&gt; <span class="title function_">handle</span><span class="params">(BiFunction&lt;? <span class="built_in">super</span> T, Throwable, ? extends U&gt; fn)</span>;</span><br><span class="line"><span class="comment">// 使用默认线程池中的线程</span></span><br><span class="line"><span class="keyword">public</span> &lt;U&gt; CompletableFuture&lt;U&gt; <span class="title function_">handleAsync</span><span class="params">(BiFunction&lt;? <span class="built_in">super</span> T, Throwable, ? extends U&gt; fn)</span>;</span><br><span class="line"><span class="comment">// 使用自定义线程池中的线程</span></span><br><span class="line"><span class="keyword">public</span> &lt;U&gt; CompletableFuture&lt;U&gt; <span class="title function_">handleAsync</span><span class="params">(BiFunction&lt;? <span class="built_in">super</span> T, Throwable, ? extends U&gt; fn, Executor executor)</span>;</span><br></pre></td></tr></table></figure></li><li>示例代码<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><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">CompletableFuture.supplyAsync(() -&gt; &#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;).handleAsync((result, exception) -&gt; &#123;</span><br><span class="line">    <span class="keyword">if</span> (exception != <span class="literal">null</span>) &#123;</span><br><span class="line">        <span class="comment">// TODO 处理异常</span></span><br><span class="line">        <span class="keyword">return</span> <span class="string">&quot;ERROR!&quot;</span>;</span><br><span class="line">    &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">        <span class="comment">// 执行任务</span></span><br><span class="line">        <span class="keyword">return</span> <span class="string">&quot;SUCCESS!&quot;</span>;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;);</span><br></pre></td></tr></table></figure></li></ul><h1 id="两个任务组合"><a href="#两个任务组合" class="headerlink" title="两个任务组合"></a>两个任务组合</h1><h3 id="任务转化"><a href="#任务转化" class="headerlink" title="任务转化"></a>任务转化</h3><blockquote><p>将前一个任务的执行结果<strong>作为参数</strong>进行处理，处理后<strong>返回一个新的任务</strong></p></blockquote><ul><li>方法签名<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 使用调用它的线程上运行（一般是主线程）</span></span><br><span class="line"><span class="keyword">public</span> &lt;U&gt; CompletableFuture&lt;U&gt; <span class="title function_">thenCompose</span><span class="params">(Function&lt;? <span class="built_in">super</span> T, ? extends CompletionStage&lt;U&gt;&gt; fn)</span>;</span><br><span class="line"><span class="comment">// 使用默认线程池中的线程</span></span><br><span class="line"><span class="keyword">public</span> &lt;U&gt; CompletableFuture&lt;U&gt; <span class="title function_">thenComposeAsync</span><span class="params">(Function&lt;? <span class="built_in">super</span> T, ? extends CompletionStage&lt;U&gt;&gt; fn)</span>;</span><br><span class="line"><span class="comment">// 使用自定义线程池中的线程</span></span><br><span class="line"><span class="keyword">public</span> &lt;U&gt; CompletableFuture&lt;U&gt; <span class="title function_">thenComposeAsync</span><span class="params">(Function&lt;? <span class="built_in">super</span> T, ? extends CompletionStage&lt;U&gt;&gt; fn, Executor executor)</span>;</span><br></pre></td></tr></table></figure></li><li>示例代码<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><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">CompletableFuture&lt;String&gt; task = CompletableFuture.supplyAsync(() -&gt; &#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">CompletableFuture&lt;String&gt; stringCompletableFuture = CompletableFuture.supplyAsync(() -&gt; &#123;</span><br><span class="line">    <span class="keyword">return</span> <span class="string">&quot;任务二的结果&quot;</span>;</span><br><span class="line">&#125;).thenComposeAsync((result) -&gt; &#123;</span><br><span class="line">    <span class="comment">// TODO 执行任务</span></span><br><span class="line">    <span class="keyword">return</span> task;</span><br><span class="line">&#125;);</span><br></pre></td></tr></table></figure></li></ul><h3 id="“且”组合、接返回值、有返回值"><a href="#“且”组合、接返回值、有返回值" class="headerlink" title="“且”组合、接返回值、有返回值"></a>“且”组合、接返回值、有返回值</h3><blockquote><p>将两个任务组合，当两个任务都正常执行完了，将两个任务的结果作为参数，传递到回调方法中，回调方法有返回值</p></blockquote><ul><li>方法签名<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 使用调用它的线程上运行（一般是主线程）</span></span><br><span class="line"><span class="keyword">public</span> &lt;U,V&gt; CompletableFuture&lt;V&gt; <span class="title function_">thenCombine</span><span class="params">(CompletionStage&lt;? extends U&gt; other, BiFunction&lt;? <span class="built_in">super</span> T,? <span class="built_in">super</span> U,? extends V&gt; fn)</span>;</span><br><span class="line"><span class="comment">// 使用默认线程池中的线程</span></span><br><span class="line"><span class="keyword">public</span> &lt;U,V&gt; CompletableFuture&lt;V&gt; <span class="title function_">thenCombineAsync</span><span class="params">(CompletionStage&lt;? extends U&gt; other, BiFunction&lt;? <span class="built_in">super</span> T,? <span class="built_in">super</span> U,? extends V&gt; fn)</span>;</span><br><span class="line"><span class="comment">// 使用自定义线程池中的线程</span></span><br><span class="line"><span class="keyword">public</span> &lt;U,V&gt; CompletableFuture&lt;V&gt; <span class="title function_">thenCombineAsync</span><span class="params">(CompletionStage&lt;? extends U&gt; other, BiFunction&lt;? <span class="built_in">super</span> T,? <span class="built_in">super</span> U,? extends V&gt; fn, Executor executor)</span>;</span><br></pre></td></tr></table></figure></li><li>示例代码<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><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">CompletableFuture.supplyAsync(() -&gt; &#123;</span><br><span class="line">    <span class="comment">// TODO 任务一</span></span><br><span class="line">    <span class="keyword">return</span> <span class="string">&quot;任务一的结果&quot;</span>;</span><br><span class="line">&#125;).thenCombineAsync(CompletableFuture.supplyAsync(() -&gt; &#123;</span><br><span class="line">    <span class="comment">// TODO 任务二</span></span><br><span class="line">    <span class="keyword">return</span> <span class="string">&quot;任务二的结果&quot;</span>;</span><br><span class="line">&#125;), (result1, result2) -&gt; &#123;</span><br><span class="line">    <span class="comment">// 任务一和任务二都完成时执行的回调方法</span></span><br><span class="line">    <span class="keyword">return</span> <span class="literal">null</span>;</span><br><span class="line">&#125;);</span><br></pre></td></tr></table></figure></li></ul><h3 id="“且”组合、接返回值、无返回值"><a href="#“且”组合、接返回值、无返回值" class="headerlink" title="“且”组合、接返回值、无返回值"></a>“且”组合、接返回值、无返回值</h3><blockquote><p>将两个任务组合，当两个任务都正常执行完了，将两个任务的结果作为参数，传递到回调方法中，回调方法无返回值</p></blockquote><ul><li>方法签名<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 使用调用它的线程上运行（一般是主线程）</span></span><br><span class="line"><span class="keyword">public</span> &lt;U&gt; CompletableFuture&lt;Void&gt; <span class="title function_">thenAcceptBoth</span><span class="params">(CompletionStage&lt;? extends U&gt; other, BiConsumer&lt;? <span class="built_in">super</span> T, ? <span class="built_in">super</span> U&gt; action)</span>;</span><br><span class="line"><span class="comment">// 使用默认线程池中的线程</span></span><br><span class="line"><span class="keyword">public</span> &lt;U&gt; CompletableFuture&lt;Void&gt; <span class="title function_">thenAcceptBothAsync</span><span class="params">(CompletionStage&lt;? extends U&gt; other, BiConsumer&lt;? <span class="built_in">super</span> T, ? <span class="built_in">super</span> U&gt; action)</span>;</span><br><span class="line"><span class="comment">// 使用自定义线程池中的线程</span></span><br><span class="line"><span class="keyword">public</span> &lt;U&gt; CompletableFuture&lt;Void&gt; <span class="title function_">thenAcceptBothAsync</span><span class="params">(CompletionStage&lt;? extends U&gt; other, BiConsumer&lt;? <span class="built_in">super</span> T, ? <span class="built_in">super</span> U&gt; action, Executor executor)</span>;</span><br></pre></td></tr></table></figure></li><li>示例代码<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line">CompletableFuture.supplyAsync(() -&gt; &#123;</span><br><span class="line">    <span class="comment">// TODO 任务一</span></span><br><span class="line">    <span class="keyword">return</span> <span class="string">&quot;任务一的结果&quot;</span>;</span><br><span class="line">&#125;).thenAcceptBothAsync(CompletableFuture.supplyAsync(() -&gt; &#123;</span><br><span class="line">    <span class="comment">// TODO 任务二</span></span><br><span class="line">    <span class="keyword">return</span> <span class="string">&quot;任务二的结果&quot;</span>;</span><br><span class="line">&#125;), (result1, result2) -&gt; &#123;</span><br><span class="line">    <span class="comment">// TODO 任务一和任务二都完成时执行的回调方法</span></span><br><span class="line">&#125;);</span><br></pre></td></tr></table></figure></li></ul><h3 id="“且”组合、不接参数、无返回值"><a href="#“且”组合、不接参数、无返回值" class="headerlink" title="“且”组合、不接参数、无返回值"></a>“且”组合、不接参数、无返回值</h3><blockquote><p>将两个任务组合，当两个任务都正常执行完了，执行回调方法，回调方法无返回值</p></blockquote><ul><li>方法签名<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 使用调用它的线程上运行（一般是主线程）</span></span><br><span class="line"><span class="keyword">public</span> CompletableFuture&lt;Void&gt; <span class="title function_">runAfterBoth</span><span class="params">(CompletionStage&lt;?&gt; other, Runnable action)</span>;</span><br><span class="line"><span class="comment">// 使用默认线程池中的线程</span></span><br><span class="line"><span class="keyword">public</span> CompletableFuture&lt;Void&gt; <span class="title function_">runAfterBothAsync</span><span class="params">(CompletionStage&lt;?&gt; other, Runnable action)</span>;</span><br><span class="line"><span class="comment">// 使用自定义线程池中的线程</span></span><br><span class="line"><span class="keyword">public</span> CompletableFuture&lt;Void&gt; <span class="title function_">runAfterBothAsync</span><span class="params">(CompletionStage&lt;?&gt; other, Runnable action, Executor executor)</span>;</span><br></pre></td></tr></table></figure></li><li>示例代码<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line">CompletableFuture.supplyAsync(() -&gt; &#123;</span><br><span class="line">    <span class="comment">// TODO 任务一</span></span><br><span class="line">    <span class="keyword">return</span> <span class="string">&quot;任务一的结果&quot;</span>;</span><br><span class="line">&#125;).runAfterBothAsync(CompletableFuture.supplyAsync(() -&gt; &#123;</span><br><span class="line">    <span class="comment">// TODO 任务二</span></span><br><span class="line">    <span class="keyword">return</span> <span class="string">&quot;任务二的结果&quot;</span>;</span><br><span class="line">&#125;), () -&gt; &#123;</span><br><span class="line">    <span class="comment">// TODO 任务一和任务二都完成时执行的回调方法</span></span><br><span class="line">&#125;);</span><br></pre></td></tr></table></figure></li></ul><h3 id="“或”组合、接返回值、有返回值"><a href="#“或”组合、接返回值、有返回值" class="headerlink" title="“或”组合、接返回值、有返回值"></a>“或”组合、接返回值、有返回值</h3><blockquote><p>将两个任务组合，其中任意一个任务执行完成，将该任务的结果作为参数，传递到回调方法中，回调方法有返回值</p></blockquote><ul><li>方法签名<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 使用调用它的线程上运行（一般是主线程）</span></span><br><span class="line"><span class="keyword">public</span> &lt;U&gt; CompletableFuture&lt;U&gt; <span class="title function_">applyToEither</span><span class="params">(CompletionStage&lt;? extends T&gt; other, Function&lt;? <span class="built_in">super</span> T, U&gt; fn)</span>;</span><br><span class="line"><span class="comment">// 使用默认线程池中的线程</span></span><br><span class="line"><span class="keyword">public</span> &lt;U&gt; CompletableFuture&lt;U&gt; <span class="title function_">applyToEitherAsync</span><span class="params">(CompletionStage&lt;? extends T&gt; other, Function&lt;? <span class="built_in">super</span> T, U&gt; fn)</span>;</span><br><span class="line"><span class="comment">// 使用自定义线程池中的线程</span></span><br><span class="line"><span class="keyword">public</span> &lt;U&gt; CompletableFuture&lt;U&gt; <span class="title function_">applyToEitherAsync</span><span class="params">(CompletionStage&lt;? extends T&gt; other, Function&lt;? <span class="built_in">super</span> T, U&gt; fn, Executor executor)</span>;</span><br></pre></td></tr></table></figure></li><li>示例代码<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><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">CompletableFuture.supplyAsync(() -&gt; &#123;</span><br><span class="line">    <span class="comment">// TODO 任务一</span></span><br><span class="line">    <span class="keyword">return</span> <span class="string">&quot;任务一的结果&quot;</span>;</span><br><span class="line">&#125;).applyToEitherAsync(CompletableFuture.supplyAsync(() -&gt; &#123;</span><br><span class="line">    <span class="comment">// TODO 任务二</span></span><br><span class="line">    <span class="keyword">return</span> <span class="string">&quot;任务二的结果&quot;</span>;</span><br><span class="line">&#125;), (result) -&gt; &#123;</span><br><span class="line">    <span class="comment">// TODO 任务一和任务二任意一个执行完成时执行的回调方法</span></span><br><span class="line">    <span class="keyword">return</span> <span class="string">&quot;SUCCESS&quot;</span>;</span><br><span class="line">&#125;);</span><br></pre></td></tr></table></figure></li></ul><h3 id="“或”组合、接返回值、无返回值"><a href="#“或”组合、接返回值、无返回值" class="headerlink" title="“或”组合、接返回值、无返回值"></a>“或”组合、接返回值、无返回值</h3><blockquote><p>将两个任务组合，其中任意一个任务执行完成，将该任务的结果作为参数，传递到回调方法中，回调方法无返回值</p></blockquote><ul><li>方法签名<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 使用调用它的线程上运行（一般是主线程）</span></span><br><span class="line"><span class="keyword">public</span> CompletableFuture&lt;Void&gt; <span class="title function_">acceptEither</span><span class="params">(CompletionStage&lt;? extends T&gt; other, Consumer&lt;? <span class="built_in">super</span> T&gt; action)</span>;</span><br><span class="line"><span class="comment">// 使用默认线程池中的线程</span></span><br><span class="line"><span class="keyword">public</span> CompletableFuture&lt;Void&gt; <span class="title function_">acceptEitherAsync</span><span class="params">(CompletionStage&lt;? extends T&gt; other, Consumer&lt;? <span class="built_in">super</span> T&gt; action)</span>;</span><br><span class="line"><span class="comment">// 使用自定义线程池中的线程</span></span><br><span class="line"><span class="keyword">public</span> CompletableFuture&lt;Void&gt; <span class="title function_">acceptEitherAsync</span><span class="params">(CompletionStage&lt;? extends T&gt; other, Consumer&lt;? <span class="built_in">super</span> T&gt; action, Executor executor)</span>;</span><br></pre></td></tr></table></figure></li><li>示例代码<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><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">CompletableFuture.supplyAsync(() -&gt; &#123;</span><br><span class="line">    <span class="comment">// TODO 任务一</span></span><br><span class="line">    <span class="keyword">try</span> &#123;</span><br><span class="line">        Thread.sleep(<span class="number">10</span>);</span><br><span class="line">    &#125; <span class="keyword">catch</span> (InterruptedException e) &#123;</span><br><span class="line">        <span class="keyword">throw</span> <span class="keyword">new</span> <span class="title class_">RuntimeException</span>(e);</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">return</span> <span class="string">&quot;任务一的结果&quot;</span>;</span><br><span class="line">&#125;).acceptEitherAsync(CompletableFuture.supplyAsync(() -&gt; &#123;</span><br><span class="line">    <span class="comment">// TODO 任务二</span></span><br><span class="line">    <span class="keyword">return</span> <span class="string">&quot;任务二的结果&quot;</span>;</span><br><span class="line">&#125;), (result) -&gt; &#123;</span><br><span class="line">    <span class="comment">// TODO 任务一和任务二任意一个执行完成时执行的回调方法</span></span><br><span class="line">&#125;);</span><br></pre></td></tr></table></figure></li></ul><h3 id="“或”组合、不接参数、无返回值"><a href="#“或”组合、不接参数、无返回值" class="headerlink" title="“或”组合、不接参数、无返回值"></a>“或”组合、不接参数、无返回值</h3><blockquote><p>将两个任务组合，其中任意一个任务执行完成，执行回调方法，回调方法无返回值</p></blockquote><ul><li>方法签名<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 使用调用它的线程上运行（一般是主线程）</span></span><br><span class="line"><span class="keyword">public</span> CompletableFuture&lt;Void&gt; <span class="title function_">runAfterEither</span><span class="params">(CompletionStage&lt;?&gt; other, Runnable action)</span>;</span><br><span class="line"><span class="comment">// 使用默认线程池中的线程</span></span><br><span class="line"><span class="keyword">public</span> CompletableFuture&lt;Void&gt; <span class="title function_">runAfterEitherAsync</span><span class="params">(CompletionStage&lt;?&gt; other, Runnable action)</span>;</span><br><span class="line"><span class="comment">// 使用自定义线程池中的线程</span></span><br><span class="line"><span class="keyword">public</span> CompletableFuture&lt;Void&gt; <span class="title function_">runAfterEitherAsync</span><span class="params">(CompletionStage&lt;?&gt; other, Runnable action, Executor executor)</span>;</span><br></pre></td></tr></table></figure></li><li>示例代码<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><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">CompletableFuture.supplyAsync(() -&gt; &#123;</span><br><span class="line">    <span class="comment">// TODO 任务一</span></span><br><span class="line">    <span class="keyword">try</span> &#123;</span><br><span class="line">        Thread.sleep(<span class="number">10</span>);</span><br><span class="line">    &#125; <span class="keyword">catch</span> (InterruptedException e) &#123;</span><br><span class="line">        <span class="keyword">throw</span> <span class="keyword">new</span> <span class="title class_">RuntimeException</span>(e);</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">return</span> <span class="string">&quot;任务一的结果&quot;</span>;</span><br><span class="line">&#125;).runAfterEitherAsync(CompletableFuture.supplyAsync(() -&gt; &#123;</span><br><span class="line">    <span class="comment">// TODO 任务二</span></span><br><span class="line">    <span class="keyword">return</span> <span class="string">&quot;任务二的结果&quot;</span>;</span><br><span class="line">&#125;), () -&gt; &#123;</span><br><span class="line">    <span class="comment">// TODO 任务一和任务二任意一个执行完成时执行的回调方法</span></span><br><span class="line">&#125;);</span><br></pre></td></tr></table></figure></li></ul><h1 id="多个任务组合"><a href="#多个任务组合" class="headerlink" title="多个任务组合"></a>多个任务组合</h1><h3 id="“任意一个”组合"><a href="#“任意一个”组合" class="headerlink" title="“任意一个”组合"></a>“任意一个”组合</h3><blockquote><p>将多个任务组合，其中任意一个任务执行完成即可拿到该任务的执行结果（执行结果为Object类型），或执行接下来的任务</p></blockquote><ul><li>方法签名<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">static</span> CompletableFuture&lt;Object&gt; <span class="title function_">anyOf</span><span class="params">(CompletableFuture&lt;?&gt;... cfs)</span>;</span><br></pre></td></tr></table></figure></li><li>示例代码<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line">CompletableFuture&lt;String&gt; task1 = CompletableFuture.supplyAsync(() -&gt; &#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">CompletableFuture&lt;String&gt; task2 = CompletableFuture.supplyAsync(() -&gt; &#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="type">Object</span> <span class="variable">result</span> <span class="operator">=</span> CompletableFuture.anyOf(task1, task2).join();</span><br></pre></td></tr></table></figure></li></ul><h3 id="“所有”组合"><a href="#“所有”组合" class="headerlink" title="“所有”组合"></a>“所有”组合</h3><blockquote><p>将多个任务组合，当所有任务都执行完成后执行接下来的任务</p></blockquote><ul><li>方法签名<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">static</span> CompletableFuture&lt;Void&gt; <span class="title function_">allOf</span><span class="params">(CompletableFuture&lt;?&gt;... cfs)</span>;</span><br></pre></td></tr></table></figure></li><li>示例代码<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line">CompletableFuture&lt;String&gt; task1 = CompletableFuture.supplyAsync(() -&gt; &#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">CompletableFuture&lt;String&gt; task2 = CompletableFuture.supplyAsync(() -&gt; &#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="type">Object</span> <span class="variable">join</span> <span class="operator">=</span> CompletableFuture.allOf(task1, task2).join();</span><br></pre></td></tr></table></figure></li></ul>]]></content>
    
    
    <summary type="html">关于Java中CompletableFuture的使用方法总结。</summary>
    
    
    
    <category term="Java开发" scheme="https://cloudbillow.cn/categories/Java%E5%BC%80%E5%8F%91/"/>
    
    
    <category term="Java" scheme="https://cloudbillow.cn/tags/Java/"/>
    
    <category term="并发" scheme="https://cloudbillow.cn/tags/%E5%B9%B6%E5%8F%91/"/>
    
    <category term="CompletableFuture" scheme="https://cloudbillow.cn/tags/CompletableFuture/"/>
    
  </entry>
  
  <entry>
    <title>Hello Hexo</title>
    <link href="https://cloudbillow.cn/2020/03/10/2020-03-10-HelloHexo/"/>
    <id>https://cloudbillow.cn/2020/03/10/2020-03-10-HelloHexo/</id>
    <published>2020-03-10T08:04:53.000Z</published>
    <updated>2020-03-10T08:04:53.000Z</updated>
    
    <content type="html"><![CDATA[<p>Welcome to <a href="https://hexo.io/">Hexo</a>! This is your very first post. Check <a href="https://hexo.io/docs/">documentation</a> for more info. If you get any problems when using Hexo, you can find the answer in <a href="https://hexo.io/docs/troubleshooting.html">troubleshooting</a> or you can ask me on <a href="https://github.com/hexojs/hexo/issues">GitHub</a>.</p><h2 id="Quick-Start"><a href="#Quick-Start" class="headerlink" title="Quick Start"></a>Quick Start</h2><h3 id="Create-a-new-post"><a href="#Create-a-new-post" class="headerlink" title="Create a new post"></a>Create a new post</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">$ hexo new <span class="string">&quot;New Post&quot;</span></span><br></pre></td></tr></table></figure><p>More info: <a href="https://hexo.io/docs/writing.html">Writing</a></p><h3 id="Run-server"><a href="#Run-server" class="headerlink" title="Run server"></a>Run server</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">$ hexo server</span><br></pre></td></tr></table></figure><p>More info: <a href="https://hexo.io/docs/server.html">Server</a></p><h3 id="Clean-cache"><a href="#Clean-cache" class="headerlink" title="Clean cache"></a>Clean cache</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">$ hexo clean</span><br></pre></td></tr></table></figure><p>More info: <a href="https://hexo.io/docs/commands.html#clean">Commands Clean</a></p><h3 id="Generate-static-files"><a href="#Generate-static-files" class="headerlink" title="Generate static files"></a>Generate static files</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">$ hexo generate</span><br></pre></td></tr></table></figure><p>More info: <a href="https://hexo.io/docs/generating.html">Generating</a></p><h3 id="Deploy-to-remote-sites"><a href="#Deploy-to-remote-sites" class="headerlink" title="Deploy to remote sites"></a>Deploy to remote sites</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">$ hexo deploy</span><br></pre></td></tr></table></figure><p>More info: <a href="https://hexo.io/docs/one-command-deployment.html">Deployment</a></p>]]></content>
    
    
      
      
    <summary type="html">&lt;p&gt;Welcome to &lt;a href=&quot;https://hexo.io/&quot;&gt;Hexo&lt;/a&gt;! This is your very first post. Check &lt;a href=&quot;https://hexo.io/docs/&quot;&gt;documentation&lt;/a&gt; for</summary>
      
    
    
    
    
  </entry>
  
</feed>
