-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathatom.xml
More file actions
217 lines (106 loc) · 148 KB
/
Copy pathatom.xml
File metadata and controls
217 lines (106 loc) · 148 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
<?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("unused")</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> {</span><br><span class="line"></span><br><span class="line"> <span class="comment">/**</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">"成功"</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">"网络异常"</span>);</span><br><span class="line">}</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("unused")</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">R</span><T> {</span><br><span class="line"></span><br><span class="line"> <span class="comment">/**</span></span><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> <T> R<T> <span class="title function_">success</span><span class="params">()</span> {</span><br><span class="line"> <span class="keyword">return</span> R.<T>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"> }</span><br><span class="line"></span><br><span class="line"> <span class="keyword">public</span> <span class="keyword">static</span> <T> R<T> <span class="title function_">success</span><span class="params">(T data)</span> {</span><br><span class="line"> <span class="keyword">return</span> R.<T>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"> }</span><br><span class="line"></span><br><span class="line"> <span class="comment">/**</span></span><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> <T> R<T> <span class="title function_">success</span><span class="params">(T data, String message)</span> {</span><br><span class="line"> <span class="keyword">return</span> R.<T>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"> }</span><br><span class="line"></span><br><span class="line"> <span class="comment">/**</span></span><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> <T> R<T> <span class="title function_">fail</span><span class="params">(Integer code, String message)</span> {</span><br><span class="line"> <span class="keyword">return</span> R.<T>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"> }</span><br><span class="line"></span><br><span class="line"> <span class="comment">/**</span></span><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> <T> R<T> <span class="title function_">fail</span><span class="params">(ReturnCode returnCode)</span> {</span><br><span class="line"> <span class="keyword">return</span> R.<T>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"> }</span><br><span class="line"></span><br><span class="line"> <span class="comment">/**</span></span><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> <T> R<T> <span class="title function_">fail</span><span class="params">(ReturnCode returnCode, String message)</span> {</span><br><span class="line"> <span class="keyword">return</span> R.<T>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"> }</span><br><span class="line"></span><br><span class="line"></span><br><span class="line">}</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("unused")</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> {</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> {</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"> }</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> {</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"> }</span><br><span class="line">}</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> {</span><br><span class="line"></span><br><span class="line"> <span class="comment">/**</span></span><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<String> <span class="title function_">serviceExceptionHandler</span><span class="params">(ServiceException e)</span> {</span><br><span class="line"> log.error(<span class="string">"业务异常: {} -> {}"</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"> }</span><br><span class="line">}</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<String> 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) {</span><br><span class="line"> <span class="comment">// do something</span></span><br><span class="line">} <span class="keyword">else</span> {</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">}</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><T> {</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> {</span><br><span class="line"> checkError();</span><br><span class="line"> <span class="keyword">return</span> data;</span><br><span class="line"> }</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> {</span><br><span class="line"> <span class="keyword">if</span> (isSuccess()) {</span><br><span class="line"> <span class="keyword">return</span>;</span><br><span class="line"> }</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"> }</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> {</span><br><span class="line"> <span class="keyword">return</span> ReturnCode.RC200.getCode().equals(code);</span><br><span class="line"> }</span><br><span class="line">}</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> {</span><br><span class="line"> R<String> 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">}</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"> * <p></span></span><br><span class="line"><span class="comment"> * 该异常用于处理Feign调用异常</span></span><br><span class="line"><span class="comment"> * <p></span></span><br><span class="line"><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("unused")</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> {</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> {</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"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure><p>并且在全局异常处理器中对该异常进行处理,如下:</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br></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> {</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> {</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"> }</span><br><span class="line">}</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 {</span><br><span class="line"> <span class="keyword">public</span> String <span class="title function_">method</span><span class="params">()</span> {</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">"Feign调用异常, 该异常会被继续抛出!"</span>);</span><br><span class="line"> }</span><br><span class="line">}</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"><blockquote>
<p><strong>环境信息</strong>: <code>Java 8</code>、<code>Spring Boot 2.x</code>、<code>Spring Cloud 2021.x</code></p>
</blockquote>
<</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> {</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> {</span><br><span class="line"> <span class="built_in">this</span>.repository = repository;</span><br><span class="line"> }</span><br><span class="line"> </span><br><span class="line">}</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> {</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> {</span><br><span class="line"> <span class="built_in">this</span>.repository = repository;</span><br><span class="line"> }</span><br><span class="line"> </span><br><span class="line">}</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> {</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">}</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"><h1 id="引言"><a href="#引言" class="headerlink" title="引言"></a>引言</h1><h2 id="什么是依赖注入?"><a href="#什么是依赖注入?" class="headerlink" title="什么是依赖注入?"</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 函数名 {</span><br><span class="line"> # 函数体</span><br><span class="line">}</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 {</span><br><span class="line"> param([string]$status)</span><br><span class="line"> switch ($status) {</span><br><span class="line"> "on" {</span><br><span class="line"> git config --global http.proxy 'http://127.0.0.1:7890'</span><br><span class="line"> Write-Host "Git proxy has been turned ON."</span><br><span class="line"> }</span><br><span class="line"> "off" {</span><br><span class="line"> git config --global --unset http.proxy</span><br><span class="line"> Write-Host "Git proxy has been turned OFF."</span><br><span class="line"> }</span><br><span class="line"> default {</span><br><span class="line"> Write-Host "Use 'on' to turn on the proxy or 'off' to turn off the proxy."</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line">}</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)) { New-Item -Type File -Path $PROFILE.AllUsersAllHosts -Force }</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"><h1 id="概述"><a href="#概述" class="headerlink" title="概述"></a>概述</h1><p>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 'xxx'</code>异常。<br>如果确实要使用,可以通过设置<code>SET sql_mode='NO_UNSIGNED_SUBTRACTION';</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 'xxx' (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">'F'</span>) <span class="keyword">OR</span> (`sex`<span class="operator">=</span>_utf8mb4<span class="string">'M'</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='+8:00'</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"><h1 id="数字类型"><a href="#数字类型" class="headerlink" title="数字类型"></a>数字类型</h1><h2 id="整形类型"><a href="#整形类型" class="headerlink" title="整形类型"></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-{name}</code>,其中<code>{name}</code>为自定义的 Starter 名称。</p></li><li><p>自定义Starter的命名规则:<code>{name}-spring-boot-starter</code>,其中<code>{name}</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"><?xml version=<span class="string">"1.0"</span> encoding=<span class="string">"UTF-8"</span>?></span></span><br><span class="line"><span class="tag"><<span class="name">project</span> <span class="attr">xmlns</span>=<span class="string">"http://maven.apache.org/POM/4.0.0"</span></span></span><br><span class="line"><span class="tag"> <span class="attr">xmlns:xsi</span>=<span class="string">"http://www.w3.org/2001/XMLSchema-instance"</span></span></span><br><span class="line"><span class="tag"> <span class="attr">xsi:schemaLocation</span>=<span class="string">"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">modelVersion</span>></span>4.0.0<span class="tag"></<span class="name">modelVersion</span>></span></span><br><span class="line"></span><br><span class="line"> <span class="tag"><<span class="name">parent</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">groupId</span>></span>org.springframework.boot<span class="tag"></<span class="name">groupId</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">artifactId</span>></span>spring-boot-starter-parent<span class="tag"></<span class="name">artifactId</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">version</span>></span>2.7.14<span class="tag"></<span class="name">version</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">relativePath</span>/></span></span><br><span class="line"> <span class="tag"></<span class="name">parent</span>></span></span><br><span class="line"></span><br><span class="line"> <span class="tag"><<span class="name">groupId</span>></span>com.cloud.billow<span class="tag"></<span class="name">groupId</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">artifactId</span>></span>notify-spring-boot-starter<span class="tag"></<span class="name">artifactId</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">version</span>></span>1.0.0-SNAPSHOT<span class="tag"></<span class="name">version</span>></span></span><br><span class="line"></span><br><span class="line"> <span class="tag"><<span class="name">properties</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">maven.compiler.source</span>></span>8<span class="tag"></<span class="name">maven.compiler.source</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">maven.compiler.target</span>></span>8<span class="tag"></<span class="name">maven.compiler.target</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">project.build.sourceEncoding</span>></span>UTF-8<span class="tag"></<span class="name">project.build.sourceEncoding</span>></span></span><br><span class="line"> <span class="tag"></<span class="name">properties</span>></span></span><br><span class="line"></span><br><span class="line"> <span class="tag"><<span class="name">dependencies</span>></span></span><br><span class="line"></span><br><span class="line"> <span class="comment"><!-- Spring Boot 自动配置 --></span></span><br><span class="line"> <span class="tag"><<span class="name">dependency</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">groupId</span>></span>org.springframework.boot<span class="tag"></<span class="name">groupId</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">artifactId</span>></span>spring-boot-autoconfigure<span class="tag"></<span class="name">artifactId</span>></span></span><br><span class="line"> <span class="tag"></<span class="name">dependency</span>></span></span><br><span class="line"></span><br><span class="line"> <span class="comment"><!-- Spring Boot 配置处理器 --></span></span><br><span class="line"> <span class="tag"><<span class="name">dependency</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">groupId</span>></span>org.springframework.boot<span class="tag"></<span class="name">groupId</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">artifactId</span>></span>spring-boot-configuration-processor<span class="tag"></<span class="name">artifactId</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">optional</span>></span>true<span class="tag"></<span class="name">optional</span>></span></span><br><span class="line"> <span class="tag"></<span class="name">dependency</span>></span></span><br><span class="line"></span><br><span class="line"> <span class="comment"><!-- 其他依赖项 --></span></span><br><span class="line"> <span class="tag"><<span class="name">dependency</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">groupId</span>></span>org.projectlombok<span class="tag"></<span class="name">groupId</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">artifactId</span>></span>lombok<span class="tag"></<span class="name">artifactId</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">optional</span>></span>true<span class="tag"></<span class="name">optional</span>></span></span><br><span class="line"> <span class="tag"></<span class="name">dependency</span>></span></span><br><span class="line"></span><br><span class="line"> <span class="tag"></<span class="name">dependencies</span>></span></span><br><span class="line"></span><br><span class="line"><span class="tag"></<span class="name">project</span>></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> {</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">"notify"</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">}</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> {</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> {</span><br><span class="line"> <span class="keyword">try</span> {</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">"from: "</span> + from + <span class="string">", to: "</span> + to + <span class="string">", content: "</span> + content);</span><br><span class="line"> } <span class="keyword">catch</span> (Exception e) {</span><br><span class="line"> <span class="keyword">return</span> <span class="literal">false</span>;</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">return</span> <span class="literal">true</span>;</span><br><span class="line"> }</span><br><span class="line">}</span><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 = "com.cloud.billow.properties.NotifyProperties")</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> {</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> {</span><br><span class="line"> <span class="built_in">this</span>.notifyProperties = notifyProperties;</span><br><span class="line"> }</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> {</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"> }</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 {</span><br><span class="line"> <span class="comment">// 校验参数</span></span><br><span class="line"> }</span><br><span class="line">}</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"><<span class="name">dependency</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">groupId</span>></span>com.cloud.billow<span class="tag"></<span class="name">groupId</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">artifactId</span>></span>notify-spring-boot-starter<span class="tag"></<span class="name">artifactId</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">version</span>></span>1.0.0-SNAPSHOT<span class="tag"></<span class="name">version</span>></span></span><br><span class="line"><span class="tag"></<span class="name">dependency</span>></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 = "/test")</span></span><br><span class="line"><span class="keyword">public</span> String <span class="title function_">test</span><span class="params">()</span> {</span><br><span class="line"> <span class="type">boolean</span> <span class="variable">isSuccess</span> <span class="operator">=</span> notifyService.sendNotify(<span class="string">"这个是消息的内容"</span>);</span><br><span class="line"> <span class="keyword">return</span> String.valueOf(isSuccess);</span><br><span class="line">}</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"><h1 id="概述"><a href="#概述" class="headerlink" title="概述"></a>概述</h1><h2 id="什么是-SpringBoot-Starter"><a href="#什么是-SpringBoot-Starter" class="</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> {</span><br><span class="line"></span><br><span class="line"> <span class="meta">@GetMapping("/hello")</span></span><br><span class="line"> <span class="keyword">public</span> String <span class="title function_">hello</span><span class="params">()</span> {</span><br><span class="line"> <span class="keyword">return</span> <span class="string">"Hello Docker!"</span>;</span><br><span class="line"> }</span><br><span class="line">}</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_">> </span><span class="language-bash">GET /hello HTTP/1.1</span></span><br><span class="line"><span class="meta prompt_">> </span><span class="language-bash">Host: localhost:8080</span></span><br><span class="line"><span class="meta prompt_">> </span><span class="language-bash">User-Agent: curl/8.4.0</span></span><br><span class="line"><span class="meta prompt_">> </span><span class="language-bash">Accept: */*</span></span><br><span class="line"><span class="meta prompt_">> </span><span class="language-bash"></span></span><br><span class="line"><span class="language-bash">< HTTP/1.1 200</span> </span><br><span class="line">< Content-Type: text/plain;charset=UTF-8</span><br><span class="line">< Content-Length: 13</span><br><span class="line">< Date: Mon, 15 Apr 2024 06:56:56 GMT</span><br><span class="line">< </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">${JAR_FILE}</span> app.jar</span></span><br><span class="line"><span class="keyword">ENTRYPOINT</span><span class="language-bash"> [<span class="string">"java"</span>,<span class="string">"-jar"</span>,<span class="string">"/app.jar"</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>:创建一个/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 ${JAR_FILE} app.jar</code>:将<code>JAR_FILE</code>指定的jar文件添加到容器中,并重命名为<code>app.jar</code>。</li><li><code>ENTRYPOINT ["java","-jar","/app.jar"]</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"><h1 id="概述"><a href="#概述" class="headerlink" title="概述"></a>概述</h1><p>对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<Void> <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<Void> <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(() -> {</span><br><span class="line"> <span class="comment">// TODO 执行任务</span></span><br><span class="line">});</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> <U> CompletableFuture<U> <span class="title function_">supplyAsync</span><span class="params">(Supplier<U> 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> <U> CompletableFuture<U> <span class="title function_">supplyAsync</span><span class="params">(Supplier<U> 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(() -> {</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">});</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<Void> <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<Void> <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<Void> <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(() -> {</span><br><span class="line">}).thenRun(() -> {</span><br><span class="line"> <span class="comment">// TODO 执行任务</span></span><br><span class="line">});</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<Void> <span class="title function_">thenAccept</span><span class="params">(Consumer<? <span class="built_in">super</span> T> action)</span>;</span><br><span class="line"><span class="comment">// 使用默认线程池中的线程</span></span><br><span class="line"><span class="keyword">public</span> CompletableFuture<Void> <span class="title function_">thenAcceptAsync</span><span class="params">(Consumer<? <span class="built_in">super</span> T> action)</span>;</span><br><span class="line"><span class="comment">// 使用自定义线程池中的线程</span></span><br><span class="line"><span class="keyword">public</span> CompletableFuture<Void> <span class="title function_">thenAcceptAsync</span><span class="params">(Consumer<? <span class="built_in">super</span> T> 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(() -> {</span><br><span class="line"> <span class="keyword">return</span> <span class="string">"Result"</span>;</span><br><span class="line">}).thenAccept((result) -> {</span><br><span class="line"> <span class="comment">// TODO 执行任务</span></span><br><span class="line">});</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> <U> CompletableFuture<U> <span class="title function_">thenApply</span><span class="params">(Function<? <span class="built_in">super</span> T,? extends U> fn)</span>;</span><br><span class="line"><span class="comment">// 使用默认线程池中的线程</span></span><br><span class="line"><span class="keyword">public</span> <U> CompletableFuture<U> <span class="title function_">thenApplyAsync</span><span class="params">(Function<? <span class="built_in">super</span> T,? extends U> fn)</span>;</span><br><span class="line"><span class="comment">// 使用自定义线程池中的线程</span></span><br><span class="line"><span class="keyword">public</span> <U> CompletableFuture<U> <span class="title function_">thenApplyAsync</span><span class="params">(Function<? <span class="built_in">super</span> T,? extends U> 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(() -> {</span><br><span class="line"> <span class="keyword">return</span> <span class="string">"Result_1"</span>;</span><br><span class="line">}).thenApply(() -> {</span><br><span class="line"> <span class="comment">// TODO 执行任务</span></span><br><span class="line"> <span class="keyword">return</span> <span class="string">"Result_2"</span>;</span><br><span class="line">});</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<T> <span class="title function_">exceptionally</span><span class="params">(Function<Throwable, ? extends T> fn)</span>;</span><br><span class="line"><span class="comment">// 使用默认线程池中的线程</span></span><br><span class="line"><span class="keyword">public</span> CompletableFuture<T> <span class="title function_">exceptionallyAsync</span><span class="params">(Function<Throwable, ? extends T> fn)</span>;</span><br><span class="line"><span class="comment">// 使用自定义线程池中的线程</span></span><br><span class="line"><span class="keyword">public</span> CompletableFuture<T> <span class="title function_">exceptionallyAsync</span><span class="params">(Function<Throwable, ? extends T> 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(() -> {</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">}).exceptionally((exception) -> {</span><br><span class="line"> <span class="comment">// TODO 处理异常</span></span><br><span class="line"> <span class="keyword">return</span> <span class="string">"ERROR!"</span>;</span><br><span class="line">});</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<T> <span class="title function_">whenComplete</span><span class="params">(BiConsumer<? <span class="built_in">super</span> T, ? <span class="built_in">super</span> Throwable> action)</span>;</span><br><span class="line"><span class="comment">// 使用默认线程池中的线程</span></span><br><span class="line"><span class="keyword">public</span> CompletableFuture<T> <span class="title function_">whenCompleteAsync</span><span class="params">(BiConsumer<? <span class="built_in">super</span> T, ? <span class="built_in">super</span> Throwable> action)</span>;</span><br><span class="line"><span class="comment">// 使用自定义线程池中的线程</span></span><br><span class="line"><span class="keyword">public</span> CompletableFuture<T> <span class="title function_">whenCompleteAsync</span><span class="params">(BiConsumer<? <span class="built_in">super</span> T, ? <span class="built_in">super</span> Throwable> 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(() -> {</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">}).whenCompleteAsync((result, exception) -> {</span><br><span class="line"> <span class="keyword">if</span> (exception != <span class="literal">null</span>) {</span><br><span class="line"> <span class="comment">// TODO 处理异常</span></span><br><span class="line"> } <span class="keyword">else</span> {</span><br><span class="line"> <span class="comment">// TODO 执行任务</span></span><br><span class="line"> }</span><br><span class="line">});</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> <U> CompletableFuture<U> <span class="title function_">handle</span><span class="params">(BiFunction<? <span class="built_in">super</span> T, Throwable, ? extends U> fn)</span>;</span><br><span class="line"><span class="comment">// 使用默认线程池中的线程</span></span><br><span class="line"><span class="keyword">public</span> <U> CompletableFuture<U> <span class="title function_">handleAsync</span><span class="params">(BiFunction<? <span class="built_in">super</span> T, Throwable, ? extends U> fn)</span>;</span><br><span class="line"><span class="comment">// 使用自定义线程池中的线程</span></span><br><span class="line"><span class="keyword">public</span> <U> CompletableFuture<U> <span class="title function_">handleAsync</span><span class="params">(BiFunction<? <span class="built_in">super</span> T, Throwable, ? extends U> 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(() -> {</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">}).handleAsync((result, exception) -> {</span><br><span class="line"> <span class="keyword">if</span> (exception != <span class="literal">null</span>) {</span><br><span class="line"> <span class="comment">// TODO 处理异常</span></span><br><span class="line"> <span class="keyword">return</span> <span class="string">"ERROR!"</span>;</span><br><span class="line"> } <span class="keyword">else</span> {</span><br><span class="line"> <span class="comment">// 执行任务</span></span><br><span class="line"> <span class="keyword">return</span> <span class="string">"SUCCESS!"</span>;</span><br><span class="line"> }</span><br><span class="line">});</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> <U> CompletableFuture<U> <span class="title function_">thenCompose</span><span class="params">(Function<? <span class="built_in">super</span> T, ? extends CompletionStage<U>> fn)</span>;</span><br><span class="line"><span class="comment">// 使用默认线程池中的线程</span></span><br><span class="line"><span class="keyword">public</span> <U> CompletableFuture<U> <span class="title function_">thenComposeAsync</span><span class="params">(Function<? <span class="built_in">super</span> T, ? extends CompletionStage<U>> fn)</span>;</span><br><span class="line"><span class="comment">// 使用自定义线程池中的线程</span></span><br><span class="line"><span class="keyword">public</span> <U> CompletableFuture<U> <span class="title function_">thenComposeAsync</span><span class="params">(Function<? <span class="built_in">super</span> T, ? extends CompletionStage<U>> 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<String> task = CompletableFuture.supplyAsync(() -> {</span><br><span class="line"> <span class="keyword">return</span> <span class="string">"任务一的结果"</span>;</span><br><span class="line">});</span><br><span class="line"></span><br><span class="line">CompletableFuture<String> stringCompletableFuture = CompletableFuture.supplyAsync(() -> {</span><br><span class="line"> <span class="keyword">return</span> <span class="string">"任务二的结果"</span>;</span><br><span class="line">}).thenComposeAsync((result) -> {</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">});</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> <U,V> CompletableFuture<V> <span class="title function_">thenCombine</span><span class="params">(CompletionStage<? extends U> other, BiFunction<? <span class="built_in">super</span> T,? <span class="built_in">super</span> U,? extends V> fn)</span>;</span><br><span class="line"><span class="comment">// 使用默认线程池中的线程</span></span><br><span class="line"><span class="keyword">public</span> <U,V> CompletableFuture<V> <span class="title function_">thenCombineAsync</span><span class="params">(CompletionStage<? extends U> other, BiFunction<? <span class="built_in">super</span> T,? <span class="built_in">super</span> U,? extends V> fn)</span>;</span><br><span class="line"><span class="comment">// 使用自定义线程池中的线程</span></span><br><span class="line"><span class="keyword">public</span> <U,V> CompletableFuture<V> <span class="title function_">thenCombineAsync</span><span class="params">(CompletionStage<? extends U> other, BiFunction<? <span class="built_in">super</span> T,? <span class="built_in">super</span> U,? extends V> 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(() -> {</span><br><span class="line"> <span class="comment">// TODO 任务一</span></span><br><span class="line"> <span class="keyword">return</span> <span class="string">"任务一的结果"</span>;</span><br><span class="line">}).thenCombineAsync(CompletableFuture.supplyAsync(() -> {</span><br><span class="line"> <span class="comment">// TODO 任务二</span></span><br><span class="line"> <span class="keyword">return</span> <span class="string">"任务二的结果"</span>;</span><br><span class="line">}), (result1, result2) -> {</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">});</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> <U> CompletableFuture<Void> <span class="title function_">thenAcceptBoth</span><span class="params">(CompletionStage<? extends U> other, BiConsumer<? <span class="built_in">super</span> T, ? <span class="built_in">super</span> U> action)</span>;</span><br><span class="line"><span class="comment">// 使用默认线程池中的线程</span></span><br><span class="line"><span class="keyword">public</span> <U> CompletableFuture<Void> <span class="title function_">thenAcceptBothAsync</span><span class="params">(CompletionStage<? extends U> other, BiConsumer<? <span class="built_in">super</span> T, ? <span class="built_in">super</span> U> action)</span>;</span><br><span class="line"><span class="comment">// 使用自定义线程池中的线程</span></span><br><span class="line"><span class="keyword">public</span> <U> CompletableFuture<Void> <span class="title function_">thenAcceptBothAsync</span><span class="params">(CompletionStage<? extends U> other, BiConsumer<? <span class="built_in">super</span> T, ? <span class="built_in">super</span> U> 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(() -> {</span><br><span class="line"> <span class="comment">// TODO 任务一</span></span><br><span class="line"> <span class="keyword">return</span> <span class="string">"任务一的结果"</span>;</span><br><span class="line">}).thenAcceptBothAsync(CompletableFuture.supplyAsync(() -> {</span><br><span class="line"> <span class="comment">// TODO 任务二</span></span><br><span class="line"> <span class="keyword">return</span> <span class="string">"任务二的结果"</span>;</span><br><span class="line">}), (result1, result2) -> {</span><br><span class="line"> <span class="comment">// TODO 任务一和任务二都完成时执行的回调方法</span></span><br><span class="line">});</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<Void> <span class="title function_">runAfterBoth</span><span class="params">(CompletionStage<?> other, Runnable action)</span>;</span><br><span class="line"><span class="comment">// 使用默认线程池中的线程</span></span><br><span class="line"><span class="keyword">public</span> CompletableFuture<Void> <span class="title function_">runAfterBothAsync</span><span class="params">(CompletionStage<?> other, Runnable action)</span>;</span><br><span class="line"><span class="comment">// 使用自定义线程池中的线程</span></span><br><span class="line"><span class="keyword">public</span> CompletableFuture<Void> <span class="title function_">runAfterBothAsync</span><span class="params">(CompletionStage<?> 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(() -> {</span><br><span class="line"> <span class="comment">// TODO 任务一</span></span><br><span class="line"> <span class="keyword">return</span> <span class="string">"任务一的结果"</span>;</span><br><span class="line">}).runAfterBothAsync(CompletableFuture.supplyAsync(() -> {</span><br><span class="line"> <span class="comment">// TODO 任务二</span></span><br><span class="line"> <span class="keyword">return</span> <span class="string">"任务二的结果"</span>;</span><br><span class="line">}), () -> {</span><br><span class="line"> <span class="comment">// TODO 任务一和任务二都完成时执行的回调方法</span></span><br><span class="line">});</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> <U> CompletableFuture<U> <span class="title function_">applyToEither</span><span class="params">(CompletionStage<? extends T> other, Function<? <span class="built_in">super</span> T, U> fn)</span>;</span><br><span class="line"><span class="comment">// 使用默认线程池中的线程</span></span><br><span class="line"><span class="keyword">public</span> <U> CompletableFuture<U> <span class="title function_">applyToEitherAsync</span><span class="params">(CompletionStage<? extends T> other, Function<? <span class="built_in">super</span> T, U> fn)</span>;</span><br><span class="line"><span class="comment">// 使用自定义线程池中的线程</span></span><br><span class="line"><span class="keyword">public</span> <U> CompletableFuture<U> <span class="title function_">applyToEitherAsync</span><span class="params">(CompletionStage<? extends T> other, Function<? <span class="built_in">super</span> T, U> 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(() -> {</span><br><span class="line"> <span class="comment">// TODO 任务一</span></span><br><span class="line"> <span class="keyword">return</span> <span class="string">"任务一的结果"</span>;</span><br><span class="line">}).applyToEitherAsync(CompletableFuture.supplyAsync(() -> {</span><br><span class="line"> <span class="comment">// TODO 任务二</span></span><br><span class="line"> <span class="keyword">return</span> <span class="string">"任务二的结果"</span>;</span><br><span class="line">}), (result) -> {</span><br><span class="line"> <span class="comment">// TODO 任务一和任务二任意一个执行完成时执行的回调方法</span></span><br><span class="line"> <span class="keyword">return</span> <span class="string">"SUCCESS"</span>;</span><br><span class="line">});</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<Void> <span class="title function_">acceptEither</span><span class="params">(CompletionStage<? extends T> other, Consumer<? <span class="built_in">super</span> T> action)</span>;</span><br><span class="line"><span class="comment">// 使用默认线程池中的线程</span></span><br><span class="line"><span class="keyword">public</span> CompletableFuture<Void> <span class="title function_">acceptEitherAsync</span><span class="params">(CompletionStage<? extends T> other, Consumer<? <span class="built_in">super</span> T> action)</span>;</span><br><span class="line"><span class="comment">// 使用自定义线程池中的线程</span></span><br><span class="line"><span class="keyword">public</span> CompletableFuture<Void> <span class="title function_">acceptEitherAsync</span><span class="params">(CompletionStage<? extends T> other, Consumer<? <span class="built_in">super</span> T> 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(() -> {</span><br><span class="line"> <span class="comment">// TODO 任务一</span></span><br><span class="line"> <span class="keyword">try</span> {</span><br><span class="line"> Thread.sleep(<span class="number">10</span>);</span><br><span class="line"> } <span class="keyword">catch</span> (InterruptedException e) {</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"> }</span><br><span class="line"> <span class="keyword">return</span> <span class="string">"任务一的结果"</span>;</span><br><span class="line">}).acceptEitherAsync(CompletableFuture.supplyAsync(() -> {</span><br><span class="line"> <span class="comment">// TODO 任务二</span></span><br><span class="line"> <span class="keyword">return</span> <span class="string">"任务二的结果"</span>;</span><br><span class="line">}), (result) -> {</span><br><span class="line"> <span class="comment">// TODO 任务一和任务二任意一个执行完成时执行的回调方法</span></span><br><span class="line">});</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<Void> <span class="title function_">runAfterEither</span><span class="params">(CompletionStage<?> other, Runnable action)</span>;</span><br><span class="line"><span class="comment">// 使用默认线程池中的线程</span></span><br><span class="line"><span class="keyword">public</span> CompletableFuture<Void> <span class="title function_">runAfterEitherAsync</span><span class="params">(CompletionStage<?> other, Runnable action)</span>;</span><br><span class="line"><span class="comment">// 使用自定义线程池中的线程</span></span><br><span class="line"><span class="keyword">public</span> CompletableFuture<Void> <span class="title function_">runAfterEitherAsync</span><span class="params">(CompletionStage<?> 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(() -> {</span><br><span class="line"> <span class="comment">// TODO 任务一</span></span><br><span class="line"> <span class="keyword">try</span> {</span><br><span class="line"> Thread.sleep(<span class="number">10</span>);</span><br><span class="line"> } <span class="keyword">catch</span> (InterruptedException e) {</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"> }</span><br><span class="line"> <span class="keyword">return</span> <span class="string">"任务一的结果"</span>;</span><br><span class="line">}).runAfterEitherAsync(CompletableFuture.supplyAsync(() -> {</span><br><span class="line"> <span class="comment">// TODO 任务二</span></span><br><span class="line"> <span class="keyword">return</span> <span class="string">"任务二的结果"</span>;</span><br><span class="line">}), () -> {</span><br><span class="line"> <span class="comment">// TODO 任务一和任务二任意一个执行完成时执行的回调方法</span></span><br><span class="line">});</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<Object> <span class="title function_">anyOf</span><span class="params">(CompletableFuture<?>... 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<String> task1 = CompletableFuture.supplyAsync(() -> {</span><br><span class="line"> <span class="keyword">return</span> <span class="string">"任务一的结果"</span>;</span><br><span class="line">});</span><br><span class="line"></span><br><span class="line">CompletableFuture<String> task2 = CompletableFuture.supplyAsync(() -> {</span><br><span class="line"> <span class="keyword">return</span> <span class="string">"任务二的结果"</span>;</span><br><span class="line">});</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<Void> <span class="title function_">allOf</span><span class="params">(CompletableFuture<?>... 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<String> task1 = CompletableFuture.supplyAsync(() -> {</span><br><span class="line"> <span class="keyword">return</span> <span class="string">"任务一的结果"</span>;</span><br><span class="line">});</span><br><span class="line"></span><br><span class="line">CompletableFuture<String> task2 = CompletableFuture.supplyAsync(() -> {</span><br><span class="line"> <span class="keyword">return</span> <span class="string">"任务二的结果"</span>;</span><br><span class="line">});</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">"New Post"</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"><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</summary>
</entry>
</feed>