<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom">
  <title>枪口下的砚台</title>
  
  <subtitle>Stay Hungry. Stay Foolish.</subtitle>
  <link href="https://syshlang.com/atom.xml" rel="self"/>
  
  <link href="https://syshlang.com/"/>
  <updated>2025-12-01T14:31:49.645Z</updated>
  <id>https://syshlang.com/</id>
  
  <author>
    <name>syshlang</name>
    
  </author>
  
  <generator uri="https://hexo.io/">Hexo</generator>
  
  <entry>
    <title>权限模型</title>
    <link href="https://syshlang.com/posts/c5423dbf/"/>
    <id>https://syshlang.com/posts/c5423dbf/</id>
    <published>2024-09-08T06:54:55.000Z</published>
    <updated>2025-12-01T14:31:49.645Z</updated>
    
    <content type="html"><![CDATA[<link rel="stylesheet" class="aplayer-secondary-style-marker" href="/assets/css/APlayer.min.css"><script src="/assets/js/APlayer.min.js" class="aplayer-secondary-script-marker"></script><script class="meting-secondary-script-marker" src="/assets/js/Meting.min.js"></script><p><img src="https://oss.syshlang.com/blog/image/c3c209f0704611ef854285217a3c2718.png" alt="权限控制"></p><span id="more"></span><div class="markmap-wrap" style="height: 800px">      <script type="application/json">{"content":"&#x6743;&#x9650;&#x7cfb;&#x7edf;&#x6a21;&#x578b;","children":[{"content":"&#x81ea;&#x4e3b;&#x8bbf;&#x95ee;&#x63a7;&#x5236;(DAC, Discretionary Access Control&#xff09;","children":[{"content":"&#x6839;&#x636e;&#x81ea;&#x4e3b;&#x8bbf;&#x95ee;&#x63a7;&#x5236;&#x7b56;&#x7565;&#x5efa;&#x7acb;&#x7684;&#x4e00;&#x79cd;&#x6a21;&#x578b;","children":[],"payload":{"tag":"h3","lines":"7,8"}}],"payload":{"tag":"h2","lines":"6,7"}},{"content":"&#x5f3a;&#x5236;&#x8bbf;&#x95ee;&#x63a7;&#x5236;(MAC&#xff0c;Mandatory Access Control)","children":[{"content":"&#x4e00;&#x79cd;&#x57fa;&#x4e8e;&#x5b89;&#x5168;&#x6807;&#x7b7e;&#x7684;&#x8bbf;&#x95ee;&#x63a7;&#x5236;&#x6a21;&#x578b;&#xff0c;&#x901a;&#x5e38;&#x7528;&#x4e8e;&#x519b;&#x4e8b;&#x548c;&#x653f;&#x5e9c;&#x673a;&#x6784;","children":[],"payload":{"tag":"h3","lines":"9,10"}}],"payload":{"tag":"h2","lines":"8,9"}},{"content":"&#x57fa;&#x4e8e;&#x89d2;&#x8272;&#x8bbf;&#x95ee;&#x63a7;&#x5236;(RBAC, Role-based Access Control)","children":[{"content":"RBAC0","children":[{"content":"RBAC&#x6838;&#x5fc3;&#x601d;&#x60f3;&#x6a21;&#x578b;","children":[],"payload":{"tag":"h4","lines":"12,13"}},{"content":"RBAC&#x4e09;&#x8981;&#x7d20;","children":[],"payload":{"tag":"h4","lines":"13,14"}},{"content":"&#x901a;&#x8fc7;&#x5b9a;&#x4e49;&#x89d2;&#x8272;&#x7684;&#x6743;&#x9650;&#xff0c;&#x5e76;&#x5bf9;&#x7528;&#x6237;&#x6388;&#x4e88;&#x67d0;&#x4e2a;&#x89d2;&#x8272;&#x6765;&#x63a7;&#x5236;&#x7528;&#x6237;&#x7684;&#x6743;&#x9650;&#xff0c;&#x5b9e;&#x73b0;&#x4e86;&#x7528;&#x6237;&#x548c;&#x6743;&#x9650;&#x7684;&#x903b;&#x8f91;&#x5206;&#x79bb;","children":[],"payload":{"tag":"h4","lines":"14,15"}}],"payload":{"tag":"h3","lines":"11,12"}},{"content":"RBAC1","children":[{"content":"&#x89d2;&#x8272;&#x5206;&#x7ea7;&#x6a21;&#x578b;","children":[{"content":"&#x5f15;&#x5165;&#x89d2;&#x8272;&#x95f4;&#x7684;&#x7ee7;&#x627f;&#x5173;&#x7cfb;","children":[],"payload":{"tag":"h5","lines":"17,18"}}],"payload":{"tag":"h4","lines":"16,17"}},{"content":"&#x89d2;&#x8272;&#x7ee7;&#x627f;&#x5173;&#x7cfb;","children":[{"content":"&#x4e00;&#x822c;&#x7ee7;&#x627f;&#x5173;&#x7cfb;","children":[{"content":"&#x53ef;&#x8de8;&#x6811;&#x5f62;&#x5c42;&#x7ea7;&#x7684;&#x591a;&#x7ee7;&#x627f;","children":[],"payload":{"tag":"h6","lines":"20,21"}}],"payload":{"tag":"h5","lines":"19,20"}},{"content":"&#x53d7;&#x9650;&#x7ee7;&#x627f;&#x5173;&#x7cfb;","children":[{"content":"&#x4e00;&#x4e2a;&#x6811;&#x5f62;&#x7ed3;&#x6784;&#x7684;&#x5355;&#x7ee7;&#x627f;","children":[],"payload":{"tag":"h6","lines":"22,23"}}],"payload":{"tag":"h5","lines":"21,22"}}],"payload":{"tag":"h4","lines":"18,19"}}],"payload":{"tag":"h3","lines":"15,16"}},{"content":"RBAC2","children":[{"content":"&#x89d2;&#x8272;&#x9650;&#x5236;&#x6a21;&#x578b;","children":[{"content":"&#x9759;&#x6001;&#x804c;&#x8d23;&#x5206;&#x79bb;SSD","children":[{"content":"&#x4e92;&#x65a5;&#x89d2;&#x8272;","children":[],"payload":{"tag":"h6","lines":"26,27"}},{"content":"&#x57fa;&#x6570;&#x7ea6;&#x675f;","children":[],"payload":{"tag":"h6","lines":"27,28"}},{"content":"&#x5148;&#x51b3;&#x6761;&#x4ef6;&#x7ea6;&#x675f;","children":[],"payload":{"tag":"h6","lines":"28,29"}}],"payload":{"tag":"h5","lines":"25,26"}},{"content":"&#x52a8;&#x6001;&#x804c;&#x8d23;&#x5206;&#x79bb;DSD","children":[{"content":"&#x7528;&#x6237;&#x767b;&#x5f55;&#x540e;&#x9009;&#x62e9;&#x4f7f;&#x7528;&#x7684;&#x89d2;&#x8272;&#xff0c;&#x6839;&#x636e;&#x9700;&#x8981;&#x5207;&#x6362;&#x89d2;&#x8272;","children":[],"payload":{"tag":"h6","lines":"30,31"}}],"payload":{"tag":"h5","lines":"29,30"}}],"payload":{"tag":"h4","lines":"24,25"}}],"payload":{"tag":"h3","lines":"23,24"}},{"content":"RBAC3","children":[{"content":"&#x7edf;&#x4e00;&#x6a21;&#x578b;","children":[{"content":"&#x7528;&#x6237;&#x767b;&#x5f55;&#x540e;&#x9009;&#x62e9;&#x4f7f;&#x7528;&#x7684;&#x89d2;&#x8272;&#xff0c;&#x6839;&#x636e;&#x9700;&#x8981;&#x5207;&#x6362;&#x89d2;&#x8272;","children":[],"payload":{"tag":"h5","lines":"33,34"}}],"payload":{"tag":"h4","lines":"32,33"}}],"payload":{"tag":"h3","lines":"31,32"}}],"payload":{"tag":"h2","lines":"10,11"}},{"content":"&#x57fa;&#x4e8e;&#x5c5e;&#x6027;&#x7684;&#x8bbf;&#x95ee;&#x63a7;&#x5236;(ABAC, Attribute Based Access Control)","children":[{"content":"&#x901a;&#x8fc7;&#x52a8;&#x6001;&#x8ba1;&#x7b97;&#x4e00;&#x4e2a;&#x6216;&#x4e00;&#x7ec4;&#x5c5e;&#x6027;&#x662f;&#x5426;&#x6ee1;&#x8db3;&#x67d0;&#x79cd;&#x6761;&#x4ef6;&#x6765;&#x8fdb;&#x884c;&#x6388;&#x6743;&#x5224;&#x65ad;","children":[],"payload":{"tag":"h3","lines":"35,36"}},{"content":"&#x56db;&#x7c7b;&#x5c5e;&#x6027;","children":[{"content":"&#x4e3b;&#x4f53;&#x5c5e;&#x6027;","children":[],"payload":{"tag":"h4","lines":"37,38"}},{"content":"&#x73af;&#x5883;&#x5c5e;&#x6027;","children":[],"payload":{"tag":"h4","lines":"38,39"}},{"content":"&#x8d44;&#x6e90;&#x5c5e;&#x6027;","children":[],"payload":{"tag":"h4","lines":"39,40"}},{"content":"&#x64cd;&#x4f5c;&#x5c5e;&#x6027;","children":[],"payload":{"tag":"h4","lines":"40,41"}}],"payload":{"tag":"h3","lines":"36,37"}}],"payload":{"tag":"h2","lines":"34,35"}}],"payload":{"tag":"h1","lines":"5,6"}}</script>      <script type="application/json">{"colorFreezeLevel":2}</script>    </div><h1 id="自主访问控制-DAC-Discretionary-Access-Control"><a href="#自主访问控制-DAC-Discretionary-Access-Control" class="headerlink" title="自主访问控制(DAC, Discretionary Access Control)"></a>自主访问控制 (DAC, Discretionary Access Control)</h1><p>自主访问控制 (DAC, Discretionary Access Control) 模型是根据自主访问控制策略建立的一种模型，允许合法用户或用户组的身份访问策略规定的客体，同时阻止非授权用户访问客体。拥有客体权限的用户，可以将该客体的权限分配给其他用户，是 ACL（Access<br>Control List）访问控制列表模型的扩展。</p><hr><h1 id="强制访问控制-MAC，Mandatory-Access-Control"><a href="#强制访问控制-MAC，Mandatory-Access-Control" class="headerlink" title="强制访问控制(MAC，Mandatory Access Control)"></a>强制访问控制 (MAC，Mandatory Access Control)</h1><p>强制访问控制 (MAC，Mandatory Access Control) 模型是一种基于安全标签的访问控制模型，通常用于军事和政府机构，以确保信息的机密性和完整性。</p><hr><h1 id="基于角色访问控制-RBAC-Role-based-Access-Control"><a href="#基于角色访问控制-RBAC-Role-based-Access-Control" class="headerlink" title="基于角色访问控制(RBAC, Role-based Access Control)"></a>基于角色访问控制 (RBAC, Role-based Access Control)</h1><p>基于角色访问控制 (RBAC, Role-based Access Control) 模型通过定义角色的权限，并对用户授予某个角色来控制用户的权限，实现了用户和权限的逻辑分离，这又叫 RBAC0。随着系统权限复杂性增加，RBAC 也随之扩展出了多个部件模型，主要包括：RBAC1（角色分级模型）、 RBAC2（角色限制模型）、 RBAC3（统一模型）。</p><h2 id="RBAC0（基本模型）"><a href="#RBAC0（基本模型）" class="headerlink" title="RBAC0（基本模型）"></a>RBAC0（基本模型）</h2><p>RBAC0 是 RBAC 核心思想模型，它定义了完全支持 RBAC 概念的任何系统的最低需求，包括 RBAC 三要素（用户（U）、角色（R）和权限（P））。</p><h2 id="RBAC1（角色分级模型）"><a href="#RBAC1（角色分级模型）" class="headerlink" title="RBAC1（角色分级模型）"></a>RBAC1（角色分级模型）</h2><p>RBAC1（角色分级模型）的思路是上层角色继承下层角色的所有权限，并且可以额外拥有其他权限。简单来说，就是引入角色继承的关系，使得角色具有上下级的区别，这种继承关系可以分为一般继承关系和受限继承关系。</p><h3 id="一般继承关系"><a href="#一般继承关系" class="headerlink" title="一般继承关系"></a>一般继承关系</h3><p>要求角色继承关系是一个绝对偏序关系，允许角色间的多继承。这意味着一个角色可以继承多个上级角色的权限。</p><h3 id="受限继承关系"><a href="#受限继承关系" class="headerlink" title="受限继承关系"></a>受限继承关系</h3><p>要求角色继承关系是一个树结构，实现角色间的单继承。这种模型适用于角色之间的层次明确，包含关系清晰的情况</p><h2 id="RBAC2（角色限制模型）"><a href="#RBAC2（角色限制模型）" class="headerlink" title="RBAC2（角色限制模型）"></a>RBAC2（角色限制模型）</h2><p>RBAC2（角色限制模型）的思路是引入约束的概念，主要引入了静态职责分离 SSD (Static Separation of Duty) 和动态职责分离 DSD (Dynamic<br>Separation of Duty)。</p><h3 id="静态职责分离SSD"><a href="#静态职责分离SSD" class="headerlink" title="静态职责分离SSD"></a>静态职责分离 SSD</h3><p>发生在用户和角色的指派阶段，主要有如下约束：</p><ul><li>互斥角色：同一个用户在两个互斥角色中只能选择一个</li><li>基数约束：一个用户拥有的角色是有限的，一个角色拥有的许可也是有限的</li><li>先决条件约束：用户想要获得高级角色，首先必须拥有低级角色</li></ul><h3 id="动态职责分离DSD"><a href="#动态职责分离DSD" class="headerlink" title="动态职责分离DSD"></a>动态职责分离 DSD</h3><p>是会话和角色之间的约束，可以动态的约束用户拥有的角色，如一个用户可以拥有两个角色，但是运行时只能激活一个角色。</p><h2 id="RBAC3（统一模型）"><a href="#RBAC3（统一模型）" class="headerlink" title="RBAC3（统一模型）"></a>RBAC3（统一模型）</h2><p>BAC3（统一模型）同时包含了 RBAC1 和 RBAC2 的特性，即继承关系和约束关系</p><hr><h1 id="基于属性的访问控制-ABAC-Attribute-Based-Access-Control"><a href="#基于属性的访问控制-ABAC-Attribute-Based-Access-Control" class="headerlink" title="基于属性的访问控制(ABAC, Attribute Based Access Control)"></a>基于属性的访问控制 (ABAC, Attribute Based Access Control)</h1><p>通过动态计算一个或一组属性是否满足某种条件来进行授权判断。属性可以分为四类：主体属性、环境属性、资源属性、操作属性。可以按需实现不同颗粒度的权限控制，但定义权限时不易看出用户和对象间的关系。ABAC 设计的目的是为了能够满足控制请求者在某些条件下是否对请求数据具备某个操作（API）<br>的能力。这个模型在云系统中使用的比较多，比如阿里云，腾讯云，华为云等，它们都是用 ABAC 来管控云服务资源。ABAC 语法设计指引中的一些术语:</p><ul><li><strong>subject</strong> 指的是系统的使用者，可以是用户 (user), 也可以是其他非服务的个体 (non-person entity，NPE)</li><li><strong>object</strong> 泛指被访问的数据</li><li><strong> operation/action</strong> 指操作行为，一般对应系统中的 API</li><li><strong>policy</strong> 访问策略，它规定了一个用户在什么条件下可以对哪些数据做什么，是 ABAC 系统核心实体之一</li><li><strong> pdp</strong> pdp 是 policy decision point，策略点，其实我理解这玩意就是一个 policy 的展示出来的形式而已</li><li><strong> pep</strong> pep 是 policy enforcement point, 策略执行点，简单说就是根据 policy 来鉴权</li><li><strong> acm</strong> acm 是 access control mechanism, 权限管控机制，一般来说就是权限系统本身</li><li><strong> attribute</strong> 它泛指各种属性，可以是 subject 的，也可以是 object 的</li><li><strong> condition</strong> 各种额外的限制条件</li></ul><link rel="stylesheet" href="https://fastly.jsdelivr.net/npm/markmap-toolbar@0.18.10/dist/style.css"><script src="https://fastly.jsdelivr.net/npm/d3@7"></script><script src="https://fastly.jsdelivr.net/npm/markmap-view@0.18.10"></script><script src="https://fastly.jsdelivr.net/npm/markmap-toolbar@0.18.10"></script><link rel="stylesheet" href="/css/markmap.css"><script src="/js/markmap.js"></script>]]></content>
    
    
    <summary type="html">&lt;p&gt;&lt;img src=&quot;https://oss.syshlang.com/blog/image/c3c209f0704611ef854285217a3c2718.png&quot; alt=&quot;权限控制&quot;&gt;&lt;/p&gt;</summary>
    
    
    
    <category term="CODING" scheme="https://syshlang.com/categories/CODING/"/>
    
    <category term="后端" scheme="https://syshlang.com/categories/CODING/%E5%90%8E%E7%AB%AF/"/>
    
    <category term="java" scheme="https://syshlang.com/categories/CODING/%E5%90%8E%E7%AB%AF/java/"/>
    
    
    <category term="java" scheme="https://syshlang.com/tags/java/"/>
    
    <category term="权限模型" scheme="https://syshlang.com/tags/%E6%9D%83%E9%99%90%E6%A8%A1%E5%9E%8B/"/>
    
  </entry>
  
  <entry>
    <title>2022 年总结</title>
    <link href="https://syshlang.com/posts/7448189f/"/>
    <id>https://syshlang.com/posts/7448189f/</id>
    <published>2022-12-31T15:15:02.000Z</published>
    <updated>2025-05-22T15:08:23.923Z</updated>
    
    <content type="html"><![CDATA[<div class="hbe hbe-container" id="hexo-blog-encrypt" data-wpm="密码错误，请重新输入......" data-whm="无权限修改！">  <script id="hbeData" type="hbeData" data-hmacdigest="043912295a867ab4c7c6f1dbb72745d1d5846b2e15dbe5bce0eb413dbdf8ff5e">6310b677c51d396ed567d6ddd717d7b0a8cd09df8a6ec689e7d7975a591d07c6d842fad346386e2e2155b2d65cb2e79963809c7afdf4deb258cb1d4dad285102a168d369ea477390d55a03e872cbc9caeda57bd96a9af3691294a6cc541ad729a9a0ed93bd33bd2407edff24d17a9dab5141d9e0ba384c723ffbbbb834adf44ad41f54ae7e7b06cc2a0330ba68c869fb907270f4f6b39cb1eb4f645f153c932b080b167991d1510e91959cb34cbf6da39f7bfe0cd47e13cb61e47deda23ad01e968407042149cacf755e9dc9c4ce8741cc74dac8a5dda22e0f47eb42f3ea9efc63737c0f41644490cf8d3e8519fa18a9166a71c2758de6aa40b7db2ab00b0555094c1a0fcbaa075a34e894ced1ac40786833d4473730343fe4715be20a9df645668a81a9ca0a21b56f0a667276994a49041e9afdfb6877e3abcea7c8fb68d1526f3998984f1f8bed1ad79a0e60587c5c034bef7fe4ff66468a6e93ee53c4a54a2304ff5eaaa389126639b687f7b1a2b13095246eb68aa478e2925e199c69655a8014ae8e0af4dc276be121f2e705459d0d5dd45ed01cfdc5b32790cfb6e762ea9095d2e9bc4bd59d377801922a9f87cb674814b240a43035648dd01c237164d1dc76509bd6e388b3ffea70f6ad7a394691436ac613db172fdc78adef6f99618e4c25007d932bbeded2507a3bf2180c60b6c40bfa300e423b2f79ba6bc102a0f95ae650c3460cf431dd82b050f39f12eba08ab61486afa4d58d65e0d5f798663f46ef63902d22e02e931260d05cae30ad49df4f8a6b21c58ec8b25021689d649afeeee54696187956365006330c428821c8bde39c7ace8e4955eeaca9550b85da4265fefc529d5fb1ed444daf44a23b7e6842e6f3000e291dcc9404dc746b3be5185826164796c08f1e5dde1fae2b79fc5ed1d856cdf9e5ad48d992afa258c2f8c67effa6417e8abd8a145060d0c1a93db62a13ef91ca067c863c6a7beeba2779</script>  <div class="hbe hbe-content">    <div class="hbe hbe-input hbe-input-xray">      <input class="hbe hbe-input-field hbe-input-field-xray" type="password" id="hbePass">      <label class="hbe hbe-input-label hbe-input-label-xray" for="hbePass">        <span class="hbe hbe-input-label-content hbe-input-label-content-xray">请输入密码......</span>      </label>      <svg class="hbe hbe-graphic hbe-graphic-xray" width="300%" height="100%" viewBox="0 0 1200 60" preserveAspectRatio="none">        <path d="M0,56.5c0,0,298.666,0,399.333,0C448.336,56.5,513.994,46,597,46c77.327,0,135,10.5,200.999,10.5c95.996,0,402.001,0,402.001,0"></path>        <path d="M0,2.5c0,0,298.666,0,399.333,0C448.336,2.5,513.994,13,597,13c77.327,0,135-10.5,200.999-10.5c95.996,0,402.001,0,402.001,0"></path>      </svg>    </div>  </div></div><script data-pjax src="/lib/hbe.js"></script><link href="/css/hbe.style.css" rel="stylesheet" type="text/css">]]></content>
    
    
    <summary type="html">访问受限，请输入密码！</summary>
    
    
    
    <category term="LIFE" scheme="https://syshlang.com/categories/LIFE/"/>
    
    <category term="essay" scheme="https://syshlang.com/categories/LIFE/essay/"/>
    
    
    <category term="随笔" scheme="https://syshlang.com/tags/%E9%9A%8F%E7%AC%94/"/>
    
  </entry>
  
  <entry>
    <title>生命曾如灿烂的野花，一遍遍重复不结束的盛夏。</title>
    <link href="https://syshlang.com/posts/a9dbcc64/"/>
    <id>https://syshlang.com/posts/a9dbcc64/</id>
    <published>2022-10-23T15:56:02.000Z</published>
    <updated>2025-05-22T15:08:23.923Z</updated>
    
    <content type="html"><![CDATA[<div class="hbe hbe-container" id="hexo-blog-encrypt" data-wpm="密码错误，请重新输入......" data-whm="无权限修改！">  <script id="hbeData" type="hbeData" data-hmacdigest="91db1c1dff952440dc02ec222a79c01e5a9cfe586c634d175ae87258b29a6897">926c897caebaabffcf2724d72cb558b36d8790259ac02109f38dcc4d0baeeaf26e040a7c4de38512862feb1cb150aa0e23973a5702928902069fafd67f6c751a2f65b6978dba7ebd30bf1339c6ea47cd9a8fc58ebe7dfb3a95a7b3a307cef97d6a66e4bac5ce62b65ff1b3becb3d6ad3a9c5a5758bdfd518439f2d06a1c53563c073e6c52374a06b7d6edd177528e73b58645b218eb91682956c4c72412527f5d3bc180ee3d7ed9bf09867296ba7b09e920e5ce792cc893daa9b301fdd9e7e3005204b536804888b0a00010e80bd7f432ac1546fc42fb5591fe301f54f092eb6ed4112666d4780ae68dba1eadd9d15944e6735acc43c62bbf3fb50cb86a470a0c993778abb656c1529ecd4ab35e115e50f46e071672f2705f85e9d2d3b9fd27c2157f55bca901e9741a29d25fc8848abbe05976976ff3760886b2af8f09afdbe7ce009a1f071abd5c8c2f8463987b5b135f78419905ded02c3db0ae12e1cd516a80d76f401776c451209b0e736e028328b62d53e701b06b28504589b79d6428c5ad40fa584093d5ff703288677dce90740a065ec712c0fbb673b82d9c0311586fa1f2754f1b6d72a58c21093fe6c85272d048d6def29ed7aed54f44d565a2d4ae7d311c8dfdf581e05ff7a4194a554964ffbeebf994fe4f921abe8255053591cc6d813fdc8a76fe511a3d5f07826f5c3bc8cbbc847f182711e839e70e9f5db8f62d0efbf500e2d443b41d19f543f348ee93cad4a61c44b549ed18f13c8df18c723deee02b358be6609a9175576091a213a046d6c07089a2e20aa81465880d9e3e1d45f5f2c8b4e60f811545743a79c6055a43b23eb8fb53b65cabffc543a9823ac15aa1fd8795aa18020861002b8d256f36a0083d7b95cc7e1e1a0dd31bdb33e06b4c87c876f1fc3ac56b3a5bd1451f59b5a7938dae0f2fe5925e966a52c88a30588524b915f1e7a0a36739786c1d4fae5cfe0e2ed3459c4d1c3313905337d8dca9b9f9337302721d6b7c2ec74173223d0c895023a21f78b970d786d6ece5a7afcffa1d54e6cfa407708de67ab1fdefdc7d609998aa6035b841d32f9bbf8e9f13005bbc293c0ee8b9d9df5c921dd135d1883d1b7eae835fc52cf33129e891710781b8c68253647d19b1b9d3f447617c57814876ce166c53711682e8e48b32060bb1548da7270738e1f6b51b46f5f6792bc909f6f60e9350e0c379e6f31fbb4c6d50a0e502f46493f124d79822bd46b8193bb9158963c7f0418f232aebd6a9de464ff21abb6ba9ed56c6dcdfe7a187e70bb54b6714c55ef0db71eab65aa988793155abb09087f4f3d21d64f3206b3bfc660831206a9d347fb97cdeb580a2bca530aa9900645c6ed63aa56d8b0af3d3e6cb86d018f49682145f87dac0456cf1385642f326687eb64abd4265031f16a6b7c7aaeb0dbb727dc9d6ef6c720d9e57ba71106618c0e2fc30f622d42d18d59dc085849a279ff2ce9e0d8cef6b70fa078a26276a547927de3529578e7caec3335f1cf5b9463f3b0b2b55546c48929f96f7c746c7ac49998c9943b4a8ba2f9add15102eb8f59fbccd5ecb26728d5c9658c07f1055e2c0fc68d4a02318b3ce0b06f5ac352184eebd5871c17a257377d2505587bf68cd4fbf910fa402098e55259130033e953f074bbc64f7810f58f2e3b19dd3c6e239fc758e613c11963e4dffac8bc459892df3db42579472f44ee9cf0ea04124a28ac3a35c079256203e4ed54ee4be5d317f5b3e0171b8c7f96d78b9b7573f3e1756aef98ab244516dd75e11b0963dbf50015fd7162f0792ac8e5b129968fb6409c797068cab106028d19ba1d69a5d2f8784005f8ed21260458e2c09791e16d3a9a72716c4fad612ad9c8ad9b225bc16d8915c0ce1f4dc6679b150af5677251e3b1f46b23ff6948b39cc31705010d2b91bcec9b27b0708a6cc40ad81e7d722c9125db02d0d01c6df73fa7248952d45dd8247335643d70abfdd3e00e3344acdf9287c2f8a60a93b2b7f6905f987de25365cadb9c36b60188da601748db796a77f7ba65eb4ba7f826156b2cce15233e650e0a0de4ae66625bec59a1ec8c24697b798332aeb783b07e0ed73cfc34a2a4fb80991593d21bc711a002f918259f9db553b0b78be661f7434306ecaadafa9cc1e14d18b9cdabd7888ca83cbb5a8dcac11aa60946b5f7186f682ee4cb8096627c2f1454505865fbfd3ae50a7ff0a3b016a7174fc85c96965ae1bb6394b106f0f8647af8595ca56a77d2ada18f5e52d3cf9f438a2fb3756e971ed63e5d3efa5511823f148c2fe0bc4cbadd749df5fbbfd57b7bf499051ce161bc33c8a0dcdc76ce9628e4fcb61d349107ab415cdc138084932a3e9b0c3385effa4a671347dc20c394db8fa93101d507d92b6acff913b9482116c148535c963c07890fe3d98037a4dccfcc46790c7ae2e81b160c34199c24447171aaef65b92589bd2e9b8dc949abc77343c20174f8880e8291b881e7e5beb970c9daed480c7b4b8292fd2ef9f07b11a20a0c88bfc1f36daf82c5130e2bef078f8071de5f4ce1848772121dd58b2bcf4701698507ce0273cec58edef48e18c6cfc3efe75d62fe1141662e5def4a486369446d315b10c9378334aa3be6ba2d229f701f0d2c0267e146c9bad9393938a967cebf61396049cb4a22c15b71f4967159e464eeb3e195b7d967f40a0e1551856b5fc541d7bc2c88146064cf92d648b2b1e3ca34351af299f9f86b7039f17ac78ba621ece932ae30e96e9f10e12b3b94b04c19e662b6d8aaab4e564931cb15436b8dc4b4ba94515bd8e20fc8b7aa</script>  <div class="hbe hbe-content">    <div class="hbe hbe-input hbe-input-xray">      <input class="hbe hbe-input-field hbe-input-field-xray" type="password" id="hbePass">      <label class="hbe hbe-input-label hbe-input-label-xray" for="hbePass">        <span class="hbe hbe-input-label-content hbe-input-label-content-xray">请输入密码......</span>      </label>      <svg class="hbe hbe-graphic hbe-graphic-xray" width="300%" height="100%" viewBox="0 0 1200 60" preserveAspectRatio="none">        <path d="M0,56.5c0,0,298.666,0,399.333,0C448.336,56.5,513.994,46,597,46c77.327,0,135,10.5,200.999,10.5c95.996,0,402.001,0,402.001,0"></path>        <path d="M0,2.5c0,0,298.666,0,399.333,0C448.336,2.5,513.994,13,597,13c77.327,0,135-10.5,200.999-10.5c95.996,0,402.001,0,402.001,0"></path>      </svg>    </div>  </div></div><script data-pjax src="/lib/hbe.js"></script><link href="/css/hbe.style.css" rel="stylesheet" type="text/css">]]></content>
    
    
    <summary type="html">访问受限，请输入密码！</summary>
    
    
    
    <category term="LIFE" scheme="https://syshlang.com/categories/LIFE/"/>
    
    <category term="essay" scheme="https://syshlang.com/categories/LIFE/essay/"/>
    
    
    <category term="随笔" scheme="https://syshlang.com/tags/%E9%9A%8F%E7%AC%94/"/>
    
  </entry>
  
  <entry>
    <title>2021 年总结</title>
    <link href="https://syshlang.com/posts/9f7fa39c/"/>
    <id>https://syshlang.com/posts/9f7fa39c/</id>
    <published>2021-12-31T13:30:04.000Z</published>
    <updated>2025-05-22T15:08:23.923Z</updated>
    
    <content type="html"><![CDATA[<div class="hbe hbe-container" id="hexo-blog-encrypt" data-wpm="密码错误，请重新输入......" data-whm="无权限修改！">  <script id="hbeData" type="hbeData" data-hmacdigest="0ae50fcb2a9586674862f15306907af1d1ecdb522f9962ac7288bc26271a0a52">e6d62ced284323bbc94917fd4e3ae3c6357d3ba1e09c1943adbe610d55253e2f24d80109be1c5ea443fd8a2d9b613a19c74cad6dc569fc9fd6a07b58fb0bdb633b0beca95f8b7066ddb322bc2f72f03ce540b01189c7c6ec82925ecfd1036065b26b60010f051d210098acd2764ba03e2f791db9aeeacdcaca7340ecaf406297cda11d18d66425db2930a5a5af3cb05c360a497d615fe7080a9c89851d4fe0d5e0bcffb8ef742af002c6a0700844ff4d8c35739401f70efb6c0c47f6cb5f3c774eef62d24dfe492fc93d1f0c3a36ac29997a94844654e6810c7ca101ec2097a0ad79cfb78c5b235ffc067f1b3656059fc0712067ea92745fce062977db80f97dacd703ec88ea9ec171c0d03f5bb86701ad3ab4ca068f6c26d52a0685c36f0569366693cf3f94bc8fd3f67042ebdfbe38dbcd8f2ea2fcfd7831ecaf780c48aeeebf9c8b36ed4891fb99cdbcb14b6c1156b70076948587d5e9f5fa3709833adf13485cc647370c511a3fa17d074c50857212861a8e318f388349216dba9b6e92920d9ea220cc1520a2e35cddc4409f9f8e3141f28cc151dc8cac6fc714e00ad5580a5bd00be3e72b916f72df4b12ae41ce052cf404ca793ba8f07ac2342fc91688d9aa8e97f60a78a75da1b71fcc2fb26b247a99da5eec9cbee6d7ab8be47e5fa0d7706ed9a23eb09a7f6f44ba7b9005597cea0b11f71af9b174b0a8e35710d350749557b7bf5b3caa3a0f77d4b2b7159bf129c3051bf1e5323bfc2447f4829595ef9290774d71cba31e6ee0817fced999a80969c8a69a2f033070dbad2d32da4a9ed8915741877d3670c8953b6e10617347cd1e1089063ea4213c503ec5f9aef8292eeecad849ea6e07662d69ee6ff76843aaa490674c070a86fd4e7ddc0112b5f5c7c6903d4a88f91478257dd182534e517716a199abc1ef231857c702da20ea659d7f2ab562343990a25e3e113ae4c3444eb40a7ba0ac1bed87e43ee6f69f79</script>  <div class="hbe hbe-content">    <div class="hbe hbe-input hbe-input-xray">      <input class="hbe hbe-input-field hbe-input-field-xray" type="password" id="hbePass">      <label class="hbe hbe-input-label hbe-input-label-xray" for="hbePass">        <span class="hbe hbe-input-label-content hbe-input-label-content-xray">请输入密码......</span>      </label>      <svg class="hbe hbe-graphic hbe-graphic-xray" width="300%" height="100%" viewBox="0 0 1200 60" preserveAspectRatio="none">        <path d="M0,56.5c0,0,298.666,0,399.333,0C448.336,56.5,513.994,46,597,46c77.327,0,135,10.5,200.999,10.5c95.996,0,402.001,0,402.001,0"></path>        <path d="M0,2.5c0,0,298.666,0,399.333,0C448.336,2.5,513.994,13,597,13c77.327,0,135-10.5,200.999-10.5c95.996,0,402.001,0,402.001,0"></path>      </svg>    </div>  </div></div><script data-pjax src="/lib/hbe.js"></script><link href="/css/hbe.style.css" rel="stylesheet" type="text/css">]]></content>
    
    
    <summary type="html">访问受限，请输入密码！</summary>
    
    
    
    <category term="LIFE" scheme="https://syshlang.com/categories/LIFE/"/>
    
    <category term="essay" scheme="https://syshlang.com/categories/LIFE/essay/"/>
    
    
    <category term="随笔" scheme="https://syshlang.com/tags/%E9%9A%8F%E7%AC%94/"/>
    
  </entry>
  
  <entry>
    <title>Node.js 的包管理工具及其使用</title>
    <link href="https://syshlang.com/posts/85f40d7c/"/>
    <id>https://syshlang.com/posts/85f40d7c/</id>
    <published>2021-06-28T07:04:36.000Z</published>
    <updated>2025-12-01T14:31:49.859Z</updated>
    
    <content type="html"><![CDATA[<link rel="stylesheet" class="aplayer-secondary-style-marker" href="/assets/css/APlayer.min.css"><script src="/assets/js/APlayer.min.js" class="aplayer-secondary-script-marker"></script><script class="meting-secondary-script-marker" src="/assets/js/Meting.min.js"></script><p><a href="/categories/technology/javascript/nodejs-home/"><img src="https://oss.syshlang.com/blog/image/e295e892aef741369391a118dff3ae58.svg" style="width:500px;height:300px;background-color:#333" alt="node.js"></a></p><span id="more"></span><h1 id="一、Node-js的包管理工具的使用"><a href="#一、Node-js的包管理工具的使用" class="headerlink" title="一、Node.js的包管理工具的使用"></a>一、Node.js 的包管理工具的使用</h1><h2 id="1-1、包管理工具对比"><a href="#1-1、包管理工具对比" class="headerlink" title="1.1、包管理工具对比"></a>1.1、包管理工具对比</h2><blockquote><p>和 Node.js 版本管理工具一样，Node.js 的包管理工具也有很多个，同样先做个对比下：</p></blockquote><table><thead><tr><th align="center">名称</th><th align="left">安装方式<div style="width:300px"></div></th><th align="left">特点</th></tr></thead><tbody><tr><td align="center"><strong> npm</strong></td><td align="left"> 安装 node 时会自动同时安装 npm，无需额外安装</td><td align="left"><indent> nodejs 集成了 npm，是 nodejs 默认的包管理器。<a href="https://www.npmjs.com/package/npm5"><em><strong>npm5</strong></em></a> 之前的版本存在一系列的问题，例如：依赖树结构过长的问题 <a href="#refer-anchor-1"><sup>[1]</sup></a>、安装逻辑的问题 <a href="#refer-anchor-2"><sup>[2]</sup></a> 等等，这些问题导致 npm 使用过程中很不友好，由于 yarn 的出现，<a href="https://www.npmjs.com/package/npm5"><em><strong>npm5</strong></em></a> 进行了重大改进 <a href="#refer-anchor-3"><sup>[3]</sup></a>，npm6 加入了缓存，速度有了进一步提升，发展到现在 npm 的使用体验上有了很大的提升。</indent></td></tr><tr><td align="center"><strong>cnpm</strong></td><td align="left">npm install -g cnpm –registry=<span class="exturl" data-url="aHR0cHM6Ly9yZWdpc3RyeS5ucG0udGFvYmFvLm9yZy8=">https://registry.npm.taobao.org<i class="fa fa-external-link-alt"></i></span></td><td align="left">cnpm 是阿里巴巴为了提升国内下载速度定制的命令行工具，是一个完整 npmjs.org <span class="exturl" data-url="aHR0cHM6Ly93d3cubnBtbWlycm9yLmNvbS8=">国内镜像<i class="fa fa-external-link-alt"></i></span></td></tr><tr><td align="center"><strong>yarn</strong></td><td align="left">1. 使用 npm 工具安装模块：<br><strong><mark class="label success">npm install yarn -g</mark></strong><br> 2. 通过安装包独立安装（推荐）：<br>- Windows：<br>点击<a href="https://classic.yarnpkg.com/latest.msi"><em><strong>下载安装包</strong></em></a>安装<br>- Ubuntu: <br><strong><mark class="label success">sudo apt update &amp;&amp; sudo apt install yarn</mark></strong><br>3. 其他系统安装方式可访问官网了解详情： <br>- <a href="https://classic.yarnpkg.com/en/docs/install#windows-stable"><em><strong>Yarn 英文官网</strong></em></a> <br>- <a href="https://yarn.bootcss.com/docs/install/#windows-stable"><em><strong>Yarn 中文官网</strong></em></a></td><td align="left"><indent>由于 npm 存在的一些问题，使用体验不太好，<a href="https://yarnpkg.com/"><em><strong>yarn</strong></em></a> 应运而生，<a href="https://yarnpkg.com/"><em><strong>yarn</strong></em></a> 引入 yarn.lock 文件来管理依赖版本问题，保证每次安装都是一致的，并提供了离线模式，安装时缓存加并行下载速度得到了显著的提升，使用体验极佳。</indent></td></tr></tbody></table><h2 id="1-2、包管理器常用命令"><a href="#1-2、包管理器常用命令" class="headerlink" title="1.2、包管理器常用命令"></a>1.2、包管理器常用命令</h2><h3 id="1-2-1、npm和yarn管理器常用命令对比"><a href="#1-2-1、npm和yarn管理器常用命令对比" class="headerlink" title="1.2.1、npm和yarn管理器常用命令对比"></a>1.2.1、<a href="https://www.npmjs.com/"><em><strong>npm</strong></em></a> 和 <a href="https://yarnpkg.com/"><em><strong>yarn</strong></em></a> 管理器常用命令对比</h3><p><img src="https://oss.syshlang.com/blog/image/ba395678f93c4478a55acdafaa0cbd43.png" alt="npm VS yarn"></p><table><thead><tr><th align="center">Command</th><th align="center">npm</th><th align="center">yarn</th></tr></thead><tbody><tr><td align="center"> 安装依赖</td><td align="center"><code>npm install</code></td><td align="center"><code>yarn install</code></td></tr><tr><td align="center">安装包</td><td align="center"><code>npm install [package]</code></td><td align="center"><code>yarn add [package]</code></td></tr><tr><td align="center">安装开发包</td><td align="center"><code>npm install --save-dev [package]</code></td><td align="center"><code>yarn add --dev [package]</code></td></tr><tr><td align="center">卸载包</td><td align="center"><code>npm uninstall [package]</code></td><td align="center"><code>yarn remove [package]</code></td></tr><tr><td align="center">卸载开发包</td><td align="center"><code>npm uninstall --save-dev [package]</code></td><td align="center"><code>yarn remove [package]</code></td></tr><tr><td align="center">更新</td><td align="center"><code>npm update</code></td><td align="center"><code>yarn upgrade</code></td></tr><tr><td align="center">更新包</td><td align="center"><code>npm update [package]</code></td><td align="center"><code>yarn upgrade [package]</code></td></tr><tr><td align="center">全局安装包</td><td align="center"><code>npm install --global [package]</code></td><td align="center"><code>yarn global add [package]</code></td></tr><tr><td align="center">全局卸载包</td><td align="center"><code>npm uninstall --global [package]</code></td><td align="center"><code>yarn global remove [package]</code></td></tr><tr><td align="center">—</td><td align="center">—</td><td align="center">—</td></tr><tr><td align="center"> 初始化项目</td><td align="center"><code>npm init</code></td><td align="center"><code>yarn init</code></td></tr><tr><td align="center">运行脚本</td><td align="center"><code>npm run [script]</code></td><td align="center"><code>yarn run [script]</code></td></tr><tr><td align="center">运行测试脚本</td><td align="center"><code>npm test</code></td><td align="center"><code>yarn test</code></td></tr><tr><td align="center">登录 / 登出</td><td align="center"><code>npm login/logout</code></td><td align="center"><code>yarn login/logout</code></td></tr><tr><td align="center">链接 / 取消链接包</td><td align="center"><code>npm link/unlink [package]</code></td><td align="center"><code>yarn link/unlink [package]</code></td></tr><tr><td align="center">发布软件包</td><td align="center"><code>npm publish</code></td><td align="center"><code>yarn publish</code></td></tr><tr><td align="center">清除全局缓存</td><td align="center"><code>npm cache clean</code></td><td align="center"><code>yarn cache clean</code></td></tr><tr><td align="center">设置配置</td><td align="center"><code>npm config set &lt;key&gt; &lt;value&gt;</code></td><td align="center"><code>yarn config set &lt;key&gt; &lt;value&gt;</code></td></tr><tr><td align="center">删除配置</td><td align="center"><code>npm config delete &lt;key&gt;</code></td><td align="center"><code>yarn config delete &lt;key&gt;</code></td></tr></tbody></table><h3 id="1-2-1、其他命令"><a href="#1-2-1、其他命令" class="headerlink" title="1.2.1、其他命令"></a>1.2.1、其他命令</h3><blockquote><p>设置镜像源</p></blockquote><figure class="highlight plaintext"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><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">// npm设置淘宝源和官方源</span><br><span class="line">npm config set registry http://registry.npm.taobao.org/</span><br><span class="line">npm config set registry https://registry.npmjs.org/</span><br><span class="line">// yarn设淘宝源和官方源</span><br><span class="line">yarn config set registry http://registry.npm.taobao.org/</span><br><span class="line">yarn config set registry https://registry.npmjs.org/</span><br></pre></td></tr></tbody></table></figure><blockquote><p>代理配置</p></blockquote><figure class="highlight plaintext"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><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">// npm代理配置</span><br><span class="line">npm config set proxy http://username:password@server:port</span><br><span class="line">npm confit set https-proxy http://username:password@server:port</span><br><span class="line">// 或者编辑用户目录下的~/.npmrc file</span><br><span class="line">proxy=http://username:password@host:port</span><br><span class="line">https-proxy=http://username:password@host:port</span><br><span class="line"></span><br><span class="line">// yarn代理配置</span><br><span class="line">yarn config set proxy http://username:password@host:port</span><br><span class="line">yarn config set https-proxy http://username:password@host:port</span><br></pre></td></tr></tbody></table></figure><blockquote><p>取消代理</p></blockquote><figure class="highlight plaintext"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><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">// npm取消代理</span><br><span class="line">npm config delete proxy</span><br><span class="line">npm config delete https-proxy</span><br><span class="line"></span><br><span class="line">// yarn取消代理</span><br><span class="line">yarn config delete proxy</span><br><span class="line">yarn config delete https-proxy</span><br></pre></td></tr></tbody></table></figure><p><font color="black" size="5" face="华文行楷">附：</font><br></p><ul><li><a href="https://classic.yarnpkg.com/en/docs/cli/"><em><strong>Yarn 命令行命令介绍</strong></em></a></li><li><a href="https://docs.npmjs.com/cli/v7/commands"><em><strong>npm 命令行命令介绍</strong></em></a></li></ul><hr><div id="refer-anchor-1"><ul><li> [1] <strong>npm 2 依赖树结构过长的问题</strong><details class="note warning no-icon"><summary><p>点击显 / 隐</p></summary><p><indent>npm2 版本时期，安装每一个包所依赖的所有依赖项，如果依赖的数据层级很多，那么依赖树结构会很长，这种情况对于基于 Unix 的操作系统来说影响不大，但是在 Windows 下很多程序无法处理超过 260 个字符的文件路径名，这就会导致致命的错误，为了解决这个问题，npm 3 采用了扁平依赖关系树，也就是将依赖包的目录层级从嵌套变到扁平化。</indent></p></details></li></ul><div id="refer-anchor-2"><ul><li>[2] <strong>安装逻辑的问题</strong><details class="note warning no-icon"><summary><p>点击显 / 隐</p></summary><p><indent>npm 的早期版本，无法并行下载，也就是说 npm 在安装依赖的过程中，同一个时刻只能有一个模块在下载、解析、安装，这很大程度上影响了安装依赖的速度。  </indent></p></details></li></ul><div id="refer-anchor-3"><ul><li>[3] <strong><a href="https://www.npmjs.com/package/npm5"><em><strong>npm5</strong></em></a> 进行了重大改进</strong><details class="note warning no-icon"><summary><p>点击显 / 隐</p></summary><p><indent><a href="https://www.npmjs.com/package/npm5"><em><strong>npm5</strong></em></a> 引入离线缓存，提高了安装速度，也引入了 package-lock.json 文件增强了版本控制。  </indent></p></details></li></ul></div></div></div><link rel="stylesheet" href="https://fastly.jsdelivr.net/npm/markmap-toolbar@0.18.10/dist/style.css"><script src="https://fastly.jsdelivr.net/npm/d3@7"></script><script src="https://fastly.jsdelivr.net/npm/markmap-view@0.18.10"></script><script src="https://fastly.jsdelivr.net/npm/markmap-toolbar@0.18.10"></script><link rel="stylesheet" href="/css/markmap.css"><script src="/js/markmap.js"></script>]]></content>
    
    
    <summary type="html">&lt;p&gt;&lt;a href=&quot;/categories/technology/javascript/nodejs-home/&quot;&gt;&lt;img src=&quot;https://oss.syshlang.com/blog/image/e295e892aef741369391a118dff3ae58.svg&quot; style=&quot;width:500px;height:300px;background-color:#333&quot; alt=&quot;node.js&quot;&gt;&lt;/a&gt;&lt;/p&gt;</summary>
    
    
    
    <category term="CODING" scheme="https://syshlang.com/categories/CODING/"/>
    
    <category term="前端" scheme="https://syshlang.com/categories/CODING/%E5%89%8D%E7%AB%AF/"/>
    
    <category term="nodejs" scheme="https://syshlang.com/categories/CODING/%E5%89%8D%E7%AB%AF/nodejs/"/>
    
    
    <category term="JavaScript" scheme="https://syshlang.com/tags/JavaScript/"/>
    
    <category term="nodeJS" scheme="https://syshlang.com/tags/nodeJS/"/>
    
    <category term="nvm" scheme="https://syshlang.com/tags/nvm/"/>
    
  </entry>
  
  <entry>
    <title>Node.js 版本管理工具及其使用</title>
    <link href="https://syshlang.com/posts/23ec03a4/"/>
    <id>https://syshlang.com/posts/23ec03a4/</id>
    <published>2021-06-27T05:46:36.000Z</published>
    <updated>2025-12-01T14:31:49.597Z</updated>
    
    <content type="html"><![CDATA[<link rel="stylesheet" class="aplayer-secondary-style-marker" href="/assets/css/APlayer.min.css"><script src="/assets/js/APlayer.min.js" class="aplayer-secondary-script-marker"></script><script class="meting-secondary-script-marker" src="/assets/js/Meting.min.js"></script><p><a href="/categories/technology/javascript/nodejs-home/"><img src="https://oss.syshlang.com/blog/image/e295e892aef741369391a118dff3ae58.svg" style="width:500px;height:300px;background-color:#333" alt="node.js"></a></p><span id="more"></span><h1 id="一、Node-js版本管理工具的使用"><a href="#一、Node-js版本管理工具的使用" class="headerlink" title="一、Node.js版本管理工具的使用"></a>一、Node.js 版本管理工具的使用</h1><h2 id="1-1、版本管理工具对比"><a href="#1-1、版本管理工具对比" class="headerlink" title="1.1、版本管理工具对比"></a>1.1、版本管理工具对比</h2><blockquote><p>Node.js 版本管理工具有很多个，首先简单对比下：</p></blockquote><table><thead><tr><th align="center">名称</th><th align="center">适用系统</th><th align="left">安装方式<div style="width:165px"></div></th><th align="left">常用命令<div style="width:155px"></div></th><th align="left">其它</th></tr></thead><tbody><tr><td align="center"><strong> n</strong></td><td align="center">Linux / Unix / macOS X</td><td align="left"> 安装命令：<br><strong><mark class="label success">sudo npm install n -g</mark></strong></td><td align="left"><a href="#1-2-1%E3%80%81n%E7%AE%A1%E7%90%86%E5%99%A8%E5%B8%B8%E7%94%A8%E5%91%BD%E4%BB%A4"><em><strong>n 管理器常用命令</strong></em></a></td><td align="left"> n 是 npm 的一个全局模块，因此安装 n 之前需要先安装 node，然后借助 npm 来安装</td></tr><tr><td align="center"><strong> nvm</strong></td><td align="center">Linux / Unix / macOS X</td><td align="left"><a href="https://github.com/nvm-sh/nvm"><em><strong>nvm 安装方式</strong></em></a></td><td align="left"><a href="#1-2-2%E3%80%81nvm%E7%AE%A1%E7%90%86%E5%99%A8%E5%B8%B8%E7%94%A8%E5%91%BD%E4%BB%A4"><em><strong>nvm 管理器常用命令</strong></em></a></td><td align="left"> nvm 是一个独立软件包，因此它可以不依赖 npm 直接安装</td></tr><tr><td align="center"><strong> nvm-windows</strong></td><td align="center">Windows</td><td align="left"><a href="https://github.com/coreybutler/nvm-windows/"><em><strong>nvm-windows 安装方式</strong></em></a></td><td align="left"><a href="#1-2-2%E3%80%81nvm%E7%AE%A1%E7%90%86%E5%99%A8%E5%B8%B8%E7%94%A8%E5%91%BD"><em><strong>nvm 管理器常用命令</strong></em></a></td><td align="left">由于 n 和 nvm 本身不支持 Windows，于是就有了 <a href="https://github.com/coreybutler/nvm-windows"><em><strong>nvm-windows</strong></em></a>，它是一个开源的用于 Windows 环境的 nvm 管理器</td></tr><tr><td align="center"><strong> nvmw</strong></td><td align="center">Windows</td><td align="left"><a href="https://github.com/hakobera/nvmw"><em><strong>nvmw 安装方式</strong></em></a></td><td align="left"><a href="#1-2-3%E3%80%81nvmw%E7%AE%A1%E7%90%86%E5%99%A8%E5%B8%B8%E7%94%A8%E5%91%BD%E4%BB%A4"><em><strong>nvmw 管理器常用命令</strong></em></a></td><td align="left"> node version manager for window</td></tr><tr><td align="center"><strong>nvs</strong></td><td align="center">Windows / Linux / Unix / macOS X</td><td align="left"><a href="https://github.com/jasongin/nvs"><em><strong>nvs 安装方式</strong></em></a></td><td align="left"><a href="#1-2-4%E3%80%81nvs%E7%AE%A1%E7%90%86%E5%99%A8%E5%B8%B8%E7%94%A8%E5%91%BD%E4%BB%A4"><em><strong>nvs 管理器常用命令</strong></em></a></td><td align="left"> nvs 是一个跨平台的版本管理器，适用于多种操作系统。</td></tr></tbody></table><h2 id="1-2、版本管理器常用命令"><a href="#1-2、版本管理器常用命令" class="headerlink" title="1.2、版本管理器常用命令"></a>1.2、版本管理器常用命令</h2><blockquote><p>各管理器在命令行的使用过程中，均可使用 help 帮助命令查询相关用法，例如</p></blockquote><figure class="highlight shell"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">root@syshlang:~$ nvm help</span><br></pre></td></tr></tbody></table></figure><p><img src="https://oss.syshlang.com/blog/image/d28877f0b3874884959637a1540ca39c.png" alt="使用帮助命令"><br><indent>以下列出各管理器常用命令</indent></p><h3 id="1-2-1、n管理器常用命令"><a href="#1-2-1、n管理器常用命令" class="headerlink" title="1.2.1、n管理器常用命令"></a>1.2.1、n 管理器常用命令</h3><figure class="highlight shell"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><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 prompt_"># </span><span class="language-bash">查看已安装的node版本</span></span><br><span class="line">n</span><br><span class="line"><span class="meta prompt_"># </span><span class="language-bash">查看可用的Node版</span></span><br><span class="line">n ls</span><br><span class="line"><span class="meta prompt_"># </span><span class="language-bash">安装稳定版本node</span></span><br><span class="line">n stable</span><br><span class="line"><span class="meta prompt_"># </span><span class="language-bash">安装最新的版本node</span></span><br><span class="line">n latest</span><br><span class="line"><span class="meta prompt_"># </span><span class="language-bash">安装或使用某个版本node</span></span><br><span class="line">n [version]</span><br><span class="line"><span class="meta prompt_"># </span><span class="language-bash">卸载某个版本node</span></span><br><span class="line">n rm [version]</span><br><span class="line"><span class="meta prompt_"># </span><span class="language-bash">以指定的版本node来执行脚本</span></span><br><span class="line">n use [version] [xxx.js]</span><br></pre></td></tr></tbody></table></figure><h3 id="1-2-2、nvm管理器常用命令"><a href="#1-2-2、nvm管理器常用命令" class="headerlink" title="1.2.2、nvm管理器常用命令"></a>1.2.2、nvm 管理器常用命令</h3><figure class="highlight shell"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta prompt_"># </span><span class="language-bash">查看已经安装的node版本</span></span><br><span class="line">nvm list</span><br><span class="line">nvm ls</span><br><span class="line">nvm list installed</span><br><span class="line">nvm ls installed</span><br><span class="line"><span class="meta prompt_"># </span><span class="language-bash">查看网络可以安装的node版本 （window用户）</span></span><br><span class="line">nvm list available</span><br><span class="line">nvm ls available</span><br><span class="line"><span class="meta prompt_"># </span><span class="language-bash">查看网络可以安装的node版本 （非window用户）</span></span><br><span class="line">nvm ls-remote</span><br><span class="line"><span class="meta prompt_"># </span><span class="language-bash">安装最新的node版本</span></span><br><span class="line">nvm install latest</span><br><span class="line"><span class="meta prompt_"># </span><span class="language-bash">安装某个版本node</span></span><br><span class="line">nvm install [version]</span><br><span class="line"><span class="meta prompt_"># </span><span class="language-bash">卸载某个版本node</span></span><br><span class="line">nvm uninstall [version]</span><br><span class="line"><span class="meta prompt_"># </span><span class="language-bash">切换node版本至某个版本</span></span><br><span class="line">nvm use [version]</span><br><span class="line"><span class="meta prompt_"># </span><span class="language-bash">设置nodejs镜像和npm镜像</span></span><br><span class="line">nvm node_mirror https://npm.taobao.org/mirrors/node/</span><br><span class="line">nvm npm_mirror https://npm.taobao.org/mirrors/npm/</span><br></pre></td></tr></tbody></table></figure><blockquote><p>除了通过命令设置 nodejs 镜像和 npm 镜像外，还可以以修改配置文件的方式，打开 nvm 文件夹下 settings.txt 文件进行修改：</p></blockquote><figure class="highlight txt"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line">root: D:\nvm</span><br><span class="line">arch: 64</span><br><span class="line">proxy: none</span><br><span class="line">originalpath:</span><br><span class="line">originalversion:</span><br><span class="line">node_mirror: https://npm.taobao.org/mirrors/node/</span><br><span class="line">npm_mirror: https://npm.taobao.org/mirrors/npm/</span><br></pre></td></tr></tbody></table></figure><h3 id="1-2-3、nvmw管理器常用命令"><a href="#1-2-3、nvmw管理器常用命令" class="headerlink" title="1.2.3、nvmw管理器常用命令"></a>1.2.3、nvmw 管理器常用命令</h3><figure class="highlight shell"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><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 prompt_"># </span><span class="language-bash">查看已经安装的node版本</span></span><br><span class="line">nvmw ls</span><br><span class="line"><span class="meta prompt_"># </span><span class="language-bash">安装某个版本node</span></span><br><span class="line">nvmw install [version]</span><br><span class="line"><span class="meta prompt_"># </span><span class="language-bash">卸载某个版本node</span></span><br><span class="line">nvmw uninstall [version]</span><br><span class="line"><span class="meta prompt_"># </span><span class="language-bash">切换node版本至某个版本</span></span><br><span class="line">nvmw use [version]</span><br><span class="line"><span class="meta prompt_"># </span><span class="language-bash">永久切换node版本</span></span><br><span class="line">nvmw switch  [version]</span><br></pre></td></tr></tbody></table></figure><h3 id="1-2-4、nvs管理器常用命令"><a href="#1-2-4、nvs管理器常用命令" class="headerlink" title="1.2.4、nvs管理器常用命令"></a>1.2.4、nvs 管理器常用命令</h3><figure class="highlight shell"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><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 prompt_"># </span><span class="language-bash">初始化并使用 NVS</span></span><br><span class="line">nvs install</span><br><span class="line"><span class="meta prompt_"># </span><span class="language-bash">从 profile 和 environment 中移除 NVS</span></span><br><span class="line">nvs uninstall</span><br><span class="line"><span class="meta prompt_"># </span><span class="language-bash">安装某个版本node</span></span><br><span class="line">nvs add [version]</span><br><span class="line"><span class="meta prompt_"># </span><span class="language-bash">安装最新版本的node</span></span><br><span class="line">nvs add latest</span><br><span class="line"><span class="meta prompt_"># </span><span class="language-bash">安装lts版本的node</span></span><br><span class="line">nvs add lts</span><br><span class="line"><span class="meta prompt_"># </span><span class="language-bash">移除某个版本的 Node</span></span><br><span class="line">nvs rm [version]</span><br><span class="line"><span class="meta prompt_"># </span><span class="language-bash">查看已经安装的版本</span></span><br><span class="line">nvs ls</span><br><span class="line"><span class="meta prompt_"># </span><span class="language-bash">查看网络可以安装的node版本</span></span><br><span class="line">nvs ls-remote</span><br><span class="line"><span class="meta prompt_"># </span><span class="language-bash">更新当前环境的 node 至最新版本</span></span><br><span class="line">nvs upgrade</span><br></pre></td></tr></tbody></table></figure><hr><p><font color="black" size="5" face="华文行楷">附：</font><a href="https://nodejs.org/zh-cn/download/package-manager/"><em><strong>不同操作系统通过包管理器方式安装 Node.js</strong></em></a></p><link rel="stylesheet" href="https://fastly.jsdelivr.net/npm/markmap-toolbar@0.18.10/dist/style.css"><script src="https://fastly.jsdelivr.net/npm/d3@7"></script><script src="https://fastly.jsdelivr.net/npm/markmap-view@0.18.10"></script><script src="https://fastly.jsdelivr.net/npm/markmap-toolbar@0.18.10"></script><link rel="stylesheet" href="/css/markmap.css"><script src="/js/markmap.js"></script>]]></content>
    
    
    <summary type="html">&lt;p&gt;&lt;a href=&quot;/categories/technology/javascript/nodejs-home/&quot;&gt;&lt;img src=&quot;https://oss.syshlang.com/blog/image/e295e892aef741369391a118dff3ae58.svg&quot; style=&quot;width:500px;height:300px;background-color:#333&quot; alt=&quot;node.js&quot;&gt;&lt;/a&gt;&lt;/p&gt;</summary>
    
    
    
    <category term="CODING" scheme="https://syshlang.com/categories/CODING/"/>
    
    <category term="前端" scheme="https://syshlang.com/categories/CODING/%E5%89%8D%E7%AB%AF/"/>
    
    <category term="nodejs" scheme="https://syshlang.com/categories/CODING/%E5%89%8D%E7%AB%AF/nodejs/"/>
    
    
    <category term="JavaScript" scheme="https://syshlang.com/tags/JavaScript/"/>
    
    <category term="nodeJS" scheme="https://syshlang.com/tags/nodeJS/"/>
    
    <category term="nvm" scheme="https://syshlang.com/tags/nvm/"/>
    
  </entry>
  
  <entry>
    <title>Node.js 安装及配置</title>
    <link href="https://syshlang.com/posts/6a97bf21/"/>
    <id>https://syshlang.com/posts/6a97bf21/</id>
    <published>2021-06-26T10:46:36.000Z</published>
    <updated>2025-12-01T14:31:48.552Z</updated>
    
    <content type="html"><![CDATA[<link rel="stylesheet" class="aplayer-secondary-style-marker" href="/assets/css/APlayer.min.css"><script src="/assets/js/APlayer.min.js" class="aplayer-secondary-script-marker"></script><script class="meting-secondary-script-marker" src="/assets/js/Meting.min.js"></script><p><a href="/categories/technology/javascript/nodejs-home/"><img src="https://oss.syshlang.com/blog/image/e295e892aef741369391a118dff3ae58.svg" style="width:500px;height:300px;background-color:#333" alt="node.js"></a></p><span id="more"></span><h1 id="一、Node-js安装及环境配置"><a href="#一、Node-js安装及环境配置" class="headerlink" title="一、Node.js安装及环境配置"></a>一、Node.js 安装及环境配置</h1><h2 id="1、Windows下安装及配置"><a href="#1、Windows下安装及配置" class="headerlink" title="1、Windows下安装及配置"></a>1、Windows 下安装及配置</h2><h3 id="1-1、下载安装"><a href="#1-1、下载安装" class="headerlink" title="1.1、下载安装"></a>1.1、下载安装</h3><ul><li><a href="http://nodejs.cn/download/"><em><strong>Node.js 中文网</strong></em></a><br><img src="https://oss.syshlang.com/blog/image/44b7d5bf920444aba438489028a6832d.png" alt="中文网下载安装包"></li><li><a href="https://nodejs.org/en/download/"><em><strong>Node.js 官网</strong></em></a><br><img src="https://oss.syshlang.com/blog/image/1d4a8aac16014a3bb62121c9a6bb6196.png" alt="官网下载安装包"></li></ul><div class="note info"><p><strong>LTS 和 Current</strong>： LTS（<mark class="label info">Long Term Support</mark>）长期维护版本，相对稳定；Current 最新发布的版本。一般选择 LTS 版本。<br><strong>安装包</strong>：一键安装选择 xxx.msi 的安装包，直接点击下一步直至安装完成，安装过程会自动配置环境变量；绿色版安装选择 xxx.zip 的安装包，需要手动解压并<a href="#1-2%E3%80%81%E7%8E%AF%E5%A2%83%E5%8F%98%E9%87%8F%E9%85%8D%E7%BD%AE"><em><strong>配置环境变量</strong></em></a></p><p><mark class="label danger">* 目前最新版本的 Nodejs 已不再支持 Windows7，如需在 Windows7 上安装 Nodejs，需安装 12.19.1 或之前的版本。</mark></p></div><h3 id="1-2、环境变量配置"><a href="#1-2、环境变量配置" class="headerlink" title="1.2、环境变量配置"></a>1.2、环境变量配置</h3><ul><li>新建系统变量 </li></ul><figure class="highlight shell"><table><tbody><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">变量名：NODE_HOME</span><br><span class="line">变量值：Node.js安装目录，如：D:\Program Files\Node</span><br></pre></td></tr></tbody></table></figure><ul><li>编辑 Path 变量，新增 </li></ul><figure class="highlight shell"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta prompt_">%</span><span class="language-bash">NODE_HOME%</span></span><br></pre></td></tr></tbody></table></figure><p> 如果一切正常，此时在终端输入 node -v 和 npm -v 即可显示相应的版本号。</p><ul><li>设置 npm 安装程序时的默认目录（该步骤可忽略）</li></ul><figure class="highlight shell"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta prompt_"># </span><span class="language-bash">设置npm全局安装模的默认位置</span></span><br><span class="line">npm config set prefix "D:\Program Files\Node\node_global"</span><br><span class="line"><span class="meta prompt_"># </span><span class="language-bash">设置npm全局安装模的缓存位置</span></span><br><span class="line">npm config set cache "D:\Program Files\Node\node_cache"</span><br><span class="line"><span class="meta prompt_"># </span><span class="language-bash">设置环境变量NODE_PATH</span></span><br><span class="line">变量名：NODE_PATH</span><br><span class="line">变量值：npm全局安装模的默认位置的node_modules目录，如： D:\Program Files\Node\node_global\node_modules</span><br></pre></td></tr></tbody></table></figure><p> 如果一切正常，此时在终端输入 npm root -g 即可显示全局安装模的默认位置。</p><h2 id="2、Linux下安装及配置（以Ubuntu20为例）"><a href="#2、Linux下安装及配置（以Ubuntu20为例）" class="headerlink" title="2、Linux下安装及配置（以Ubuntu20为例）"></a>2、Linux 下安装及配置（以 Ubuntu20 为例）</h2><figure class="highlight shell"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><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 prompt_"># </span><span class="language-bash">更新ubuntu软件源</span></span><br><span class="line">sudo apt-get update</span><br><span class="line">sudo apt-get install -y python-software-properties software-properties-common</span><br><span class="line"><span class="meta prompt_"># </span><span class="language-bash">添加nodejs的软件源</span></span><br><span class="line">sudo add-apt-repository ppa:chris-lea/node.js</span><br><span class="line">sudo apt-get update</span><br><span class="line"><span class="meta prompt_"># </span><span class="language-bash">安装nodejs</span></span><br><span class="line">sudo apt-get install nodejs</span><br><span class="line">sudo apt install nodejs-legacy</span><br><span class="line"><span class="meta prompt_"># </span><span class="language-bash">安装npm包管理工具</span></span><br><span class="line">sudo apt install npm</span><br></pre></td></tr></tbody></table></figure><p>经过上面一顿操作，nodeJs 已经安装到 Ubuntu 环境，并自动配置好了环境变量，输入相应的命令即可查看版本。</p><p>必要时配置下环境变量，一般不需要</p><figure class="highlight shell"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">root@syshlang:~$ vi /etc/profile</span><br></pre></td></tr></tbody></table></figure><p>在末尾追加</p><figure class="highlight shell"><table><tbody><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">export NODE_HOME=/usr/local/node</span><br><span class="line">export PATH=$NODE_HOME/bin:$PATH</span><br></pre></td></tr></tbody></table></figure><p>刷新配置，使环境变量生效</p><figure class="highlight shell"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">root@syshlang:~$ source /etc/profile</span><br></pre></td></tr></tbody></table></figure><link rel="stylesheet" href="https://fastly.jsdelivr.net/npm/markmap-toolbar@0.18.10/dist/style.css"><script src="https://fastly.jsdelivr.net/npm/d3@7"></script><script src="https://fastly.jsdelivr.net/npm/markmap-view@0.18.10"></script><script src="https://fastly.jsdelivr.net/npm/markmap-toolbar@0.18.10"></script><link rel="stylesheet" href="/css/markmap.css"><script src="/js/markmap.js"></script>]]></content>
    
    
    <summary type="html">&lt;p&gt;&lt;a href=&quot;/categories/technology/javascript/nodejs-home/&quot;&gt;&lt;img src=&quot;https://oss.syshlang.com/blog/image/e295e892aef741369391a118dff3ae58.svg&quot; style=&quot;width:500px;height:300px;background-color:#333&quot; alt=&quot;node.js&quot;&gt;&lt;/a&gt;&lt;/p&gt;</summary>
    
    
    
    <category term="CODING" scheme="https://syshlang.com/categories/CODING/"/>
    
    <category term="前端" scheme="https://syshlang.com/categories/CODING/%E5%89%8D%E7%AB%AF/"/>
    
    <category term="操作系统" scheme="https://syshlang.com/categories/CODING/%E6%93%8D%E4%BD%9C%E7%B3%BB%E7%BB%9F/"/>
    
    <category term="nodejs" scheme="https://syshlang.com/categories/CODING/%E5%89%8D%E7%AB%AF/nodejs/"/>
    
    <category term="windows" scheme="https://syshlang.com/categories/CODING/%E6%93%8D%E4%BD%9C%E7%B3%BB%E7%BB%9F/windows/"/>
    
    <category term="linux" scheme="https://syshlang.com/categories/CODING/%E6%93%8D%E4%BD%9C%E7%B3%BB%E7%BB%9F/linux/"/>
    
    
    <category term="JavaScript" scheme="https://syshlang.com/tags/JavaScript/"/>
    
    <category term="nodeJS" scheme="https://syshlang.com/tags/nodeJS/"/>
    
    <category term="nvm" scheme="https://syshlang.com/tags/nvm/"/>
    
  </entry>
  
  <entry>
    <title>使用应用诊断利器 Arthas 定位 Java 线上问题</title>
    <link href="https://syshlang.com/posts/b0f55f17/"/>
    <id>https://syshlang.com/posts/b0f55f17/</id>
    <published>2021-05-19T02:31:20.000Z</published>
    <updated>2025-12-01T14:31:49.377Z</updated>
    
    <content type="html"><![CDATA[<link rel="stylesheet" class="aplayer-secondary-style-marker" href="/assets/css/APlayer.min.css"><script src="/assets/js/APlayer.min.js" class="aplayer-secondary-script-marker"></script><script class="meting-secondary-script-marker" src="/assets/js/Meting.min.js"></script><p><img src="https://oss.syshlang.com/blog/image/87b4e519b443444bbd40126452cb860e.png" alt="Arthas 是Alibaba开源的Java诊断工具"></p><span id="more"></span><p><indent>最近新系统上线之后，在早高峰使用期，访问系统会变得很卡很慢，在 linux (CentOS 7) 生产服务器上，经过一系列简单的排查分析之后，发现导致慢的原因可能是 JVM 配置的内存太小，于是对 Tomcat (tomcat-9.0.34) 的 JVM 内存配置加大，重启 Tomcat，访问系统速度明显变快。但是，系统又运行了一段时间之后，访问又开始变得卡了起来，于是重复操作加大内存配置，问题解决。你以为这样就结束了？在一段时间之后的某个早晨，群里炸开了锅，系统崩了，情况紧急，直接将 JVM 内存配置干到了 16G，在此之后，系统的访问速度恢复正常，几乎再没出现过类似的情况。但是，这样就完事了吗？冷静分析下，就我们系统目前的这个量级，如果说需要配置 16G 的内存，属实有点夸张，很大程度上可能是我们的开发人员写的代码有问题，于是考虑使用线上诊断工具查找一下问题，工具的第一选择便是 Arthas。</indent></p><h1 id="一、Arthas-安装运行"><a href="#一、Arthas-安装运行" class="headerlink" title="一、Arthas 安装运行"></a>一、Arthas 安装运行</h1><p>Arthas 官方提供了多种安装：</p><blockquote><ol><li>下载 arthas-boot.jar，然后运行 jar 包（推荐）</li></ol></blockquote><figure class="highlight plaintext"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><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"># 下载arthas-boot.jar</span><br><span class="line">curl -O https://arthas.aliyun.com/arthas-boot.jar</span><br><span class="line"># 用java -jar的方式启动</span><br><span class="line">java -jar arthas-boot.jar</span><br><span class="line"># 打印帮助信息</span><br><span class="line">java -jar arthas-boot.jar -h</span><br></pre></td></tr></tbody></table></figure><p>运行之后，选择对应的 java 应用进程编号，即可进入 Arthas 的命令行操作界面<br><img src="https://oss.syshlang.com/blog/image/aa277593e73341c8bbe3d20fa8ab4ef4.png" alt="运行 Arthas"></p><blockquote><ol start="2"><li>使用 as.sh 一键安装启动 </li></ol></blockquote><figure class="highlight plaintext"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><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"># 运行一下命令会下载启动脚本文件 as.sh 到当前目录</span><br><span class="line">curl -L https://arthas.aliyun.com/install.sh | sh</span><br><span class="line"># 运行脚本启动</span><br><span class="line">./as.sh</span><br><span class="line"># 打印帮助信息</span><br><span class="line">./as.sh -h</span><br></pre></td></tr></tbody></table></figure><blockquote><ol start="3"><li>下载全量安装包进行安装运行</li></ol></blockquote><p>点击<a class="reference external" href="https://arthas.aliyun.com/download/latest_version?mirror=aliyun"><img alt="Arthas" src="https://img.shields.io/maven-central/v/com.taobao.arthas/arthas-packaging.svg?style=flat-square" data-spm-anchor-id="0.0.0.i23.2fc817834OCPHW" style="margin-bottom: -5px;display: inline !important;"></a>下载最新版本的 jar 包，解压后，在文件夹里有 arthas-boot.jar，然后按照 1 中的方式运行</p><blockquote><ol start="4"><li>通过操作系统的软件包管理器（rpm/deb）安装 </li></ol></blockquote><figure class="highlight plaintext"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><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"># 安装deb</span><br><span class="line">sudo dpkg -i arthas*.deb</span><br><span class="line"># 安装rpm</span><br><span class="line">sudo rpm -i arthas*.rpm</span><br><span class="line"># 安装完成之后执行as.sh启动</span><br><span class="line">./as.sh</span><br></pre></td></tr></tbody></table></figure><h1 id="二、Arthas-常用命令"><a href="#二、Arthas-常用命令" class="headerlink" title="二、Arthas 常用命令"></a>二、Arthas 常用命令</h1><table><thead><tr><th align="center">命令</th><th align="center">说明 </th></tr></thead><tbody><tr><td align="center"><a href="https://arthas.aliyun.com/doc/dashboard.html"><strong>dashboard</strong></a></td><td align="center"> 当前系统的实时数据面板 </td></tr><tr><td align="center"><a href="https://arthas.aliyun.com/doc/thread.html"><strong>thread</strong></a></td><td align="center"> 查看当前 JVM 的线程堆栈信息 </td></tr><tr><td align="center"><a href="https://arthas.aliyun.com/doc/trace.html"><strong>trace</strong></a></td><td align="center"> 方法内部调用路径，并输出方法路径上的每个节点上耗时 </td></tr><tr><td align="center"><a href="https://arthas.aliyun.com/doc/watch.html"><strong>watch</strong></a></td><td align="center"> 方法执行数据观测 </td></tr><tr><td align="center"><a href="https://arthas.aliyun.com/doc/stack.html"><strong>stack</strong></a></td><td align="center"> 输出当前方法被调用的调用路径 </td></tr><tr><td align="center"><a href="https://arthas.aliyun.com/doc/tt.html"><strong>tt</strong></a></td><td align="center"> 方法执行数据的时空隧道，记录下指定方法每次调用的入参和返回信息，并能对这些不同的时间下调用进行观测 </td></tr><tr><td align="center"><a href="https://arthas.aliyun.com/doc/monitor.html"><strong>monitor</strong></a></td><td align="center"> 方法执行监控 </td></tr><tr><td align="center"><a href="https://arthas.aliyun.com/doc/jvm.html"><strong>jvm</strong></a></td><td align="center"> 查看当前 JVM 信息 </td></tr><tr><td align="center"><a href="https://arthas.aliyun.com/doc/vmoption.html"><strong>vmoption</strong></a></td><td align="center"> 查看，更新 JVM 诊断相关的参数 </td></tr><tr><td align="center"><a href="https://arthas.aliyun.com/doc/sc.html"><strong>sc</strong></a></td><td align="center"> 查看 JVM 已加载的类信息 </td></tr><tr><td align="center"><a href="https://arthas.aliyun.com/doc/sm.html"><strong>sm</strong></a></td><td align="center"> 查看已加载类的方法信息 </td></tr><tr><td align="center"><a href="https://arthas.aliyun.com/doc/jad.html"><strong>jad</strong></a></td><td align="center"> 反编译指定已加载类的源码 </td></tr><tr><td align="center"><a href="https://arthas.aliyun.com/doc/classloader.html"><strong>classloader</strong></a></td><td align="center"> 查看 classloader 的继承树，urls，类加载信息 </td></tr><tr><td align="center"><a href="https://arthas.aliyun.com/doc/heapdump.html"><strong>heapdump</strong></a></td><td align="center"> 类似 jmap 命令的 heap dump 功能</td></tr></tbody></table><h1 id="三、Arthas-常用操作"><a href="#三、Arthas-常用操作" class="headerlink" title="三、Arthas 常用操作"></a>三、Arthas 常用操作</h1><h2 id="3-1、dashboard实时数据面板"><a href="#3-1、dashboard实时数据面板" class="headerlink" title="3.1、dashboard实时数据面板"></a>3.1、dashboard 实时数据面板</h2><blockquote><p>使用 dashboard 命令进行全局监控</p></blockquote><figure class="highlight plaintext"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">[arthas@38]$ dashboard</span><br></pre></td></tr></tbody></table></figure><p>使用 dashboard 命令可以概览程序的 线程、内存、GC、运行环境信息。<br><img src="https://oss.syshlang.com/blog/image/b93f3809183e4becb17b49a798040c61.png" alt="实时数据面板"></p><h2 id="3-2、thread线程详情查看"><a href="#3-2、thread线程详情查看" class="headerlink" title="3.2、thread线程详情查看"></a>3.2、thread 线程详情查看</h2><blockquote><p>使用 thread 命令查看当前线程信息，查看线程的堆栈</p></blockquote><div class="note info"><p>参数说明:</p><ul><li>id   根据指定的线程 id 查看线程详细信息</li><li> [n:] 指定最忙的前 N 个线程并打印堆</li><li> [b]  找出当前阻塞其他线程的线程</li><li> [i]  指定 cpu 占比统计的采样间隔，单位为毫秒</li><li> [–all] 显示所有匹配的线程</li></ul></div><p><img src="https://oss.syshlang.com/blog/image/65c2afcfdea849b6b4b404e023ccda10.png" alt="当没有参数时，显示第一页线程信息"><br><img src="https://oss.syshlang.com/blog/image/f4033bc853944d159fa60ff611b8e1b2.png" alt="thread id， 显示指定线程的运行堆栈"><br><img src="https://oss.syshlang.com/blog/image/28698db4494e4fce95533be3e92a82a6.png" alt="指定最忙的前3个线程并打印堆"><br><img src="https://oss.syshlang.com/blog/image/3cfbbc81830d4086bb6ebd33dd282fb6.png" alt="thread -b, 找出当前阻塞其他线程的线程"></p><h2 id="3-3、trace方法内部调用情况跟踪"><a href="#3-3、trace方法内部调用情况跟踪" class="headerlink" title="3.3、trace方法内部调用情况跟踪"></a>3.3、trace 方法内部调用情况跟踪</h2><blockquote><p>使用 trace 命令跟踪方法内部调用路径，并输出方法路径上的每个节点上耗时，能方便的帮助定位性能问题缺陷</p></blockquote><div class="note info"><p>参数说明:</p><ul><li>class-pattern 类名表达式匹配</li><li> method-pattern方法名表达式匹配</li><li> condition-express条件表达式</li><li> [E]开启正则表达式匹配，默认为通配符匹配</li><li> [n:]命令执行次数</li><li>#cost方法执行耗时</li></ul></div><p><img src="https://oss.syshlang.com/blog/image/828f7a4c93ba423cad0b6aeb9be4b02a.png" alt="trace函数"><br><img src="https://oss.syshlang.com/blog/image/27ee2b99c6374726a21c636de763e6e8.png" alt="trace次数限制"><br><img src="https://oss.syshlang.com/blog/image/47ca62ab73644eff8ab7e5dfd7c7e194.png" alt="据调用耗时过滤"></p><h1 id="四、Arthas-插件"><a href="#四、Arthas-插件" class="headerlink" title="四、Arthas 插件"></a>四、Arthas 插件</h1><blockquote><p>如果使用的是 idea 开发，在插件管理中心，搜索安装 arthas idea，即可使用</p></blockquote><p>在项目中找到对应的代码，然后选择 Tools &gt; Arthas Command，即可看到相关的操作选项<br><img src="https://oss.syshlang.com/blog/image/6765d441d7754b01b19c3932c8c0e2fe.png" alt="arthas idea"><br>选择对应的选项，即可生成对应的命令行<br><img src="https://oss.syshlang.com/blog/image/f6476f8951ab49268ba81dc33d5436a6.png" alt="arthas idea生成命令"></p><p><font color="black" size="5" face="华文行楷">附：</font><br><font color="black" size="5" face="华文行楷">Arthas 开源地址：</font><span class="exturl" data-url="aHR0cHM6Ly9naXRodWIuY29tL2FsaWJhYmEvYXJ0aGFz">https://github.com/alibaba/arthas<i class="fa fa-external-link-alt"></i></span><br><font color="black" size="5" face="华文行楷">Arthas 官方地址：</font><span class="exturl" data-url="aHR0cHM6Ly9hcnRoYXMuYWxpeXVuLmNvbS96aC1jbi8=">https://arthas.aliyun.com/zh-cn/<i class="fa fa-external-link-alt"></i></span></p><link rel="stylesheet" href="https://fastly.jsdelivr.net/npm/markmap-toolbar@0.18.10/dist/style.css"><script src="https://fastly.jsdelivr.net/npm/d3@7"></script><script src="https://fastly.jsdelivr.net/npm/markmap-view@0.18.10"></script><script src="https://fastly.jsdelivr.net/npm/markmap-toolbar@0.18.10"></script><link rel="stylesheet" href="/css/markmap.css"><script src="/js/markmap.js"></script>]]></content>
    
    
    <summary type="html">&lt;p&gt;&lt;img src=&quot;https://oss.syshlang.com/blog/image/87b4e519b443444bbd40126452cb860e.png&quot; alt=&quot;Arthas 是Alibaba开源的Java诊断工具&quot;&gt;&lt;/p&gt;</summary>
    
    
    
    <category term="CODING" scheme="https://syshlang.com/categories/CODING/"/>
    
    <category term="tools" scheme="https://syshlang.com/categories/CODING/tools/"/>
    
    <category term="后端" scheme="https://syshlang.com/categories/CODING/%E5%90%8E%E7%AB%AF/"/>
    
    <category term="java" scheme="https://syshlang.com/categories/CODING/%E5%90%8E%E7%AB%AF/java/"/>
    
    <category term="arthas" scheme="https://syshlang.com/categories/CODING/tools/arthas/"/>
    
    
    <category term="java" scheme="https://syshlang.com/tags/java/"/>
    
    <category term="arthas" scheme="https://syshlang.com/tags/arthas/"/>
    
  </entry>
  
  <entry>
    <title>CentOS 挂载硬盘</title>
    <link href="https://syshlang.com/posts/d56fa8f7/"/>
    <id>https://syshlang.com/posts/d56fa8f7/</id>
    <published>2021-03-30T01:32:29.000Z</published>
    <updated>2025-12-01T14:31:49.745Z</updated>
    
    <content type="html"><![CDATA[<link rel="stylesheet" class="aplayer-secondary-style-marker" href="/assets/css/APlayer.min.css"><script src="/assets/js/APlayer.min.js" class="aplayer-secondary-script-marker"></script><script class="meting-secondary-script-marker" src="/assets/js/Meting.min.js"></script><h1 id="挂载前准备工作"><a href="#挂载前准备工作" class="headerlink" title="挂载前准备工作"></a>挂载前准备工作</h1><ol><li>显示目前在 Linux 系统上的文件系统磁盘使用情况统计 </li></ol><figure class="highlight shell"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">df -lh</span><br></pre></td></tr></tbody></table></figure><span id="more"></span><ol start="2"><li>查看当前未挂载的硬盘 </li></ol><figure class="highlight shell"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">fdisk -l</span><br></pre></td></tr></tbody></table></figure><p><img src="https://oss.syshlang.com/blog/image/cf169e0d8044408cbfc25eba03ceea98.png"><br>可以看到 /dev/vdb 就是需要挂载的磁盘</p><ol start="3"><li>创建硬盘分区 (视具体情况可省略)</li></ol><figure class="highlight shell"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">fdisk /dev/vdb</span><br></pre></td></tr></tbody></table></figure><blockquote><p>根据提示，依次输入”n”，”p” “1”，两次回车，”wq”，分区就开始了，很快就会完成。</p></blockquote><ol start="4"><li>格式话磁盘 </li></ol><figure class="highlight shell"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">mkfs.xfs /dev/vdb</span><br></pre></td></tr></tbody></table></figure><blockquote><p>这里文件系统类型可以用 xfs、ext3、ext4 等</p></blockquote><ol start="5"><li>创建挂载目录 </li></ol><figure class="highlight shell"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">mkdir /data</span><br></pre></td></tr></tbody></table></figure><h1 id="临时挂载"><a href="#临时挂载" class="headerlink" title="临时挂载"></a>临时挂载</h1><blockquote><p>临时挂载磁盘，系统重启失效</p></blockquote><figure class="highlight shell"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">mount /dev/vdb /data</span><br></pre></td></tr></tbody></table></figure><table><thead><tr><th>命令</th><th>用途</th><th>使用</th></tr></thead><tbody><tr><td> mount</td><td> 查看挂载信息</td><td><code>mount /dev/vdb /data</code></td></tr><tr><td>umount [设备]</td><td> 卸载设备</td><td><code>umount /data</code></td></tr><tr><td>mount -o [挂载参数] device [挂载点]</td><td> 挂载点</td><td><code>mount -o ro /dev/vdb /data</code></td></tr><tr><td>mount -o remount,[ro/rw] [挂载点]</td><td> 转换挂载参数由读写变为只读 / 读写</td><td><code>mount -o remount,ro /data</code> <br> <code>mount -o remount,rw /data</code></td></tr><tr><td>fuser -kvm [设备 / 挂载点]</td><td> 如果在卸载时当出现设备正忙，可使用 fuser <br> -k 结束进程 <br> -v 显示详细信息<br> -m 显示进程</td><td><code>fuser -kvm /data</code></td></tr></tbody></table><h1 id="永久挂载"><a href="#永久挂载" class="headerlink" title="永久挂载"></a>永久挂载</h1><blockquote><p>设置开机启动自动挂载，永久生效</p></blockquote><blockquote><p>方式一：通过盘符挂载</p></blockquote><p>修改 /etc/fstab 文件</p><figure class="highlight shell"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">vim /etc/fstab</span><br></pre></td></tr></tbody></table></figure><p>增加如下配置</p><figure class="highlight plaintext"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">#设备   挂载点   文件系统类型 挂载参数   是否备份 是否检测</span><br><span class="line">/dev/vdb /data ext4 defaults 0 0</span><br></pre></td></tr></tbody></table></figure><blockquote><p>方式二：通过 UUID 挂载</p></blockquote><p>获取 UUID</p><figure class="highlight shell"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">blkid</span><br></pre></td></tr></tbody></table></figure><p><img src="https://oss.syshlang.com/blog/image/ab180d780d714707827763cc0f6de923.png"><br>或者</p><figure class="highlight shell"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">ls /dev/disk/by-uuid -la</span><br></pre></td></tr></tbody></table></figure><p><img src="https://oss.syshlang.com/blog/image/945dee6c91b64a17961f88cb20167ff7.png"><br>在 /etc/fstab 增加如下配置</p><figure class="highlight plaintext"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">#设备   挂载点   文件系统类型 挂载参数   是否备份 是否检测</span><br><span class="line">UUID=1f4f7f21-feb9-4915-857d-1290c74f19ae /data xfs defaults 0 0</span><br></pre></td></tr></tbody></table></figure><link rel="stylesheet" href="https://fastly.jsdelivr.net/npm/markmap-toolbar@0.18.10/dist/style.css"><script src="https://fastly.jsdelivr.net/npm/d3@7"></script><script src="https://fastly.jsdelivr.net/npm/markmap-view@0.18.10"></script><script src="https://fastly.jsdelivr.net/npm/markmap-toolbar@0.18.10"></script><link rel="stylesheet" href="/css/markmap.css"><script src="/js/markmap.js"></script>]]></content>
    
    
    <summary type="html">&lt;h1 id=&quot;挂载前准备工作&quot;&gt;&lt;a href=&quot;#挂载前准备工作&quot; class=&quot;headerlink&quot; title=&quot;挂载前准备工作&quot;&gt;&lt;/a&gt;挂载前准备工作&lt;/h1&gt;&lt;ol&gt;
&lt;li&gt;显示目前在 Linux 系统上的文件系统磁盘使用情况统计 &lt;/li&gt;
&lt;/ol&gt;
&lt;figure class=&quot;highlight shell&quot;&gt;&lt;table&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td class=&quot;gutter&quot;&gt;&lt;pre&gt;&lt;span class=&quot;line&quot;&gt;1&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class=&quot;code&quot;&gt;&lt;pre&gt;&lt;span class=&quot;line&quot;&gt;df -lh&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;/figure&gt;</summary>
    
    
    
    <category term="CODING" scheme="https://syshlang.com/categories/CODING/"/>
    
    <category term="操作系统" scheme="https://syshlang.com/categories/CODING/%E6%93%8D%E4%BD%9C%E7%B3%BB%E7%BB%9F/"/>
    
    <category term="linux" scheme="https://syshlang.com/categories/CODING/%E6%93%8D%E4%BD%9C%E7%B3%BB%E7%BB%9F/linux/"/>
    
    
    <category term="linux" scheme="https://syshlang.com/tags/linux/"/>
    
    <category term="mount" scheme="https://syshlang.com/tags/mount/"/>
    
  </entry>
  
  <entry>
    <title>Multipass 虚拟机的安装及使用</title>
    <link href="https://syshlang.com/posts/765d50de/"/>
    <id>https://syshlang.com/posts/765d50de/</id>
    <published>2021-03-20T10:40:06.000Z</published>
    <updated>2025-05-20T13:41:41.858Z</updated>
    
    <content type="html"><![CDATA[<link rel="stylesheet" class="aplayer-secondary-style-marker" href="/assets/css/APlayer.min.css"><script src="/assets/js/APlayer.min.js" class="aplayer-secondary-script-marker"></script><script class="meting-secondary-script-marker" src="/assets/js/Meting.min.js"></script><p><img src="https://assets.ubuntu.com/v1/0698ab2d-muiltipass-promo-header.png"></p><span id="more"></span><h2 id="下载安装"><a href="#下载安装" class="headerlink" title="下载安装"></a>下载安装</h2><p>打开 <a href="https://multipass.run/"><strong>Multipass</strong> 官网</a>下载安装</p><h2 id="查看版本"><a href="#查看版本" class="headerlink" title="查看版本"></a>查看版本</h2><figure class="highlight shell"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">multipass --version</span><br></pre></td></tr></tbody></table></figure><h2 id="查看可供下载的-Ubuntu-镜像"><a href="#查看可供下载的-Ubuntu-镜像" class="headerlink" title="查看可供下载的 Ubuntu 镜像"></a>查看可供下载的 <strong>Ubuntu</strong> 镜像</h2><figure class="highlight shell"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">multipass find</span><br></pre></td></tr></tbody></table></figure><h2 id="创建-Ubuntu-镜像"><a href="#创建-Ubuntu-镜像" class="headerlink" title="创建 Ubuntu 镜像"></a>创建 <strong>Ubuntu</strong> 镜像</h2><figure class="highlight shell"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">multipass launch -n vm01 -c 1 -m 1G -d 10G</span><br></pre></td></tr></tbody></table></figure><blockquote><p>-n, –name: 名称<br>-c, –cpus: cpu 核心数，默认: 1<br>-m, –mem: 内存大小，默认: 1G<br>-d, –disk: 硬盘大小，默认: 5G</p></blockquote><h2 id="查看虚拟机列表"><a href="#查看虚拟机列表" class="headerlink" title="查看虚拟机列表"></a>查看虚拟机列表</h2><figure class="highlight shell"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">multipass list</span><br></pre></td></tr></tbody></table></figure><h2 id="通过-multipass-exec-命令在实例内执行给定的命令"><a href="#通过-multipass-exec-命令在实例内执行给定的命令" class="headerlink" title="通过 multipass exec 命令在实例内执行给定的命令"></a>通过 <strong>multipass exec</strong> 命令在实例内执行给定的命令</h2><figure class="highlight shell"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">multipass exec vm01 pwd</span><br></pre></td></tr></tbody></table></figure><h2 id="查看当前运行的虚拟机信息"><a href="#查看当前运行的虚拟机信息" class="headerlink" title="查看当前运行的虚拟机信息"></a>查看当前运行的虚拟机信息</h2><figure class="highlight shell"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">multipass info vm01</span><br></pre></td></tr></tbody></table></figure><h2 id="进入到虚拟机内部"><a href="#进入到虚拟机内部" class="headerlink" title="进入到虚拟机内部"></a>进入到虚拟机内部</h2><figure class="highlight shell"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">multipass shell vm01</span><br></pre></td></tr></tbody></table></figure><link rel="stylesheet" href="https://fastly.jsdelivr.net/npm/markmap-toolbar@0.18.10/dist/style.css"><script src="https://fastly.jsdelivr.net/npm/d3@7"></script><script src="https://fastly.jsdelivr.net/npm/markmap-view@0.18.10"></script><script src="https://fastly.jsdelivr.net/npm/markmap-toolbar@0.18.10"></script><link rel="stylesheet" href="/css/markmap.css"><script src="/js/markmap.js"></script>]]></content>
    
    
    <summary type="html">&lt;p&gt;&lt;img src=&quot;https://assets.ubuntu.com/v1/0698ab2d-muiltipass-promo-header.png&quot;&gt;&lt;/p&gt;</summary>
    
    
    
    <category term="CODING" scheme="https://syshlang.com/categories/CODING/"/>
    
    <category term="操作系统" scheme="https://syshlang.com/categories/CODING/%E6%93%8D%E4%BD%9C%E7%B3%BB%E7%BB%9F/"/>
    
    <category term="linux" scheme="https://syshlang.com/categories/CODING/%E6%93%8D%E4%BD%9C%E7%B3%BB%E7%BB%9F/linux/"/>
    
    
    <category term="linux" scheme="https://syshlang.com/tags/linux/"/>
    
    <category term="Multipass" scheme="https://syshlang.com/tags/Multipass/"/>
    
  </entry>
  
  <entry>
    <title>设计模式之行为型模式中的责任链模式（Chain of Responsibility Pattern）</title>
    <link href="https://syshlang.com/posts/92b522e8/"/>
    <id>https://syshlang.com/posts/92b522e8/</id>
    <published>2021-01-16T12:10:59.000Z</published>
    <updated>2025-12-01T14:31:49.026Z</updated>
    
    <content type="html"><![CDATA[<link rel="stylesheet" class="aplayer-secondary-style-marker" href="/assets/css/APlayer.min.css"><script src="/assets/js/APlayer.min.js" class="aplayer-secondary-script-marker"></script><script class="meting-secondary-script-marker" src="/assets/js/Meting.min.js"></script><img src="https://refactoringguru.cn/images/patterns/cards/chain-of-responsibility-mini-2x.png" class="" width="600" height="300" title="责任链模式" alt="责任链模式"><span id="more"></span><h1 id="一、基本介绍"><a href="#一、基本介绍" class="headerlink" title="一、基本介绍"></a>一、基本介绍</h1><p><indent><strong>责任链（Chain of Responsibility）模式</strong>，也叫职责链模式，属于行为型模式。<strong>== 该模式为请求创建了一个接收者对象的链，通常链中每个接收者都包含对另一个接收者的引用。==</strong> 当请求发生时，请求沿着这条链发送传递，链中的接收者接收到请求之后，可根据自己的职责，处理请求中相应的业务，处理完成后并将请求传递给链上的下一个接收者，直到这条链走完处理掉所有的业务为止。</indent></p><h1 id="二、责任链模式的实现"><a href="#二、责任链模式的实现" class="headerlink" title="二、责任链模式的实现"></a>二、责任链模式的实现</h1><h2 id="2-1、职责链模式中的主要角色及职责"><a href="#2-1、职责链模式中的主要角色及职责" class="headerlink" title="2.1、职责链模式中的主要角色及职责"></a>2.1、职责链模式中的主要角色及职责</h2><p><indent>职责链模式主要包含以下角色</indent></p><ul><li><strong>抽象处理者（Handler）角色</strong>：该角色一般是一个抽象类，类中声明了所有处理者用于请求处理的通用接口，以及指向下一个处理者的引用。</li><li><strong>具体处理者（Concrete Handler）角色</strong>：该角色是一般是抽象处理者（抽象类）的子类，它实现了抽象处理者的处理方法，包含了实际的请求处理代码，用于处理请求中它自己负责的业务，并可以访问它的后继处理者。如果可以处理当前请求，则处理，否则就将该请求交给后继者去处理，从而形成一个职责链。</li><li><strong>客户端（Client）角色</strong>：创建责任链，并向链上的任意一个处理者对象提交请求，它不关心处理细节和请求的传递过程。</li></ul><h2 id="2-2、责任链模式结构"><a href="#2-2、责任链模式结构" class="headerlink" title="2.2、责任链模式结构"></a>2.2、责任链模式结构</h2><img src="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHhtbG5zOnhsaW5rPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5L3hsaW5rIiBjb250ZW50U3R5bGVUeXBlPSJ0ZXh0L2NzcyIgZGF0YS1kaWFncmFtLXR5cGU9IkNMQVNTIiBoZWlnaHQ9Ijg5N3B4IiBwcmVzZXJ2ZUFzcGVjdFJhdGlvPSJub25lIiBzdHlsZT0id2lkdGg6OTc0cHg7aGVpZ2h0Ojg5N3B4O2JhY2tncm91bmQ6I0ZGRkZGRjsiIHZlcnNpb249IjEuMSIgdmlld0JveD0iMCAwIDk3NCA4OTciIHdpZHRoPSI5NzRweCIgem9vbUFuZFBhbj0ibWFnbmlmeSI+PHRpdGxlPiYjMzYxMzE7JiMyMDIxOTsmIzM4MTQyOyYjMjcxNjk7JiMyNDMzNTsmIzMyNDY3OyYjMjY1MDA7JiMzMTg2NzsmIzIyMjcwOy0mbHQ7YiZndDsmbHQ7Y29sb3I6cm95YWxCbHVlJmd0O3N5c2hsYW5nJmx0Oy9jb2xvciZndDs8L3RpdGxlPjxkZWZzLz48Zz48ZyBjbGFzcz0idGl0bGUiIGRhdGEtc291cmNlLWxpbmU9IjEiPjx0ZXh0IGZpbGw9IiMwMDAwMDAiIGZvbnQtZmFtaWx5PSJzYW5zLXNlcmlmIiBmb250LXNpemU9IjE0IiBmb250LXdlaWdodD0iYm9sZCIgbGVuZ3RoQWRqdXN0PSJzcGFjaW5nIiB0ZXh0TGVuZ3RoPSIxMzEuODEiIHg9IjM3OS4xMzM2IiB5PSIyMi45OTUxIj4mIzM2MTMxOyYjMjAyMTk7JiMzODE0MjsmIzI3MTY5OyYjMjQzMzU7JiMzMjQ2NzsmIzI2NTAwOyYjMzE4Njc7JiMyMjI3MDstPC90ZXh0Pjx0ZXh0IGZpbGw9IiM0MTY5RTEiIGZvbnQtZmFtaWx5PSJzYW5zLXNlcmlmIiBmb250LXNpemU9IjE0IiBmb250LXdlaWdodD0iYm9sZCIgbGVuZ3RoQWRqdXN0PSJzcGFjaW5nIiB0ZXh0TGVuZ3RoPSI2OS45OTMyIiB4PSI1MTAuOTQzNiIgeT0iMjIuOTk1MSI+c3lzaGxhbmc8L3RleHQ+PC9nPjwhLS1jbGFzcyBIYW5kbGVyLS0+PGcgY2xhc3M9ImVudGl0eSIgZGF0YS1lbnRpdHk9IkhhbmRsZXIiIGRhdGEtc291cmNlLWxpbmU9IjMiIGRhdGEtdWlkPSJlbnQwMDAyIiBpZD0iZW50aXR5X0hhbmRsZXIiPjxyZWN0IGZpbGw9IiNGMUYxRjEiIGhlaWdodD0iMTIxLjQ1MzEiIHJ4PSIyLjUiIHJ5PSIyLjUiIHN0eWxlPSJzdHJva2U6IzE4MTgxODtzdHJva2Utd2lkdGg6MC41OyIgd2lkdGg9IjI0My4xNTA0IiB4PSIzNjUuNDkiIHk9IjI4NC40OTY5Ii8+PGVsbGlwc2UgY3g9IjQzNS45MTI5IiBjeT0iMzA0LjYyOTciIGZpbGw9IiNBOURDREYiIHJ4PSIxMSIgcnk9IjExIiBzdHlsZT0ic3Ryb2tlOiMxODE4MTg7c3Ryb2tlLXdpZHRoOjE7Ii8+PHBhdGggZD0iTTQzNi4wMjIyLDI5OS45NzM0IEw0MzQuODY2LDMwNS4wNTE2IEw0MzcuMTk0MSwzMDUuMDUxNiBMNDM2LjAyMjIsMjk5Ljk3MzQgWiBNNDM0LjUzNzksMjk3LjczOTEgTDQzNy41MjIyLDI5Ny43MzkxIEw0NDAuODgxNiwzMTAuMTI5NyBMNDM4LjQyODUsMzEwLjEyOTcgTDQzNy42NjI5LDMwNy4wNjcyIEw0MzQuMzgxNiwzMDcuMDY3MiBMNDMzLjYzMTYsMzEwLjEyOTcgTDQzMS4xOTQxLDMxMC4xMjk3IEw0MzQuNTM3OSwyOTcuNzM5MSBaICIgZmlsbD0iIzAwMDAwMCIvPjx0ZXh0IGZpbGw9IiMwMDAwMDAiIGZvbnQtZmFtaWx5PSJzYW5zLXNlcmlmIiBmb250LXNpemU9IjEyIiBmb250LXN0eWxlPSJpdGFsaWMiIGxlbmd0aEFkanVzdD0ic3BhY2luZyIgdGV4dExlbmd0aD0iOTcuODA0NyIgeD0iNDU0LjQxMjkiIHk9IjMwMC42MzU1Ij4mIzE3MTthYnN0cmFjdCBjbGFzcyYjMTg3OzwvdGV4dD48dGV4dCBmaWxsPSIjMDAwMDAwIiBmb250LWZhbWlseT0ic2Fucy1zZXJpZiIgZm9udC1zaXplPSIxNCIgZm9udC1zdHlsZT0iaXRhbGljIiBsZW5ndGhBZGp1c3Q9InNwYWNpbmciIHRleHRMZW5ndGg9IjU1LjEyNSIgeD0iNDc1Ljc1MjciIHk9IjMxNi40NjA3Ij5IYW5kbGVyPC90ZXh0PjxsaW5lIHN0eWxlPSJzdHJva2U6IzE4MTgxODtzdHJva2Utd2lkdGg6MC41OyIgeDE9IjM2Ni40OSIgeDI9IjYwNy42NDA0IiB5MT0iMzI0Ljc2MjUiIHkyPSIzMjQuNzYyNSIvPjxnIGRhdGEtdmlzaWJpbGl0eS1tb2RpZmllcj0iUFJJVkFURV9GSUVMRCI+PHJlY3QgZmlsbD0ibm9uZSIgaGVpZ2h0PSI2IiBzdHlsZT0ic3Ryb2tlOiNDODI5MzA7c3Ryb2tlLXdpZHRoOjE7IiB3aWR0aD0iNiIgeD0iMzczLjQ5IiB5PSIzMzUuNDEwOSIvPjwvZz48dGV4dCBmaWxsPSIjMDAwMDAwIiBmb250LWZhbWlseT0ic2Fucy1zZXJpZiIgZm9udC1zaXplPSIxNCIgbGVuZ3RoQWRqdXN0PSJzcGFjaW5nIiB0ZXh0TGVuZ3RoPSIxNTAuNjc3NyIgeD0iMzg1LjQ5IiB5PSIzNDEuNzU3NiI+SGFuZGxlciBuZXh0SGFuZGxlcjs8L3RleHQ+PGxpbmUgc3R5bGU9InN0cm9rZTojMTgxODE4O3N0cm9rZS13aWR0aDowLjU7IiB4MT0iMzY2LjQ5IiB4Mj0iNjA3LjY0MDQiIHkxPSIzNDkuMDU5NCIgeTI9IjM0OS4wNTk0Ii8+PGcgZGF0YS12aXNpYmlsaXR5LW1vZGlmaWVyPSJQVUJMSUNfTUVUSE9EIj48ZWxsaXBzZSBjeD0iMzc2LjQ5IiBjeT0iMzYyLjcwNzgiIGZpbGw9IiM4NEJFODQiIHJ4PSIzIiByeT0iMyIgc3R5bGU9InN0cm9rZTojMDM4MDQ4O3N0cm9rZS13aWR0aDoxOyIvPjwvZz48dGV4dCBmaWxsPSIjMDAwMDAwIiBmb250LWZhbWlseT0ic2Fucy1zZXJpZiIgZm9udC1zaXplPSIxNCIgbGVuZ3RoQWRqdXN0PSJzcGFjaW5nIiB0ZXh0TGVuZ3RoPSIxODQuNTk3NyIgeD0iMzg1LjQ5IiB5PSIzNjYuMDU0NSI+SGFuZGxlciBzZXROZXh0SGFuZGxlcigpOzwvdGV4dD48ZyBkYXRhLXZpc2liaWxpdHktbW9kaWZpZXI9IlBVQkxJQ19NRVRIT0QiPjxlbGxpcHNlIGN4PSIzNzYuNDkiIGN5PSIzNzkuMDA0NyIgZmlsbD0iIzg0QkU4NCIgcng9IjMiIHJ5PSIzIiBzdHlsZT0ic3Ryb2tlOiMwMzgwNDg7c3Ryb2tlLXdpZHRoOjE7Ii8+PC9nPjx0ZXh0IGZpbGw9IiMwMDAwMDAiIGZvbnQtZmFtaWx5PSJzYW5zLXNlcmlmIiBmb250LXNpemU9IjE0IiBsZW5ndGhBZGp1c3Q9InNwYWNpbmciIHRleHRMZW5ndGg9IjE4Ni4xOTA0IiB4PSIzODUuNDkiIHk9IjM4Mi4zNTE0Ij5IYW5kbGVyIGdldE5leHRIYW5kbGVyKCk7PC90ZXh0PjxnIGRhdGEtdmlzaWJpbGl0eS1tb2RpZmllcj0iUFJPVEVDVEVEX01FVEhPRCI+PHBvbHlnb24gZmlsbD0iI0ZGRkY0NCIgcG9pbnRzPSIzNzYuNDksMzkwLjMwMTYsMzgwLjQ5LDM5NC4zMDE2LDM3Ni40OSwzOTguMzAxNiwzNzIuNDksMzk0LjMwMTYiIHN0eWxlPSJzdHJva2U6I0IzOEQyMjtzdHJva2Utd2lkdGg6MTsiLz48L2c+PHRleHQgZmlsbD0iIzAwMDAwMCIgZm9udC1mYW1pbHk9InNhbnMtc2VyaWYiIGZvbnQtc2l6ZT0iMTQiIGxlbmd0aEFkanVzdD0ic3BhY2luZyIgdGV4dExlbmd0aD0iMjE3LjE1MDQiIHg9IjM4NS40OSIgeT0iMzk4LjY0ODIiPmFic3RyYWN0IHZvaWQgaGFuZGxlUmVxdWVzdCgpOzwvdGV4dD48L2c+PGcgY2xhc3M9ImVudGl0eSIgZGF0YS1lbnRpdHk9IkdNTjMiIGRhdGEtc291cmNlLWxpbmU9IjQiIGRhdGEtdWlkPSJlbnQwMDA0IiBpZD0iZW50aXR5X0dNTjMiPjxwYXRoIGQ9Ik02NjIuMDcsMzMyLjY1NjkgTDY2Mi4wNywzNDEuMjI2OSBMNjA4Ljg0LDM0NS4yMjY5IEw2NjIuMDcsMzQ5LjIyNjkgTDY2Mi4wNywzNTcuNzg5NyBBMCwwIDAgMCAwIDY2Mi4wNywzNTcuNzg5NyBMNzQ4LjA3MDEsMzU3Ljc4OTcgQTAsMCAwIDAgMCA3NDguMDcwMSwzNTcuNzg5NyBMNzQ4LjA3MDEsMzQyLjY1NjkgTDczOC4wNzAxLDMzMi42NTY5IEw2NjIuMDcsMzMyLjY1NjkgQTAsMCAwIDAgMCA2NjIuMDcsMzMyLjY1NjkiIGZpbGw9IiNGRUZGREQiIHN0eWxlPSJzdHJva2U6IzE4MTgxODtzdHJva2Utd2lkdGg6MC41OyIvPjxwYXRoIGQ9Ik03MzguMDcwMSwzMzIuNjU2OSBMNzM4LjA3MDEsMzQyLjY1NjkgTDc0OC4wNzAxLDM0Mi42NTY5IEw3MzguMDcwMSwzMzIuNjU2OSIgZmlsbD0iI0ZFRkZERCIgc3R5bGU9InN0cm9rZTojMTgxODE4O3N0cm9rZS13aWR0aDowLjU7Ii8+PHRleHQgZmlsbD0iIzAwMDAwMCIgZm9udC1mYW1pbHk9InNhbnMtc2VyaWYiIGZvbnQtc2l6ZT0iMTMiIGxlbmd0aEFkanVzdD0ic3BhY2luZyIgdGV4dExlbmd0aD0iNjUuMDAwMSIgeD0iNjY4LjA3IiB5PSIzNDkuNzIzOCI+JiMyNTI3NzsmIzM1OTM3OyYjMjI3ODg7JiMyOTcwMjsmIzMyNzczOzwvdGV4dD48L2c+PCEtLWNsYXNzIENvbmNyZXRlSGFuZGxlckEtLT48ZyBjbGFzcz0iZW50aXR5IiBkYXRhLWVudGl0eT0iQ29uY3JldGVIYW5kbGVyQSIgZGF0YS1zb3VyY2UtbGluZT0iNiIgZGF0YS11aWQ9ImVudDAwMDYiIGlkPSJlbnRpdHlfQ29uY3JldGVIYW5kbGVyQSI+PHJlY3QgZmlsbD0iI0YxRjFGMSIgaGVpZ2h0PSI3Mi41NjI1IiByeD0iMi41IiByeT0iMi41IiBzdHlsZT0ic3Ryb2tlOiMxODE4MTg7c3Ryb2tlLXdpZHRoOjAuNTsiIHdpZHRoPSIxODAuOTI5NyIgeD0iMTA2LjYxIiB5PSI0ODIuOTQ2OSIvPjxlbGxpcHNlIGN4PSIxMzAuOTkwMSIgY3k9IjUwMy4wNzk3IiBmaWxsPSIjQUREMUIyIiByeD0iMTEiIHJ5PSIxMSIgc3R5bGU9InN0cm9rZTojMTgxODE4O3N0cm9rZS13aWR0aDoxOyIvPjxwYXRoIGQ9Ik0xMzMuOTU4OSw1MDguNzIwMyBRMTMzLjM4MDgsNTA5LjAxNzIgMTMyLjc0MDEsNTA5LjE1NzggUTEzMi4wOTk1LDUwOS4zMTQxIDEzMS4zOTY0LDUwOS4zMTQxIFExMjguODk2NCw1MDkuMzE0MSAxMjcuNTY4Myw1MDcuNjczNCBRMTI2LjI1NTgsNTA2LjAxNzIgMTI2LjI1NTgsNTAyLjg5MjIgUTEyNi4yNTU4LDQ5OS43NjcyIDEyNy41NjgzLDQ5OC4xMTA5IFExMjguODk2NCw0OTYuNDU0NyAxMzEuMzk2NCw0OTYuNDU0NyBRMTMyLjA5OTUsNDk2LjQ1NDcgMTMyLjc0MDEsNDk2LjYxMDkgUTEzMy4zOTY0LDQ5Ni43NjcyIDEzMy45NTg5LDQ5Ny4wNjQxIEwxMzMuOTU4OSw0OTkuNzgyOCBRMTMzLjMzMzksNDk5LjIwNDcgMTMyLjc0MDEsNDk4LjkzOTEgUTEzMi4xNDY0LDQ5OC42NTc4IDEzMS41MjE0LDQ5OC42NTc4IFExMzAuMTc3Niw0OTguNjU3OCAxMjkuNDkwMSw0OTkuNzM1OSBRMTI4LjgwMjYsNTAwLjc5ODQgMTI4LjgwMjYsNTAyLjg5MjIgUTEyOC44MDI2LDUwNC45ODU5IDEyOS40OTAxLDUwNi4wNjQxIFExMzAuMTc3Niw1MDcuMTI2NiAxMzEuNTIxNCw1MDcuMTI2NiBRMTMyLjE0NjQsNTA3LjEyNjYgMTMyLjc0MDEsNTA2Ljg2MDkgUTEzMy4zMzM5LDUwNi41Nzk3IDEzMy45NTg5LDUwNi4wMDE2IEwxMzMuOTU4OSw1MDguNzIwMyBaICIgZmlsbD0iIzAwMDAwMCIvPjx0ZXh0IGZpbGw9IiMwMDAwMDAiIGZvbnQtZmFtaWx5PSJzYW5zLXNlcmlmIiBmb250LXNpemU9IjEyIiBmb250LXN0eWxlPSJpdGFsaWMiIGxlbmd0aEFkanVzdD0ic3BhY2luZyIgdGV4dExlbmd0aD0iNDQuNDcyNyIgeD0iMTg4Ljg4MDgiIHk9IjQ5OS4wODU1Ij4mIzE3MTtjbGFzcyYjMTg3OzwvdGV4dD48dGV4dCBmaWxsPSIjMDAwMDAwIiBmb250LWZhbWlseT0ic2Fucy1zZXJpZiIgZm9udC1zaXplPSIxNCIgbGVuZ3RoQWRqdXN0PSJzcGFjaW5nIiB0ZXh0TGVuZ3RoPSIxMjguMDg1IiB4PSIxNDcuMDc0NiIgeT0iNTE0LjkxMDciPkNvbmNyZXRlSGFuZGxlckE8L3RleHQ+PGxpbmUgc3R5bGU9InN0cm9rZTojMTgxODE4O3N0cm9rZS13aWR0aDowLjU7IiB4MT0iMTA3LjYxIiB4Mj0iMjg2LjUzOTciIHkxPSI1MjMuMjEyNSIgeTI9IjUyMy4yMTI1Ii8+PGxpbmUgc3R5bGU9InN0cm9rZTojMTgxODE4O3N0cm9rZS13aWR0aDowLjU7IiB4MT0iMTA3LjYxIiB4Mj0iMjg2LjUzOTciIHkxPSI1MzEuMjEyNSIgeTI9IjUzMS4yMTI1Ii8+PGcgZGF0YS12aXNpYmlsaXR5LW1vZGlmaWVyPSJQVUJMSUNfTUVUSE9EIj48ZWxsaXBzZSBjeD0iMTE3LjYxIiBjeT0iNTQ0Ljg2MDkiIGZpbGw9IiM4NEJFODQiIHJ4PSIzIiByeT0iMyIgc3R5bGU9InN0cm9rZTojMDM4MDQ4O3N0cm9rZS13aWR0aDoxOyIvPjwvZz48dGV4dCBmaWxsPSIjMDAwMDAwIiBmb250LWZhbWlseT0ic2Fucy1zZXJpZiIgZm9udC1zaXplPSIxNCIgbGVuZ3RoQWRqdXN0PSJzcGFjaW5nIiB0ZXh0TGVuZ3RoPSIxNTQuOTI5NyIgeD0iMTI2LjYxIiB5PSI1NDguMjA3NiI+aGFuZGxlUmVxdWVzdCgpOiB2b2lkPC90ZXh0PjwvZz48ZyBjbGFzcz0iZW50aXR5IiBkYXRhLWVudGl0eT0iR01ONyIgZGF0YS1zb3VyY2UtbGluZT0iNyIgZGF0YS11aWQ9ImVudDAwMDgiIGlkPSJlbnRpdHlfR01ONyI+PHBhdGggZD0iTTMyMi42Miw1MDYuNjY2OSBMMzIyLjYyLDUxNS4yMjY5IEwyODcuOTYsNTE5LjIyNjkgTDMyMi42Miw1MjMuMjI2OSBMMzIyLjYyLDUzMS43OTk3IEEwLDAgMCAwIDAgMzIyLjYyLDUzMS43OTk3IEw0MTcuNTEzMSw1MzEuNzk5NyBBMCwwIDAgMCAwIDQxNy41MTMxLDUzMS43OTk3IEw0MTcuNTEzMSw1MTYuNjY2OSBMNDA3LjUxMzEsNTA2LjY2NjkgTDMyMi42Miw1MDYuNjY2OSBBMCwwIDAgMCAwIDMyMi42Miw1MDYuNjY2OSIgZmlsbD0iI0ZFRkZERCIgc3R5bGU9InN0cm9rZTojMTgxODE4O3N0cm9rZS13aWR0aDowLjU7Ii8+PHBhdGggZD0iTTQwNy41MTMxLDUwNi42NjY5IEw0MDcuNTEzMSw1MTYuNjY2OSBMNDE3LjUxMzEsNTE2LjY2NjkgTDQwNy41MTMxLDUwNi42NjY5IiBmaWxsPSIjRkVGRkREIiBzdHlsZT0ic3Ryb2tlOiMxODE4MTg7c3Ryb2tlLXdpZHRoOjAuNTsiLz48dGV4dCBmaWxsPSIjMDAwMDAwIiBmb250LWZhbWlseT0ic2Fucy1zZXJpZiIgZm9udC1zaXplPSIxMyIgbGVuZ3RoQWRqdXN0PSJzcGFjaW5nIiB0ZXh0TGVuZ3RoPSI3My44OTMxIiB4PSIzMjguNjIiIHk9IjUyMy43MzM4Ij4mIzIwODU1OyYjMjAzMDc7JiMyMjc4ODsmIzI5NzAyOyYjMzI3NzM7QTwvdGV4dD48L2c+PCEtLWNsYXNzIENvbmNyZXRlSGFuZGxlckItLT48ZyBjbGFzcz0iZW50aXR5IiBkYXRhLWVudGl0eT0iQ29uY3JldGVIYW5kbGVyQiIgZGF0YS1zb3VyY2UtbGluZT0iOCIgZGF0YS11aWQ9ImVudDAwMTAiIGlkPSJlbnRpdHlfQ29uY3JldGVIYW5kbGVyQiI+PHJlY3QgZmlsbD0iI0YxRjFGMSIgaGVpZ2h0PSI3Mi41NjI1IiByeD0iMi41IiByeT0iMi41IiBzdHlsZT0ic3Ryb2tlOiMxODE4MTg7c3Ryb2tlLXdpZHRoOjAuNTsiIHdpZHRoPSIxODAuOTI5NyIgeD0iMTM3LjYxIiB5PSI2NTAuNTA2OSIvPjxlbGxpcHNlIGN4PSIxNjEuOTc3OCIgY3k9IjY3MC42Mzk3IiBmaWxsPSIjQUREMUIyIiByeD0iMTEiIHJ5PSIxMSIgc3R5bGU9InN0cm9rZTojMTgxODE4O3N0cm9rZS13aWR0aDoxOyIvPjxwYXRoIGQ9Ik0xNjQuOTQ2Niw2NzYuMjgwMyBRMTY0LjM2ODQsNjc2LjU3NzIgMTYzLjcyNzgsNjc2LjcxNzggUTE2My4wODcyLDY3Ni44NzQxIDE2Mi4zODQxLDY3Ni44NzQxIFExNTkuODg0MSw2NzYuODc0MSAxNTguNTU1OSw2NzUuMjMzNCBRMTU3LjI0MzQsNjczLjU3NzIgMTU3LjI0MzQsNjcwLjQ1MjIgUTE1Ny4yNDM0LDY2Ny4zMjcyIDE1OC41NTU5LDY2NS42NzA5IFExNTkuODg0MSw2NjQuMDE0NyAxNjIuMzg0MSw2NjQuMDE0NyBRMTYzLjA4NzIsNjY0LjAxNDcgMTYzLjcyNzgsNjY0LjE3MDkgUTE2NC4zODQxLDY2NC4zMjcyIDE2NC45NDY2LDY2NC42MjQxIEwxNjQuOTQ2Niw2NjcuMzQyOCBRMTY0LjMyMTYsNjY2Ljc2NDcgMTYzLjcyNzgsNjY2LjQ5OTEgUTE2My4xMzQxLDY2Ni4yMTc4IDE2Mi41MDkxLDY2Ni4yMTc4IFExNjEuMTY1Myw2NjYuMjE3OCAxNjAuNDc3OCw2NjcuMjk1OSBRMTU5Ljc5MDMsNjY4LjM1ODQgMTU5Ljc5MDMsNjcwLjQ1MjIgUTE1OS43OTAzLDY3Mi41NDU5IDE2MC40Nzc4LDY3My42MjQxIFExNjEuMTY1Myw2NzQuNjg2NiAxNjIuNTA5MSw2NzQuNjg2NiBRMTYzLjEzNDEsNjc0LjY4NjYgMTYzLjcyNzgsNjc0LjQyMDkgUTE2NC4zMjE2LDY3NC4xMzk3IDE2NC45NDY2LDY3My41NjE2IEwxNjQuOTQ2Niw2NzYuMjgwMyBaICIgZmlsbD0iIzAwMDAwMCIvPjx0ZXh0IGZpbGw9IiMwMDAwMDAiIGZvbnQtZmFtaWx5PSJzYW5zLXNlcmlmIiBmb250LXNpemU9IjEyIiBmb250LXN0eWxlPSJpdGFsaWMiIGxlbmd0aEFkanVzdD0ic3BhY2luZyIgdGV4dExlbmd0aD0iNDQuNDcyNyIgeD0iMjE5Ljg3OTQiIHk9IjY2Ni42NDU1Ij4mIzE3MTtjbGFzcyYjMTg3OzwvdGV4dD48dGV4dCBmaWxsPSIjMDAwMDAwIiBmb250LWZhbWlseT0ic2Fucy1zZXJpZiIgZm9udC1zaXplPSIxNCIgbGVuZ3RoQWRqdXN0PSJzcGFjaW5nIiB0ZXh0TGVuZ3RoPSIxMjguMTEyMyIgeD0iMTc4LjA1OTYiIHk9IjY4Mi40NzA3Ij5Db25jcmV0ZUhhbmRsZXJCPC90ZXh0PjxsaW5lIHN0eWxlPSJzdHJva2U6IzE4MTgxODtzdHJva2Utd2lkdGg6MC41OyIgeDE9IjEzOC42MSIgeDI9IjMxNy41Mzk3IiB5MT0iNjkwLjc3MjUiIHkyPSI2OTAuNzcyNSIvPjxsaW5lIHN0eWxlPSJzdHJva2U6IzE4MTgxODtzdHJva2Utd2lkdGg6MC41OyIgeDE9IjEzOC42MSIgeDI9IjMxNy41Mzk3IiB5MT0iNjk4Ljc3MjUiIHkyPSI2OTguNzcyNSIvPjxnIGRhdGEtdmlzaWJpbGl0eS1tb2RpZmllcj0iUFVCTElDX01FVEhPRCI+PGVsbGlwc2UgY3g9IjE0OC42MSIgY3k9IjcxMi40MjA5IiBmaWxsPSIjODRCRTg0IiByeD0iMyIgcnk9IjMiIHN0eWxlPSJzdHJva2U6IzAzODA0ODtzdHJva2Utd2lkdGg6MTsiLz48L2c+PHRleHQgZmlsbD0iIzAwMDAwMCIgZm9udC1mYW1pbHk9InNhbnMtc2VyaWYiIGZvbnQtc2l6ZT0iMTQiIGxlbmd0aEFkanVzdD0ic3BhY2luZyIgdGV4dExlbmd0aD0iMTU0LjkyOTciIHg9IjE1Ny42MSIgeT0iNzE1Ljc2NzYiPmhhbmRsZVJlcXVlc3QoKTogdm9pZDwvdGV4dD48L2c+PGcgY2xhc3M9ImVudGl0eSIgZGF0YS1lbnRpdHk9IkdNTjExIiBkYXRhLXNvdXJjZS1saW5lPSI5IiBkYXRhLXVpZD0iZW50MDAxMiIgaWQ9ImVudGl0eV9HTU4xMSI+PHBhdGggZD0iTTcuNjEsNjc0LjIyNjkgTDcuNjEsNjk5LjM1OTcgQTAsMCAwIDAgMCA3LjYxLDY5OS4zNTk3IEwxMDIuNTI4NSw2OTkuMzU5NyBBMCwwIDAgMCAwIDEwMi41Mjg1LDY5OS4zNTk3IEwxMDIuNTI4NSw2OTIuMjI2OSBMMTM3LjQ4LDY4Ni43OTY5IEwxMDIuNTI4NSw2ODQuMjI2OSBMMTAyLjUyODUsNjg0LjIyNjkgTDkyLjUyODUsNjc0LjIyNjkgTDcuNjEsNjc0LjIyNjkgQTAsMCAwIDAgMCA3LjYxLDY3NC4yMjY5IiBmaWxsPSIjRkVGRkREIiBzdHlsZT0ic3Ryb2tlOiMxODE4MTg7c3Ryb2tlLXdpZHRoOjAuNTsiLz48cGF0aCBkPSJNOTIuNTI4NSw2NzQuMjI2OSBMOTIuNTI4NSw2ODQuMjI2OSBMMTAyLjUyODUsNjg0LjIyNjkgTDkyLjUyODUsNjc0LjIyNjkiIGZpbGw9IiNGRUZGREQiIHN0eWxlPSJzdHJva2U6IzE4MTgxODtzdHJva2Utd2lkdGg6MC41OyIvPjx0ZXh0IGZpbGw9IiMwMDAwMDAiIGZvbnQtZmFtaWx5PSJzYW5zLXNlcmlmIiBmb250LXNpemU9IjEzIiBsZW5ndGhBZGp1c3Q9InNwYWNpbmciIHRleHRMZW5ndGg9IjczLjkxODUiIHg9IjEzLjYxIiB5PSI2OTEuMjkzOCI+JiMyMDg1NTsmIzIwMzA3OyYjMjI3ODg7JiMyOTcwMjsmIzMyNzczO0I8L3RleHQ+PC9nPjwhLS1jbGFzcyBDb25jcmV0ZUhhbmRsZXJOLS0+PGcgY2xhc3M9ImVudGl0eSIgZGF0YS1lbnRpdHk9IkNvbmNyZXRlSGFuZGxlck4iIGRhdGEtc291cmNlLWxpbmU9IjEwIiBkYXRhLXVpZD0iZW50MDAxNCIgaWQ9ImVudGl0eV9Db25jcmV0ZUhhbmRsZXJOIj48cmVjdCBmaWxsPSIjRjFGMUYxIiBoZWlnaHQ9IjcyLjU2MjUiIHJ4PSIyLjUiIHJ5PSIyLjUiIHN0eWxlPSJzdHJva2U6IzE4MTgxODtzdHJva2Utd2lkdGg6MC41OyIgd2lkdGg9IjE4MC45Mjk3IiB4PSIyNzMuNjEiIHk9IjgxOC4wNzY5Ii8+PGVsbGlwc2UgY3g9IjMxOC4xNDk3IiBjeT0iODM4LjIwOTciIGZpbGw9IiNBREQxQjIiIHJ4PSIxMSIgcnk9IjExIiBzdHlsZT0ic3Ryb2tlOiMxODE4MTg7c3Ryb2tlLXdpZHRoOjE7Ii8+PHBhdGggZD0iTTMyMS4xMTg1LDg0My44NTAzIFEzMjAuNTQwMyw4NDQuMTQ3MiAzMTkuODk5Nyw4NDQuMjg3OCBRMzE5LjI1OTEsODQ0LjQ0NDEgMzE4LjU1Niw4NDQuNDQ0MSBRMzE2LjA1Niw4NDQuNDQ0MSAzMTQuNzI3OCw4NDIuODAzNCBRMzEzLjQxNTMsODQxLjE0NzIgMzEzLjQxNTMsODM4LjAyMjIgUTMxMy40MTUzLDgzNC44OTcyIDMxNC43Mjc4LDgzMy4yNDA5IFEzMTYuMDU2LDgzMS41ODQ3IDMxOC41NTYsODMxLjU4NDcgUTMxOS4yNTkxLDgzMS41ODQ3IDMxOS44OTk3LDgzMS43NDA5IFEzMjAuNTU2LDgzMS44OTcyIDMyMS4xMTg1LDgzMi4xOTQxIEwzMjEuMTE4NSw4MzQuOTEyOCBRMzIwLjQ5MzUsODM0LjMzNDcgMzE5Ljg5OTcsODM0LjA2OTEgUTMxOS4zMDYsODMzLjc4NzggMzE4LjY4MSw4MzMuNzg3OCBRMzE3LjMzNzIsODMzLjc4NzggMzE2LjY0OTcsODM0Ljg2NTkgUTMxNS45NjIyLDgzNS45Mjg0IDMxNS45NjIyLDgzOC4wMjIyIFEzMTUuOTYyMiw4NDAuMTE1OSAzMTYuNjQ5Nyw4NDEuMTk0MSBRMzE3LjMzNzIsODQyLjI1NjYgMzE4LjY4MSw4NDIuMjU2NiBRMzE5LjMwNiw4NDIuMjU2NiAzMTkuODk5Nyw4NDEuOTkwOSBRMzIwLjQ5MzUsODQxLjcwOTcgMzIxLjExODUsODQxLjEzMTYgTDMyMS4xMTg1LDg0My44NTAzIFogIiBmaWxsPSIjMDAwMDAwIi8+PHRleHQgZmlsbD0iIzAwMDAwMCIgZm9udC1mYW1pbHk9InNhbnMtc2VyaWYiIGZvbnQtc2l6ZT0iMTIiIGZvbnQtc3R5bGU9Iml0YWxpYyIgbGVuZ3RoQWRqdXN0PSJzcGFjaW5nIiB0ZXh0TGVuZ3RoPSI0NC40NzI3IiB4PSIzNTguMDg4NSIgeT0iODM0LjIxNTUiPiYjMTcxO2NsYXNzJiMxODc7PC90ZXh0Pjx0ZXh0IGZpbGw9IiMwMDAwMDAiIGZvbnQtZmFtaWx5PSJzYW5zLXNlcmlmIiBmb250LXNpemU9IjE0IiBsZW5ndGhBZGp1c3Q9InNwYWNpbmciIHRleHRMZW5ndGg9IjgzLjM1MDMiIHg9IjMzOC42NDk3IiB5PSI4NTAuMDQwNyI+JiMyMDg1NTsmIzIwMzA3OyYjMjI3ODg7JiMyOTcwMjsmIzMyNzczOy4uLjwvdGV4dD48bGluZSBzdHlsZT0ic3Ryb2tlOiMxODE4MTg7c3Ryb2tlLXdpZHRoOjAuNTsiIHgxPSIyNzQuNjEiIHgyPSI0NTMuNTM5NyIgeTE9Ijg1OC4zNDI1IiB5Mj0iODU4LjM0MjUiLz48bGluZSBzdHlsZT0ic3Ryb2tlOiMxODE4MTg7c3Ryb2tlLXdpZHRoOjAuNTsiIHgxPSIyNzQuNjEiIHgyPSI0NTMuNTM5NyIgeTE9Ijg2Ni4zNDI1IiB5Mj0iODY2LjM0MjUiLz48ZyBkYXRhLXZpc2liaWxpdHktbW9kaWZpZXI9IlBVQkxJQ19NRVRIT0QiPjxlbGxpcHNlIGN4PSIyODQuNjEiIGN5PSI4NzkuOTkwOSIgZmlsbD0iIzg0QkU4NCIgcng9IjMiIHJ5PSIzIiBzdHlsZT0ic3Ryb2tlOiMwMzgwNDg7c3Ryb2tlLXdpZHRoOjE7Ii8+PC9nPjx0ZXh0IGZpbGw9IiMwMDAwMDAiIGZvbnQtZmFtaWx5PSJzYW5zLXNlcmlmIiBmb250LXNpemU9IjE0IiBsZW5ndGhBZGp1c3Q9InNwYWNpbmciIHRleHRMZW5ndGg9IjE1NC45Mjk3IiB4PSIyOTMuNjEiIHk9Ijg4My4zMzc2Ij5oYW5kbGVSZXF1ZXN0KCk6IHZvaWQ8L3RleHQ+PC9nPjxnIGNsYXNzPSJlbnRpdHkiIGRhdGEtZW50aXR5PSJHTU4xNSIgZGF0YS1zb3VyY2UtbGluZT0iMTIiIGRhdGEtdWlkPSJlbnQwMDE2IiBpZD0iZW50aXR5X0dNTjE1Ij48cGF0aCBkPSJNNDg5LjM3LDg0MS43ODY5IEw0ODkuMzcsODUwLjM1NjkgTDQ1NC43Niw4NTQuMzU2OSBMNDg5LjM3LDg1OC4zNTY5IEw0ODkuMzcsODY2LjkxOTcgQTAsMCAwIDAgMCA0ODkuMzcsODY2LjkxOTcgTDUyMi43NjcsODY2LjkxOTcgQTAsMCAwIDAgMCA1MjIuNzY3LDg2Ni45MTk3IEw1MjIuNzY3LDg1MS43ODY5IEw1MTIuNzY3LDg0MS43ODY5IEw0ODkuMzcsODQxLjc4NjkgQTAsMCAwIDAgMCA0ODkuMzcsODQxLjc4NjkiIGZpbGw9IiNGRUZGREQiIHN0eWxlPSJzdHJva2U6IzE4MTgxODtzdHJva2Utd2lkdGg6MC41OyIvPjxwYXRoIGQ9Ik01MTIuNzY3LDg0MS43ODY5IEw1MTIuNzY3LDg1MS43ODY5IEw1MjIuNzY3LDg1MS43ODY5IEw1MTIuNzY3LDg0MS43ODY5IiBmaWxsPSIjRkVGRkREIiBzdHlsZT0ic3Ryb2tlOiMxODE4MTg7c3Ryb2tlLXdpZHRoOjAuNTsiLz48dGV4dCBmaWxsPSIjMDAwMDAwIiBmb250LWZhbWlseT0ic2Fucy1zZXJpZiIgZm9udC1zaXplPSIxMyIgbGVuZ3RoQWRqdXN0PSJzcGFjaW5nIiB0ZXh0TGVuZ3RoPSIxMi4zOTciIHg9IjQ5NS4zNyIgeT0iODU4Ljg1MzgiPi4uLjwvdGV4dD48L2c+PCEtLWNsYXNzIENsaWVudC0tPjxnIGNsYXNzPSJlbnRpdHkiIGRhdGEtZW50aXR5PSJDbGllbnQiIGRhdGEtc291cmNlLWxpbmU9IjEzIiBkYXRhLXVpZD0iZW50MDAxOCIgaWQ9ImVudGl0eV9DbGllbnQiPjxyZWN0IGZpbGw9IiNGMUYxRjEiIGhlaWdodD0iNTYuMjY1NiIgcng9IjIuNSIgcnk9IjIuNSIgc3R5bGU9InN0cm9rZTojMTgxODE4O3N0cm9rZS13aWR0aDowLjU7IiB3aWR0aD0iNzIuNTMwMyIgeD0iNDUwLjgiIHk9Ijg4LjI2NjkiLz48ZWxsaXBzZSBjeD0iNDY1LjgiIGN5PSIxMDguMzk5NyIgZmlsbD0iI0FERDFCMiIgcng9IjExIiByeT0iMTEiIHN0eWxlPSJzdHJva2U6IzE4MTgxODtzdHJva2Utd2lkdGg6MTsiLz48cGF0aCBkPSJNNDY4Ljc2ODgsMTE0LjA0MDMgUTQ2OC4xOTA2LDExNC4zMzcyIDQ2Ny41NSwxMTQuNDc3OCBRNDY2LjkwOTQsMTE0LjYzNDEgNDY2LjIwNjMsMTE0LjYzNDEgUTQ2My43MDYzLDExNC42MzQxIDQ2Mi4zNzgxLDExMi45OTM0IFE0NjEuMDY1NiwxMTEuMzM3MiA0NjEuMDY1NiwxMDguMjEyMiBRNDYxLjA2NTYsMTA1LjA4NzIgNDYyLjM3ODEsMTAzLjQzMDkgUTQ2My43MDYzLDEwMS43NzQ3IDQ2Ni4yMDYzLDEwMS43NzQ3IFE0NjYuOTA5NCwxMDEuNzc0NyA0NjcuNTUsMTAxLjkzMDkgUTQ2OC4yMDYzLDEwMi4wODcyIDQ2OC43Njg4LDEwMi4zODQxIEw0NjguNzY4OCwxMDUuMTAyOCBRNDY4LjE0MzgsMTA0LjUyNDcgNDY3LjU1LDEwNC4yNTkxIFE0NjYuOTU2MywxMDMuOTc3OCA0NjYuMzMxMywxMDMuOTc3OCBRNDY0Ljk4NzUsMTAzLjk3NzggNDY0LjMsMTA1LjA1NTkgUTQ2My42MTI1LDEwNi4xMTg0IDQ2My42MTI1LDEwOC4yMTIyIFE0NjMuNjEyNSwxMTAuMzA1OSA0NjQuMywxMTEuMzg0MSBRNDY0Ljk4NzUsMTEyLjQ0NjYgNDY2LjMzMTMsMTEyLjQ0NjYgUTQ2Ni45NTYzLDExMi40NDY2IDQ2Ny41NSwxMTIuMTgwOSBRNDY4LjE0MzgsMTExLjg5OTcgNDY4Ljc2ODgsMTExLjMyMTYgTDQ2OC43Njg4LDExNC4wNDAzIFogIiBmaWxsPSIjMDAwMDAwIi8+PHRleHQgZmlsbD0iIzAwMDAwMCIgZm9udC1mYW1pbHk9InNhbnMtc2VyaWYiIGZvbnQtc2l6ZT0iMTIiIGZvbnQtc3R5bGU9Iml0YWxpYyIgbGVuZ3RoQWRqdXN0PSJzcGFjaW5nIiB0ZXh0TGVuZ3RoPSI0NC40NzI3IiB4PSI0NzcuODI4OCIgeT0iMTA0LjQwNTUiPiYjMTcxO2NsYXNzJiMxODc7PC90ZXh0Pjx0ZXh0IGZpbGw9IiMwMDAwMDAiIGZvbnQtZmFtaWx5PSJzYW5zLXNlcmlmIiBmb250LXNpemU9IjE0IiBsZW5ndGhBZGp1c3Q9InNwYWNpbmciIHRleHRMZW5ndGg9IjQwLjUzMDMiIHg9IjQ3OS44IiB5PSIxMjAuMjMwNyI+Q2xpZW50PC90ZXh0PjxsaW5lIHN0eWxlPSJzdHJva2U6IzE4MTgxODtzdHJva2Utd2lkdGg6MC41OyIgeDE9IjQ1MS44IiB4Mj0iNTIyLjMzMDMiIHkxPSIxMjguNTMyNSIgeTI9IjEyOC41MzI1Ii8+PGxpbmUgc3R5bGU9InN0cm9rZTojMTgxODE4O3N0cm9rZS13aWR0aDowLjU7IiB4MT0iNDUxLjgiIHgyPSI1MjIuMzMwMyIgeTE9IjEzNi41MzI1IiB5Mj0iMTM2LjUzMjUiLz48L2c+PGcgY2xhc3M9ImVudGl0eSIgZGF0YS1lbnRpdHk9IkdNTjE5IiBkYXRhLXNvdXJjZS1saW5lPSIxNCIgZGF0YS11aWQ9ImVudDAwMjAiIGlkPSJlbnRpdHlfR01OMTkiPjxwYXRoIGQ9Ik0xMDAuMDYsNDMuMjk2OSBMMTAwLjA2LDE4OS40OTIyIEEwLDAgMCAwIDAgMTAwLjA2LDE4OS40OTIyIEw0MTYuMDczNywxODkuNDkyMiBBMCwwIDAgMCAwIDQxNi4wNzM3LDE4OS40OTIyIEw0MTYuMDczNywxMjAuMzk2OSBMNDUwLjM2LDExNi4zOTY5IEw0MTYuMDczNywxMTIuMzk2OSBMNDE2LjA3MzcsNTMuMjk2OSBMNDA2LjA3MzcsNDMuMjk2OSBMMTAwLjA2LDQzLjI5NjkgQTAsMCAwIDAgMCAxMDAuMDYsNDMuMjk2OSIgZmlsbD0iI0ZFRkZERCIgc3R5bGU9InN0cm9rZTojMTgxODE4O3N0cm9rZS13aWR0aDowLjU7Ii8+PHBhdGggZD0iTTQwNi4wNzM3LDQzLjI5NjkgTDQwNi4wNzM3LDUzLjI5NjkgTDQxNi4wNzM3LDUzLjI5NjkgTDQwNi4wNzM3LDQzLjI5NjkiIGZpbGw9IiNGRUZGREQiIHN0eWxlPSJzdHJva2U6IzE4MTgxODtzdHJva2Utd2lkdGg6MC41OyIvPjx0ZXh0IGZpbGw9IiMwMDAwMDAiIGZvbnQtZmFtaWx5PSJzYW5zLXNlcmlmIiBmb250LXNpemU9IjEzIiBsZW5ndGhBZGp1c3Q9InNwYWNpbmciIHRleHRMZW5ndGg9IjEyOC42MzU0IiB4PSIxMDYuMDYiIHk9IjYwLjM2MzgiPiYjMjM0NTg7JiMyNTE0MzsmIzMxNDcxOyYjNjUyODg7Q2xpZW50JiM2NTI4OTsmIzM1MjgyOyYjMzMzOTQ7PC90ZXh0Pjx0ZXh0IGZpbGw9IiMwMDAwMDAiIGZvbnQtZmFtaWx5PSJzYW5zLXNlcmlmIiBmb250LXNpemU9IjEzIiBsZW5ndGhBZGp1c3Q9InNwYWNpbmciIHRleHRMZW5ndGg9IjI4OS4yMTgzIiB4PSIxMDYuMDYiIHk9Ijc1LjQ5NjYiPkhhbmRsZXIgaGFuZGxlckE9bmV3IENvbmNyZXRlSGFuZGxlckEoKTs8L3RleHQ+PHRleHQgZmlsbD0iIzAwMDAwMCIgZm9udC1mYW1pbHk9InNhbnMtc2VyaWYiIGZvbnQtc2l6ZT0iMTMiIGxlbmd0aEFkanVzdD0ic3BhY2luZyIgdGV4dExlbmd0aD0iMjg5LjI2OSIgeD0iMTA2LjA2IiB5PSI5MC42Mjk0Ij5IYW5kbGVyIGhhbmRsZXJCPW5ldyBDb25jcmV0ZUhhbmRsZXJCKCk7PC90ZXh0Pjx0ZXh0IGZpbGw9IiMwMDAwMDAiIGZvbnQtZmFtaWx5PSJzYW5zLXNlcmlmIiBmb250LXNpemU9IjEzIiBsZW5ndGhBZGp1c3Q9InNwYWNpbmciIHRleHRMZW5ndGg9IjEyLjM5NyIgeD0iMTA2LjA2IiB5PSIxMDUuNzYyMiI+Li4uPC90ZXh0Pjx0ZXh0IGZpbGw9IiMwMDAwMDAiIGZvbnQtZmFtaWx5PSJzYW5zLXNlcmlmIiBmb250LXNpemU9IjEzIiBsZW5ndGhBZGp1c3Q9InNwYWNpbmciIHRleHRMZW5ndGg9IjI5MC44ODEzIiB4PSIxMDYuMDYiIHk9IjEyMC44OTUiPkhhbmRsZXIgaGFuZGxlck49bmV3IENvbmNyZXRlSGFuZGxlck4oKTs8L3RleHQ+PHRleHQgZmlsbD0iIzAwMDAwMCIgZm9udC1mYW1pbHk9InNhbnMtc2VyaWYiIGZvbnQtc2l6ZT0iMTMiIGxlbmd0aEFkanVzdD0ic3BhY2luZyIgdGV4dExlbmd0aD0iMTkwLjI4MzciIHg9IjEwNi4wNiIgeT0iMTM2LjAyNzgiPmhhbmRsZXJBLnNldE5leHQgKGhhbmRsZXJCKTs8L3RleHQ+PHRleHQgZmlsbD0iIzAwMDAwMCIgZm9udC1mYW1pbHk9InNhbnMtc2VyaWYiIGZvbnQtc2l6ZT0iMTMiIGxlbmd0aEFkanVzdD0ic3BhY2luZyIgdGV4dExlbmd0aD0iMTIuMzk3IiB4PSIxMDYuMDYiIHk9IjE1MS4xNjA2Ij4uLi48L3RleHQ+PHRleHQgZmlsbD0iIzAwMDAwMCIgZm9udC1mYW1pbHk9InNhbnMtc2VyaWYiIGZvbnQtc2l6ZT0iMTMiIGxlbmd0aEFkanVzdD0ic3BhY2luZyIgdGV4dExlbmd0aD0iMTkxLjExNTIiIHg9IjEwNi4wNiIgeT0iMTY2LjI5MzUiPmhhbmRsZXJCLnNldE5leHQgKGhhbmRsZXJOKTs8L3RleHQ+PHRleHQgZmlsbD0iIzAwMDAwMCIgZm9udC1mYW1pbHk9InNhbnMtc2VyaWYiIGZvbnQtc2l6ZT0iMTMiIGxlbmd0aEFkanVzdD0ic3BhY2luZyIgdGV4dExlbmd0aD0iMTc0Ljg5NyIgeD0iMTA2LjA2IiB5PSIxODEuNDI2MyI+aGFuZGxlckEuaGFuZGxlUmVxdWVzdCgpOzwvdGV4dD48L2c+PHBhdGggZD0iTTYsMzEwLjA4NjkgTDYsMzM1LjIxOTcgQTAsMCAwIDAgMCA2LDMzNS4yMTk3IEwxNDQuMDAwMSwzMzUuMjE5NyBBMCwwIDAgMCAwIDE0NC4wMDAxLDMzNS4yMTk3IEwxNDQuMDAwMSwzMjguMDg2OSBMMzY5LjQ5LDMzNi45MTA5IEwxNDQuMDAwMSwzMjAuMDg2OSBMMTQ0LjAwMDEsMzIwLjA4NjkgTDEzNC4wMDAxLDMxMC4wODY5IEw2LDMxMC4wODY5IEEwLDAgMCAwIDAgNiwzMTAuMDg2OSIgZmlsbD0iI0ZFRkZERCIgc3R5bGU9InN0cm9rZTojMTgxODE4O3N0cm9rZS13aWR0aDowLjU7Ii8+PHBhdGggZD0iTTEzNC4wMDAxLDMxMC4wODY5IEwxMzQuMDAwMSwzMjAuMDg2OSBMMTQ0LjAwMDEsMzIwLjA4NjkgTDEzNC4wMDAxLDMxMC4wODY5IiBmaWxsPSIjRkVGRkREIiBzdHlsZT0ic3Ryb2tlOiMxODE4MTg7c3Ryb2tlLXdpZHRoOjAuNTsiLz48dGV4dCBmaWxsPSIjMDAwMDAwIiBmb250LWZhbWlseT0ic2Fucy1zZXJpZiIgZm9udC1zaXplPSIxMyIgbGVuZ3RoQWRqdXN0PSJzcGFjaW5nIiB0ZXh0TGVuZ3RoPSIxMTcuMDAwMSIgeD0iMTIiIHk9IjMyNy4xNTM4Ij4mIzE5OTc5OyYjMTk5Njg7JiMyMDAxMDsmIzIyNzg4OyYjMjk3MDI7JiMzMjc3MzsmIzMwMzQwOyYjMjQzNDE7JiMyOTk5Mjs8L3RleHQ+PHBhdGggZD0iTTYsMzQ1LjIxOTcgTDYsMzcwLjM1MjUgQTAsMCAwIDAgMCA2LDM3MC4zNTI1IEwzMzAuMTMyNiwzNzAuMzUyNSBBMCwwIDAgMCAwIDMzMC4xMzI2LDM3MC4zNTI1IEwzMzAuMTMyNiwzNjMuMjE5NyBMMzY5LjQ5LDM5My44MDE2IEwzMzAuMTMyNiwzNTUuMjE5NyBMMzMwLjEzMjYsMzU1LjIxOTcgTDMyMC4xMzI2LDM0NS4yMTk3IEw2LDM0NS4yMTk3IEEwLDAgMCAwIDAgNiwzNDUuMjE5NyIgZmlsbD0iI0ZFRkZERCIgc3R5bGU9InN0cm9rZTojMTgxODE4O3N0cm9rZS13aWR0aDowLjU7Ii8+PHBhdGggZD0iTTMyMC4xMzI2LDM0NS4yMTk3IEwzMjAuMTMyNiwzNTUuMjE5NyBMMzMwLjEzMjYsMzU1LjIxOTcgTDMyMC4xMzI2LDM0NS4yMTk3IiBmaWxsPSIjRkVGRkREIiBzdHlsZT0ic3Ryb2tlOiMxODE4MTg7c3Ryb2tlLXdpZHRoOjAuNTsiLz48dGV4dCBmaWxsPSIjMDAwMDAwIiBmb250LWZhbWlseT0ic2Fucy1zZXJpZiIgZm9udC1zaXplPSIxMyIgbGVuZ3RoQWRqdXN0PSJzcGFjaW5nIiB0ZXh0TGVuZ3RoPSIzMDMuMTMyNiIgeD0iMTIiIHk9IjM2Mi4yODY2Ij4mIzI1MTUyOyYjMjYzNzc7JiMyMjc4ODsmIzI5NzAyOyYjMzI3NzM7JiMyOTk5MjsmIzIwMTEwOyYjMzU4MzE7JiMyNzcxNDsmIzIyNzg4OyYjMjk3MDI7JiMzMDM0MDsmIzM2ODkwOyYjMjk5OTI7JiMyNTUwOTsmIzIxNDc1OywmIzI2MTU5OyYjMTk5Njg7JiMyMDAxMDsmIzI1Mjc3OyYjMzU5Mzc7JiMyNjA0MTsmIzI3ODYxOzwvdGV4dD48cGF0aCBkPSJNNjIxLjA3LDUwMS42NjY5IEw2MjEuMDcsNTEwLjIzMzMgTDI4MS41Mzk3LDU0My4zNjA5IEw2MjEuMDcsNTE4LjIzMzMgTDYyMS4wNyw1MjYuNzk5NyBBMCwwIDAgMCAwIDYyMS4wNyw1MjYuNzk5NyBMOTY3LjA3MDMsNTI2Ljc5OTcgQTAsMCAwIDAgMCA5NjcuMDcwMyw1MjYuNzk5NyBMOTY3LjA3MDMsNTExLjY2NjkgTDk1Ny4wNzAzLDUwMS42NjY5IEw2MjEuMDcsNTAxLjY2NjkgQTAsMCAwIDAgMCA2MjEuMDcsNTAxLjY2NjkiIGZpbGw9IiNGRUZGREQiIHN0eWxlPSJzdHJva2U6IzE4MTgxODtzdHJva2Utd2lkdGg6MC41OyIvPjxwYXRoIGQ9Ik05NTcuMDcwMyw1MDEuNjY2OSBMOTU3LjA3MDMsNTExLjY2NjkgTDk2Ny4wNzAzLDUxMS42NjY5IEw5NTcuMDcwMyw1MDEuNjY2OSIgZmlsbD0iI0ZFRkZERCIgc3R5bGU9InN0cm9rZTojMTgxODE4O3N0cm9rZS13aWR0aDowLjU7Ii8+PHRleHQgZmlsbD0iIzAwMDAwMCIgZm9udC1mYW1pbHk9InNhbnMtc2VyaWYiIGZvbnQtc2l6ZT0iMTMiIGxlbmd0aEFkanVzdD0ic3BhY2luZyIgdGV4dExlbmd0aD0iMzI1LjAwMDMiIHg9IjYyNy4wNyIgeT0iNTE4LjczMzgiPiYjMjUyNzc7JiMzNTkzNzsmIzIyNzg4OyYjMjk3MDI7JiMzMjc3MzsmIzMwMzQwOyYjMjI3ODg7JiMyOTcwMjsmIzI2MDQxOyYjMjc4NjE7JiMyMzQ1NDsmIzI5NjE2OyYjNjUyOTI7JiMyMTI1MzsmIzIxNTQ3OyYjMjAxMDI7JiMyMzQ1NDsmIzM4NDY5OyYjMzAzNDA7JiMzNTgzMTsmIzI3NzE0OyYjMjI3ODg7JiMyOTcwMjsmIzIwMTk1OyYjMzA3MjE7PC90ZXh0PjwhLS1yZXZlcnNlIGxpbmsgSGFuZGxlciB0byBDb25jcmV0ZUhhbmRsZXJBLS0+PGcgY2xhc3M9ImxpbmsiIGRhdGEtZW50aXR5LTE9IkhhbmRsZXIiIGRhdGEtZW50aXR5LTI9IkNvbmNyZXRlSGFuZGxlckEiIGRhdGEtc291cmNlLWxpbmU9IjQ3IiBkYXRhLXVpZD0ibG5rMjYiIGlkPSJsaW5rX0hhbmRsZXJfQ29uY3JldGVIYW5kbGVyQSI+PHBhdGggY29kZUxpbmU9IjQ3IiBkPSJNMzcwLjE1ODMsNDE1LjU2OTIgQzMyNy4xOTgzLDQ0MS4wNDkyIDI5NC4xLDQ2MC42NzY5IDI1Ny4wMSw0ODIuNjc2OSIgZmlsbD0ibm9uZSIgaWQ9IkhhbmRsZXItYmFja3RvLUNvbmNyZXRlSGFuZGxlckEiIHN0eWxlPSJzdHJva2U6IzE4MTgxODtzdHJva2Utd2lkdGg6MTsiLz48cG9seWdvbiBmaWxsPSJub25lIiBwb2ludHM9IjM4NS42NCw0MDYuMzg2OSwzNjcuMDk3NSw0MTAuNDA4NywzNzMuMjE5MSw0MjAuNzI5OCwzODUuNjQsNDA2LjM4NjkiIHN0eWxlPSJzdHJva2U6IzE4MTgxODtzdHJva2Utd2lkdGg6MTsiLz48dGV4dCBmaWxsPSIjMDAwMDAwIiBmb250LWZhbWlseT0ic2Fucy1zZXJpZiIgZm9udC1zaXplPSIxMyIgbGVuZ3RoQWRqdXN0PSJzcGFjaW5nIiB0ZXh0TGVuZ3RoPSIyNiIgeD0iMzM0LjA3IiB5PSI0NDkuMDEzOCI+JiMzMjQ4NzsmIzI1MjE1OzwvdGV4dD48L2c+PCEtLXJldmVyc2UgbGluayBIYW5kbGVyIHRvIENvbmNyZXRlSGFuZGxlckItLT48ZyBjbGFzcz0ibGluayIgZGF0YS1lbnRpdHktMT0iSGFuZGxlciIgZGF0YS1lbnRpdHktMj0iQ29uY3JldGVIYW5kbGVyQiIgZGF0YS1zb3VyY2UtbGluZT0iNDgiIGRhdGEtdWlkPSJsbmsyNyIgaWQ9ImxpbmtfSGFuZGxlcl9Db25jcmV0ZUhhbmRsZXJCIj48cGF0aCBjb2RlTGluZT0iNDgiIGQ9Ik01MDMuMTk2Nyw0MjMuOTQwNCBDNTEyLjk2NjcsNDg0Ljk0MDQgNTE0LjMyLDU2MC45NjY5IDQ2NS4wNyw2MjAuNTA2OSBDNDQ2LjEsNjQzLjQ0NjkgMzc3LjQzLDY2MC43NzY5IDMxOC44Niw2NzEuNzg2OSIgZmlsbD0ibm9uZSIgaWQ9IkhhbmRsZXItYmFja3RvLUNvbmNyZXRlSGFuZGxlckIiIHN0eWxlPSJzdHJva2U6IzE4MTgxODtzdHJva2Utd2lkdGg6MTsiLz48cG9seWdvbiBmaWxsPSJub25lIiBwb2ludHM9IjUwMC4zNSw0MDYuMTY2OSw0OTcuMjcyMiw0MjQuODg5Miw1MDkuMTIxMiw0MjIuOTkxNSw1MDAuMzUsNDA2LjE2NjkiIHN0eWxlPSJzdHJva2U6IzE4MTgxODtzdHJva2Utd2lkdGg6MTsiLz48dGV4dCBmaWxsPSIjMDAwMDAwIiBmb250LWZhbWlseT0ic2Fucy1zZXJpZiIgZm9udC1zaXplPSIxMyIgbGVuZ3RoQWRqdXN0PSJzcGFjaW5nIiB0ZXh0TGVuZ3RoPSIyNiIgeD0iNTA3LjA3IiB5PSI1MjMuNzkzOCI+JiMzMjQ4NzsmIzI1MjE1OzwvdGV4dD48L2c+PCEtLXJldmVyc2UgbGluayBIYW5kbGVyIHRvIENvbmNyZXRlSGFuZGxlck4tLT48ZyBjbGFzcz0ibGluayIgZGF0YS1lbnRpdHktMT0iSGFuZGxlciIgZGF0YS1lbnRpdHktMj0iQ29uY3JldGVIYW5kbGVyTiIgZGF0YS1zb3VyY2UtbGluZT0iNDkiIGRhdGEtdWlkPSJsbmsyOCIgaWQ9ImxpbmtfSGFuZGxlcl9Db25jcmV0ZUhhbmRsZXJOIj48cGF0aCBjb2RlTGluZT0iNDkiIGQ9Ik01MjYuOTM0OSw0MjIuNjE2MyBDNTcwLjcyNDksNTEzLjkwNjMgNjI3LjI0LDY3Mi43MjY5IDU0Ni4wNyw3ODguMDc2OSBDNTQzLjAzLDc5Mi4zOTY5IDQ5OC44OSw4MDguMjc2OSA0NTQuODUsODIzLjI5NjkiIGZpbGw9Im5vbmUiIGlkPSJIYW5kbGVyLWJhY2t0by1Db25jcmV0ZUhhbmRsZXJOIiBzdHlsZT0ic3Ryb2tlOiMxODE4MTg7c3Ryb2tlLXdpZHRoOjE7Ii8+PHBvbHlnb24gZmlsbD0ibm9uZSIgcG9pbnRzPSI1MTkuMTUsNDA2LjM4NjksNTIxLjUyNTEsNDI1LjIxMTMsNTMyLjM0NDgsNDIwLjAyMTMsNTE5LjE1LDQwNi4zODY5IiBzdHlsZT0ic3Ryb2tlOiMxODE4MTg7c3Ryb2tlLXdpZHRoOjE7Ii8+PHRleHQgZmlsbD0iIzAwMDAwMCIgZm9udC1mYW1pbHk9InNhbnMtc2VyaWYiIGZvbnQtc2l6ZT0iMTMiIGxlbmd0aEFkanVzdD0ic3BhY2luZyIgdGV4dExlbmd0aD0iMjYiIHg9IjU4NC4wNyIgeT0iNjA3LjU3MzgiPiYjMzI0ODc7JiMyNTIxNTs8L3RleHQ+PC9nPjwhLS1saW5rIENsaWVudCB0byBIYW5kbGVyLS0+PGcgY2xhc3M9ImxpbmsiIGRhdGEtZW50aXR5LTE9IkNsaWVudCIgZGF0YS1lbnRpdHktMj0iSGFuZGxlciIgZGF0YS1zb3VyY2UtbGluZT0iNTEiIGRhdGEtdWlkPSJsbmsyOSIgaWQ9ImxpbmtfQ2xpZW50X0hhbmRsZXIiPjxwYXRoIGNvZGVMaW5lPSI1MSIgZD0iTTQ4Ny4wNywxNDQuNzU2OSBDNDg3LjA3LDE3OS4xNjY5IDQ4Ny4wNywyMzMuMjA2OSA0ODcuMDcsMjc4LjQ0NjkiIGZpbGw9Im5vbmUiIGlkPSJDbGllbnQtdG8tSGFuZGxlciIgc3R5bGU9InN0cm9rZTojMTgxODE4O3N0cm9rZS13aWR0aDoyOyIvPjxwb2x5Z29uIGZpbGw9IiMxODE4MTgiIHBvaW50cz0iNDg3LjA3LDI4NC40NDY5LDQ5MS4wNywyNzUuNDQ2OSw0ODcuMDcsMjc5LjQ0NjksNDgzLjA3LDI3NS40NDY5LDQ4Ny4wNywyODQuNDQ2OSIgc3R5bGU9InN0cm9rZTojMTgxODE4O3N0cm9rZS13aWR0aDoyOyIvPjxwYXRoIGQ9Ik00OTIuMDcsMjI0LjQ5NjkgTDQ5Mi4wNywyNDkuNDk2OSBMODI1LjA3LDI0OS40OTY5IEw4MjUuMDcsMjM0LjQ5NjkgTDgxNS4wNywyMjQuNDk2OSBMNDkyLjA3LDIyNC40OTY5IiBmaWxsPSIjMDA4MDAwIiBzdHlsZT0ic3Ryb2tlOiMxODE4MTg7c3Ryb2tlLXdpZHRoOjAuNTsiLz48cGF0aCBkPSJNODE1LjA3LDIyNC40OTY5IEw4MTUuMDcsMjM0LjQ5NjkgTDgyNS4wNywyMzQuNDk2OSBMODE1LjA3LDIyNC40OTY5IiBmaWxsPSIjMDA4MDAwIiBzdHlsZT0ic3Ryb2tlOiMxODE4MTg7c3Ryb2tlLXdpZHRoOjAuNTsiLz48dGV4dCBmaWxsPSIjMDAwMDAwIiBmb250LWZhbWlseT0ic2Fucy1zZXJpZiIgZm9udC1zaXplPSIxMyIgbGVuZ3RoQWRqdXN0PSJzcGFjaW5nIiB0ZXh0TGVuZ3RoPSIzMTIuMDAwMyIgeD0iNDk4LjA3IiB5PSIyNDEuNTYzOCI+JiMyMTAxOTsmIzI0MzE0OyYjMzYxMzE7JiMyMDIxOTsmIzM4MTQyOyYjNjUyOTI7JiMyNDE4MjsmIzIxNTIxOyYjMzgxNDI7JiMxOTk3ODsmIzMwMzQwOyYjMjAyMTk7JiMyNDg0NzsmIzE5OTY4OyYjMjAwMTA7JiMyMjc4ODsmIzI5NzAyOyYjMzI3NzM7JiMyMzU0NTsmIzM1OTM3OyYjMjU1NTI7JiMyMDEzMjsmIzM1ODMxOyYjMjc3MTQ7PC90ZXh0PjwvZz48IS0tcmV2ZXJzZSBsaW5rIEhhbmRsZXIgdG8gSGFuZGxlci0tPjxnIGNsYXNzPSJsaW5rIiBkYXRhLWVudGl0eS0xPSJIYW5kbGVyIiBkYXRhLWVudGl0eS0yPSJIYW5kbGVyIiBkYXRhLXNvdXJjZS1saW5lPSI1MyIgZGF0YS11aWQ9ImxuazMwIiBpZD0ibGlua19IYW5kbGVyX0hhbmRsZXIiPjxwYXRoIGNvZGVMaW5lPSI1MyIgZD0iTTYyMC43Njk4LDMyOS4yODE4IEM2NDEuMzM5OCwzMzEuNjcxOCA2NDMuNjUsMzM2LjA2NjkgNjQzLjY1LDM0NS4yMjY5IEM2NDMuNjUsMzU0LjM3NjkgNjI5LjQyLDM2MC4xNTY5IDYwOC44NSwzNjIuNTU2OSIgZmlsbD0ibm9uZSIgaWQ9IkhhbmRsZXItYmFja3RvLUhhbmRsZXIiIHN0eWxlPSJzdHJva2U6I0ZGMDAwMDtzdHJva2Utd2lkdGg6MTsiLz48cG9seWdvbiBmaWxsPSJub25lIiBwb2ludHM9IjYwOC44NSwzMjcuODk2OSw2MTQuMzQ4MywzMzIuNTYyNiw2MjAuNzY5OCwzMjkuMjgxOCw2MTUuMjcxNiwzMjQuNjE2MSw2MDguODUsMzI3Ljg5NjkiIHN0eWxlPSJzdHJva2U6I0ZGMDAwMDtzdHJva2Utd2lkdGg6MTsiLz48L2c+PCEtLWxpbmsgQ29uY3JldGVIYW5kbGVyQSB0byBDb25jcmV0ZUhhbmRsZXJCLS0+PGcgY2xhc3M9ImxpbmsiIGRhdGEtZW50aXR5LTE9IkNvbmNyZXRlSGFuZGxlckEiIGRhdGEtZW50aXR5LTI9IkNvbmNyZXRlSGFuZGxlckIiIGRhdGEtc291cmNlLWxpbmU9IjU0IiBkYXRhLXVpZD0ibG5rMzEiIGlkPSJsaW5rX0NvbmNyZXRlSGFuZGxlckFfQ29uY3JldGVIYW5kbGVyQiI+PHBhdGggY29kZUxpbmU9IjU0IiBkPSJNMTA2LjE3LDU0My4wNjY5IEM1OC42NSw1NTkuNjY2OSAxNS4wOSw1ODUuMzk2OSA0MC4wNyw2MjAuNTA2OSBDNDEuNjksNjIyLjc5NjkgODQuMjg1Nyw2MzcuNjE2NCAxMzEuNDk1Nyw2NTMuNTY2NCIgZmlsbD0ibm9uZSIgaWQ9IkNvbmNyZXRlSGFuZGxlckEtdG8tQ29uY3JldGVIYW5kbGVyQiIgc3R5bGU9InN0cm9rZTojMTgxODE4O3N0cm9rZS13aWR0aDoxOyIvPjxwb2x5Z29uIGZpbGw9IiMxODE4MTgiIHBvaW50cz0iMTM3LjE4LDY1NS40ODY5LDEyOS45MzM4LDY0OC44MTY2LDEzMi40NDMsNjUzLjg4NjUsMTI3LjM3MzIsNjU2LjM5NTcsMTM3LjE4LDY1NS40ODY5IiBzdHlsZT0ic3Ryb2tlOiMxODE4MTg7c3Ryb2tlLXdpZHRoOjE7Ii8+PHBhdGggZD0iTTQ1LjA3LDU5MC41MDY5IEw0NS4wNyw2MTUuNTA2OSBMNDU2LjA3LDYxNS41MDY5IEw0NTYuMDcsNjAwLjUwNjkgTDQ0Ni4wNyw1OTAuNTA2OSBMNDUuMDcsNTkwLjUwNjkiIGZpbGw9IiMwMDgwMDAiIHN0eWxlPSJzdHJva2U6IzE4MTgxODtzdHJva2Utd2lkdGg6MC41OyIvPjxwYXRoIGQ9Ik00NDYuMDcsNTkwLjUwNjkgTDQ0Ni4wNyw2MDAuNTA2OSBMNDU2LjA3LDYwMC41MDY5IEw0NDYuMDcsNTkwLjUwNjkiIGZpbGw9IiMwMDgwMDAiIHN0eWxlPSJzdHJva2U6IzE4MTgxODtzdHJva2Utd2lkdGg6MC41OyIvPjx0ZXh0IGZpbGw9IiMwMDAwMDAiIGZvbnQtZmFtaWx5PSJzYW5zLXNlcmlmIiBmb250LXNpemU9IjEzIiBsZW5ndGhBZGp1c3Q9InNwYWNpbmciIHRleHRMZW5ndGg9IjM5MC4wMDAzIiB4PSI1MS4wNyIgeT0iNjA3LjU3MzgiPiYjMjI5MTQ7JiMyNjUyNDsmIzIxNDg3OyYjMjAxOTc7JiMyMjc4ODsmIzI5NzAyOyYjMjQ0MDM7JiMyMTA2OTsmIzM1ODMxOyYjMjc3MTQ7JiM2NTI5MjsmIzIxMDE3OyYjMjI3ODg7JiMyOTcwMjsmIzY1MjkyOyYjMjE1NDI7JiMyMTAxNzsmIzIzNjAxOyYjMjM1NTg7JiMzNTgxMzsmIzM1ODMxOyYjMjc3MTQ7JiMyMDEzMjsmIzMyNDczOyYjMjE1MTg7JiMzMjQ4NzsmIzMyNzczOyYjMjE0MzU7JiMyMjc4ODsmIzI5NzAyOzwvdGV4dD48L2c+PCEtLWxpbmsgQ29uY3JldGVIYW5kbGVyQiB0byBDb25jcmV0ZUhhbmRsZXJOLS0+PGcgY2xhc3M9ImxpbmsiIGRhdGEtZW50aXR5LTE9IkNvbmNyZXRlSGFuZGxlckIiIGRhdGEtZW50aXR5LTI9IkNvbmNyZXRlSGFuZGxlck4iIGRhdGEtc291cmNlLWxpbmU9IjU2IiBkYXRhLXVpZD0ibG5rMzIiIGlkPSJsaW5rX0NvbmNyZXRlSGFuZGxlckJfQ29uY3JldGVIYW5kbGVyTiI+PHBhdGggY29kZUxpbmU9IjU2IiBkPSJNMTUxLjY2LDcyMy40NzY5IEMxMjMuNzQsNzQxLjQ1NjkgMTAyLjk0LDc2NC40MzY5IDEyMS4wNyw3ODguMDc2OSBDMTM5LjkxLDgxMi42NDY5IDIwNi40NzkxLDgyOC45ODY4IDI2Ny40MTkxLDgzOS42MDY4IiBmaWxsPSJub25lIiBpZD0iQ29uY3JldGVIYW5kbGVyQi10by1Db25jcmV0ZUhhbmRsZXJOIiBzdHlsZT0ic3Ryb2tlOiMxODE4MTg7c3Ryb2tlLXdpZHRoOjE7Ii8+PHBvbHlnb24gZmlsbD0iIzE4MTgxOCIgcG9pbnRzPSIyNzMuMzMsODQwLjYzNjksMjY1LjE1MDQsODM1LjE1MTEsMjY4LjQwNDIsODM5Ljc3ODUsMjYzLjc3NjksODQzLjAzMjMsMjczLjMzLDg0MC42MzY5IiBzdHlsZT0ic3Ryb2tlOiMxODE4MTg7c3Ryb2tlLXdpZHRoOjE7Ii8+PHBhdGggZD0iTTEyNi4wNyw3NTguMDc2OSBMMTI2LjA3LDc4My4wNzY5IEw1MzcuMDcsNzgzLjA3NjkgTDUzNy4wNyw3NjguMDc2OSBMNTI3LjA3LDc1OC4wNzY5IEwxMjYuMDcsNzU4LjA3NjkiIGZpbGw9IiMwMDgwMDAiIHN0eWxlPSJzdHJva2U6IzE4MTgxODtzdHJva2Utd2lkdGg6MC41OyIvPjxwYXRoIGQ9Ik01MjcuMDcsNzU4LjA3NjkgTDUyNy4wNyw3NjguMDc2OSBMNTM3LjA3LDc2OC4wNzY5IEw1MjcuMDcsNzU4LjA3NjkiIGZpbGw9IiMwMDgwMDAiIHN0eWxlPSJzdHJva2U6IzE4MTgxODtzdHJva2Utd2lkdGg6MC41OyIvPjx0ZXh0IGZpbGw9IiMwMDAwMDAiIGZvbnQtZmFtaWx5PSJzYW5zLXNlcmlmIiBmb250LXNpemU9IjEzIiBsZW5ndGhBZGp1c3Q9InNwYWNpbmciIHRleHRMZW5ndGg9IjM5MC4wMDAzIiB4PSIxMzIuMDciIHk9Ijc3NS4xNDM4Ij4mIzIyOTE0OyYjMjY1MjQ7JiMyMTQ4NzsmIzIwMTk3OyYjMjI3ODg7JiMyOTcwMjsmIzI0NDAzOyYjMjEwNjk7JiMzNTgzMTsmIzI3NzE0OyYjNjUyOTI7JiMyMTAxNzsmIzIyNzg4OyYjMjk3MDI7JiM2NTI5MjsmIzIxNTQyOyYjMjEwMTc7JiMyMzYwMTsmIzIzNTU4OyYjMzU4MTM7JiMzNTgzMTsmIzI3NzE0OyYjMjAxMzI7JiMzMjQ3MzsmIzIxNTE4OyYjMzI0ODc7JiMzMjc3MzsmIzIxNDM1OyYjMjI3ODg7JiMyOTcwMjs8L3RleHQ+PC9nPjwhLS1TUkM9W2xMSDFLemYwNUJ4ZEwtcDhuS3VSVWFWQVpFY2Jmbm13bFRhVThncDBEOURDYXpaTWlTdkcxbm9tNWNaSEpiNzdnTE1LZ0pDMlJRUzJHVmpkaWZqbXVZenFtdjhPSUgxRmxNdHNWVV94bGxWb3packJRbzg0cmt5Wkd6VnhiSWpTRnFJemFnYk55NDdNUkViZXh1Z0RCaVFZU0xjS2JPV1lobVdZOW94M2NCZ1lmYUgxSWFPVjNZQ25YWDRNTEtxSHVYZzhZdUFnV1lVMmIxMlgwZ0JIY3U1T1o5NWEzRzhiZEtuZjRPMHRCd3RNOEpoRWNrS0RBdkRaRDNhOWpISjlNc0tlcHNEUFlZakdXb0UtVUs4T244RG81eVBiblVNUDN5eGNuaEQ1a0VIRnZlQUpVSTJlTzZPQzZtdzdQcG9hUXJ2UTRkRU9uM0lLajBkWXZ6em5faTh5UW1ud1VPZVJ6MGhNd01VaHkxQ3lidm1zZmFSYkZ2Qlc2cnpGUGtfRC1QMVM0OThaSTQ5QVpGZG1WMTJVZnRYN0ZBbjJaT1RsREozaFlEWjJHcFJkbVdWWFJPWEJHMHpGdVFqYmc2ZzRWRUJPaDM4MGlFd05IMVg3dnBhSWtFeTZIY2daSnZsRzZxcTZINEYwTE5tamZuRjB2c1lEa1Z1eEc1dm9vMjhIWm1kMk94R182RXNDcVZ4WFplQXZjcU16N05FeHBhMGYwTW9FSUttdEQwYU5CY0ptVms2UVFCamtUOGpNdW1BdHJrYWJlVXZkVGlhekJqUEd3VVcxX2pnV3VsSnZ1MnlUXzZsNzhwbFh3UW1Fa3BETVNNSk8xcl9QdXlhMWhjeS1OUmx5dVBjd0dVUzdQaDR2dzZzWGhIbWdkbmRUM05CSmh6ZW1Sc002VmNILVVwX0RFWlZELWVHN0VvTkJ0TDdsTURSVmhHV216TERTLTdpaFpCaVJaQi02Q1FEYm0ydWlvY0JZMUhqcHZpQXBwdTBpMEo0amxHSVhmMEFYSDlQNFZXX2ZOTlQ5c3N0al80N2JKLUhpajNUOUp5YXpwZlI2OVg4ckVsUHFiQ2Y2enZXc3RKS2ZpbWlYMUg5dkx6ck5GcWtpWkxUeGd5VUpUTm9tWnFlREd3ekgydVdpb2k5N2djblJwYlZmbE5xa2R6WVZwSFBnUmJZRDZpS0djd1BVSFVLWVFQTGpsd1hKRkVEaFVlMHR0WmtfX3l0UUZtMDBdLS0+PC9nPjwvc3ZnPg=="><h1 id="三、责任链模式的应用"><a href="#三、责任链模式的应用" class="headerlink" title="三、责任链模式的应用"></a>三、责任链模式的应用</h1><h2 id="3-1、责任链模式解决问题案例"><a href="#3-1、责任链模式解决问题案例" class="headerlink" title="3.1、责任链模式解决问题案例"></a>3.1、责任链模式解决问题案例</h2><p>:chestnut: 模拟某公司请假审批流程模块，流程如下：</p><blockquote></blockquote><figure class="highlight plaintext"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><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">graph LR</span><br><span class="line">    S((开始))</span><br><span class="line">    A(申请人)</span><br><span class="line">    B(主任)</span><br><span class="line">    C(部门经理)</span><br><span class="line">    D(人力资源部经理)</span><br><span class="line">    E(主管领导)</span><br><span class="line">    F(申请人查阅)</span><br><span class="line">    G(知会考勤管理员)</span><br><span class="line">    P((结束))</span><br><span class="line"></span><br><span class="line">    S --&gt; A --普通员工--&gt; B --&gt; C</span><br><span class="line">    subgraph 请假天数大于3时需要人力资源部经理和主管领导额外审批</span><br><span class="line">    C --请假天数&lt;=3--&gt; F</span><br><span class="line">    C --请假天数&gt;3--&gt; D --&gt; E --&gt; F</span><br><span class="line">    end</span><br><span class="line">    F --&gt; G --&gt; P</span><br></pre></td></tr></tbody></table></figure><h3 id="3-1-1、定义抽象处理者（Handler）角色"><a href="#3-1-1、定义抽象处理者（Handler）角色" class="headerlink" title="3.1.1、定义抽象处理者（Handler）角色"></a>3.1.1、<strong>定义抽象处理者（Handler）角色</strong></h3><figure class="highlight java"><figcaption><span>LeaderHandler</span></figcaption><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@author</span> : sunys</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@version</span> : v1.0</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@createTime</span> : 2021/1/16 20:08</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@description</span> : 抽象领导处理者</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">abstract</span> <span class="keyword">class</span> <span class="title class_">LeaderHandler</span> {</span><br><span class="line">    <span class="keyword">private</span> LeaderHandler nextLeader;</span><br><span class="line">    <span class="keyword">protected</span> String name;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">public</span> <span class="title function_">LeaderHandler</span><span class="params">(String name)</span> {</span><br><span class="line">        <span class="built_in">this</span>.name = name;</span><br><span class="line">    }</span><br><span class="line"></span><br><span class="line">    <span class="keyword">public</span> LeaderHandler <span class="title function_">getNextLeader</span><span class="params">()</span> {</span><br><span class="line">        <span class="keyword">return</span> nextLeader;</span><br><span class="line">    }</span><br><span class="line"></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">setNextLeader</span><span class="params">(LeaderHandler nextLeader)</span> {</span><br><span class="line">        <span class="built_in">this</span>.nextLeader = nextLeader;</span><br><span class="line">    }</span><br><span class="line"></span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     * 抽象审批处理方法</span></span><br><span class="line"><span class="comment">     *</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@param</span> days 请假天数</span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">abstract</span> <span class="keyword">void</span> <span class="title function_">approval</span><span class="params">(Integer days)</span>;</span><br><span class="line">}</span><br></pre></td></tr></tbody></table></figure><h3 id="3-1-2、定义具体处理者（Concrete-Handler）角色"><a href="#3-1-2、定义具体处理者（Concrete-Handler）角色" class="headerlink" title="3.1.2、定义具体处理者（Concrete Handler）角色"></a>3.1.2、<strong>定义具体处理者（Concrete Handler）角色</strong></h3><figure class="highlight java"><figcaption><span>DirectorHandler</span></figcaption><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@author</span> : sunys</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@version</span> : v1.0</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@createTime</span> : 2021/1/16 20:18</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@description</span> : 主任具体处理者</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">DirectorHandler</span> <span class="keyword">extends</span> <span class="title class_">LeaderHandler</span> {</span><br><span class="line">    <span class="keyword">public</span> <span class="title function_">DirectorHandler</span><span class="params">(String name)</span> {</span><br><span class="line">        <span class="built_in">super</span>(name);</span><br><span class="line">    }</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">approval</span><span class="params">(Integer days)</span> {</span><br><span class="line">        System.out.println(<span class="string">"---&gt;请假"</span> + days + <span class="string">"天，由"</span> + <span class="built_in">this</span>.name + <span class="string">"审批通过---&gt;"</span>);</span><br><span class="line">        <span class="type">LeaderHandler</span> <span class="variable">nextLeader</span> <span class="operator">=</span> <span class="built_in">super</span>.getNextLeader();</span><br><span class="line">        <span class="keyword">if</span> (<span class="literal">null</span> != nextLeader) {</span><br><span class="line">            nextLeader.approval(days);</span><br><span class="line">        }</span><br><span class="line">    }</span><br><span class="line">}</span><br></pre></td></tr></tbody></table></figure><figure class="highlight java"><figcaption><span>DepartmentManagerHandler</span></figcaption><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@author</span> : sunys</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@version</span> : v1.0</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@createTime</span> : 2021/1/16 20:29</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@description</span> : 部门经理具体处理者</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">DepartmentManagerHandler</span> <span class="keyword">extends</span> <span class="title class_">LeaderHandler</span> {</span><br><span class="line">    <span class="keyword">public</span> <span class="title function_">DepartmentManagerHandler</span><span class="params">(String name)</span> {</span><br><span class="line">        <span class="built_in">super</span>(name);</span><br><span class="line">    }</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">approval</span><span class="params">(Integer days)</span> {</span><br><span class="line">        System.out.println(<span class="string">"---&gt;请假"</span> + days + <span class="string">"天，由"</span> + <span class="built_in">this</span>.name + <span class="string">"审批通过---&gt;"</span>);</span><br><span class="line">        <span class="type">LeaderHandler</span> <span class="variable">nextLeader</span> <span class="operator">=</span> <span class="built_in">super</span>.getNextLeader();</span><br><span class="line">        <span class="keyword">if</span> (<span class="literal">null</span> != nextLeader) {</span><br><span class="line">            nextLeader.approval(days);</span><br><span class="line">        }</span><br><span class="line">    }</span><br><span class="line">}</span><br></pre></td></tr></tbody></table></figure><figure class="highlight java"><figcaption><span>ResourcesManagerHandler</span></figcaption><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@author</span> : sunys</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@version</span> : v1.0</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@createTime</span> : 2021/1/16 20:30</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@description</span> : 人力资源部经理具体处理者</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">ResourcesManagerHandler</span> <span class="keyword">extends</span> <span class="title class_">LeaderHandler</span> {</span><br><span class="line">    <span class="keyword">public</span> <span class="title function_">ResourcesManagerHandler</span><span class="params">(String name)</span> {</span><br><span class="line">        <span class="built_in">super</span>(name);</span><br><span class="line">    }</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">approval</span><span class="params">(Integer days)</span> {</span><br><span class="line">        <span class="keyword">if</span> (days &gt; <span class="number">3</span>) {</span><br><span class="line">            System.out.println(<span class="string">"---&gt;请假"</span> + days + <span class="string">"天，超过3天，由"</span> + <span class="built_in">this</span>.name + <span class="string">"审批通过---&gt;"</span>);</span><br><span class="line">        }</span><br><span class="line">        <span class="type">LeaderHandler</span> <span class="variable">nextLeader</span> <span class="operator">=</span> <span class="built_in">super</span>.getNextLeader();</span><br><span class="line">        <span class="keyword">if</span> (<span class="literal">null</span> != nextLeader) {</span><br><span class="line">            nextLeader.approval(days);</span><br><span class="line">        }</span><br><span class="line">    }</span><br><span class="line">}</span><br></pre></td></tr></tbody></table></figure><figure class="highlight java"><figcaption><span>CompetentLeaderHandler</span></figcaption><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@author</span> : sunys</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@version</span> : v1.0</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@createTime</span> : 2021/1/16 20:39</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@description</span> :</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">CompetentLeaderHandler</span> <span class="keyword">extends</span> <span class="title class_">LeaderHandler</span> {</span><br><span class="line">    <span class="keyword">public</span> <span class="title function_">CompetentLeaderHandler</span><span class="params">(String name)</span> {</span><br><span class="line">        <span class="built_in">super</span>(name);</span><br><span class="line">    }</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">approval</span><span class="params">(Integer days)</span> {</span><br><span class="line">        <span class="keyword">if</span> (days &gt; <span class="number">3</span>) {</span><br><span class="line">            System.out.println(<span class="string">"---&gt;请假"</span> + days + <span class="string">"天，超过3天，由"</span> + <span class="built_in">this</span>.name + <span class="string">"审批通过---&gt;"</span>);</span><br><span class="line">        }</span><br><span class="line">        <span class="type">LeaderHandler</span> <span class="variable">nextLeader</span> <span class="operator">=</span> <span class="built_in">super</span>.getNextLeader();</span><br><span class="line">        <span class="keyword">if</span> (<span class="literal">null</span> != nextLeader) {</span><br><span class="line">            nextLeader.approval(days);</span><br><span class="line">        }</span><br><span class="line">    }</span><br><span class="line">}</span><br></pre></td></tr></tbody></table></figure><h3 id="3-1-3、定义客户端（Client）角色"><a href="#3-1-3、定义客户端（Client）角色" class="headerlink" title="3.1.3、定义客户端（Client）角色"></a>3.1.3、<strong>定义客户端（Client）角色</strong></h3><figure class="highlight java"><figcaption><span>Client</span></figcaption><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@author</span> : sunys</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@version</span> : v1.0</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@createTime</span> : 2021/1/16 20:05</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@description</span> :</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">Client</span> {</span><br><span class="line"></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title function_">approvalTest</span><span class="params">(Integer days)</span> {</span><br><span class="line">        <span class="comment">//组装责任链</span></span><br><span class="line">        <span class="type">DirectorHandler</span> <span class="variable">directorHandler</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">DirectorHandler</span>(<span class="string">"部门侯主任"</span>);</span><br><span class="line">        <span class="type">DepartmentManagerHandler</span> <span class="variable">departmentManagerHandler</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">DepartmentManagerHandler</span>(<span class="string">"部门吴经理"</span>);</span><br><span class="line">        <span class="type">ResourcesManagerHandler</span> <span class="variable">resourcesManagerHandler</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">ResourcesManagerHandler</span>(<span class="string">"人力资源王经理"</span>);</span><br><span class="line">        <span class="type">CompetentLeaderHandler</span> <span class="variable">competentLeaderHandler</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">CompetentLeaderHandler</span>(<span class="string">"主管领导老王"</span>);</span><br><span class="line"></span><br><span class="line">        directorHandler.setNextLeader(departmentManagerHandler);</span><br><span class="line">        departmentManagerHandler.setNextLeader(resourcesManagerHandler);</span><br><span class="line">        resourcesManagerHandler.setNextLeader(competentLeaderHandler);</span><br><span class="line">        <span class="comment">//提交请求</span></span><br><span class="line">        directorHandler.approval(days);</span><br><span class="line">        System.out.println(<span class="string">"流程审批结束"</span>);</span><br><span class="line">    }</span><br><span class="line">}</span><br></pre></td></tr></tbody></table></figure><h3 id="3-1-4、单元测试"><a href="#3-1-4、单元测试" class="headerlink" title="3.1.4、单元测试"></a>3.1.4、<strong>单元测试</strong></h3><figure class="highlight java"><figcaption><span>ClientTest</span></figcaption><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * The type Client test.</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="keyword">class</span> <span class="title class_">ClientTest</span> {</span><br><span class="line"></span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     * Client test.</span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    <span class="meta">@Test</span></span><br><span class="line">    <span class="keyword">void</span> <span class="title function_">clientTest</span><span class="params">()</span> {</span><br><span class="line">        System.out.println(<span class="string">"Client call director"</span>);</span><br><span class="line">        System.out.println(<span class="string">"===================================="</span>);</span><br><span class="line">        <span class="comment">//请假小于3天</span></span><br><span class="line">        Client.approvalTest(<span class="number">2</span>);</span><br><span class="line">        System.out.println(<span class="string">"===================================="</span>);</span><br><span class="line">        <span class="comment">//请假大于3天</span></span><br><span class="line">        Client.approvalTest(<span class="number">5</span>);</span><br><span class="line">        System.out.println(<span class="string">"===================================="</span>);</span><br><span class="line">    }</span><br><span class="line">}</span><br></pre></td></tr></tbody></table></figure><p>测试结果：<br><img src="https://oss.syshlang.com/blog/image/c9ff299157df4cffa55bbb99a74f33f6.png" alt="单元测试结果"></p><h2 id="3-2、责任链模式的应用"><a href="#3-2、责任链模式的应用" class="headerlink" title="3.2、责任链模式的应用"></a>3.2、责任链模式的应用</h2><p><indent>责任链模式在框架的开发中使用及其广泛，比如过滤器 (Interceptor) 和拦截器 (Interceptor)，比如 Spring Interceptor 和 Servlet Filter</indent></p><h3 id="3-2-1、-Filter-和-Interceptor"><a href="#3-2-1、-Filter-和-Interceptor" class="headerlink" title="3.2.1、 Filter 和 Interceptor"></a>3.2.1、 Filter 和 Interceptor</h3><p><indent>责任链模式在框架的开发中使用及其广泛，比如 Spring Interceptor 拦截器和 Servlet Filter 过滤器。<br><img src="https://oss.syshlang.com/blog/image/e5bf4e8e4e0148aea5adb0ea671d7afd.png" alt="Filter"><br>Filter 抽象处理者角色，ApplicationFilterChain、VirtualFilterChain 等是责任链模式的具体实现类。</indent></p><figure class="highlight java"><figcaption><span>FilterChain</span></figcaption><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">interface</span> <span class="title class_">FilterChain</span> {</span><br><span class="line">    <span class="keyword">void</span> <span class="title function_">doFilter</span><span class="params">(ServletRequest var1, ServletResponse var2)</span> <span class="keyword">throws</span> IOException, ServletException;</span><br><span class="line">}</span><br></pre></td></tr></tbody></table></figure><figure class="highlight java"><figcaption><span>ApplicationFilterChain</span></figcaption><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><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">final</span> <span class="keyword">class</span> <span class="title class_">ApplicationFilterChain</span> <span class="keyword">implements</span> <span class="title class_">FilterChain</span> {</span><br><span class="line">    ...<span class="comment">//源码省略</span></span><br><span class="line"></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">doFilter</span><span class="params">(ServletRequest request, ServletResponse response)</span> <span class="keyword">throws</span> IOException, ServletException {</span><br><span class="line">        <span class="keyword">if</span> (Globals.IS_SECURITY_ENABLED) {</span><br><span class="line">            <span class="type">ServletRequest</span> <span class="variable">req</span> <span class="operator">=</span> request;</span><br><span class="line">            <span class="type">ServletResponse</span> <span class="variable">res</span> <span class="operator">=</span> response;</span><br><span class="line"></span><br><span class="line">            <span class="keyword">try</span> {</span><br><span class="line">                AccessController.doPrivileged(() -&gt; {</span><br><span class="line">                    <span class="built_in">this</span>.internalDoFilter(req, res);</span><br><span class="line">                    <span class="keyword">return</span> <span class="literal">null</span>;</span><br><span class="line">                });</span><br><span class="line">            } <span class="keyword">catch</span> (PrivilegedActionException var7) {</span><br><span class="line">                <span class="type">Exception</span> <span class="variable">e</span> <span class="operator">=</span> var7.getException();</span><br><span class="line">                <span class="keyword">if</span> (e <span class="keyword">instanceof</span> ServletException) {</span><br><span class="line">                    <span class="keyword">throw</span> (ServletException)e;</span><br><span class="line">                }</span><br><span class="line"></span><br><span class="line">                <span class="keyword">if</span> (e <span class="keyword">instanceof</span> IOException) {</span><br><span class="line">                    <span class="keyword">throw</span> (IOException)e;</span><br><span class="line">                }</span><br><span class="line"></span><br><span class="line">                <span class="keyword">if</span> (e <span class="keyword">instanceof</span> RuntimeException) {</span><br><span class="line">                    <span class="keyword">throw</span> (RuntimeException)e;</span><br><span class="line">                }</span><br><span class="line"></span><br><span class="line">                <span class="keyword">throw</span> <span class="keyword">new</span> <span class="title class_">ServletException</span>(e.getMessage(), e);</span><br><span class="line">            }</span><br><span class="line">        } <span class="keyword">else</span> {</span><br><span class="line">            <span class="built_in">this</span>.internalDoFilter(request, response);</span><br><span class="line">        }</span><br><span class="line"></span><br><span class="line">    }</span><br><span class="line">    ...<span class="comment">//源码省略</span></span><br><span class="line">}</span><br></pre></td></tr></tbody></table></figure><p><img src="https://oss.syshlang.com/blog/image/84cdcff63435437fa1f8a91fbc192876.png" alt="Filter"><br>HandlerInterceptor 抽象处理者角色，LocaleChangeInterceptor 等是责任链模式的具体实现类。</p><figure class="highlight java"><figcaption><span>HandlerInterceptor</span></figcaption><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><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">interface</span> <span class="title class_">HandlerInterceptor</span> {</span><br><span class="line">    <span class="type">boolean</span> <span class="title function_">preHandle</span><span class="params">(HttpServletRequest var1, HttpServletResponse var2, Object var3)</span> <span class="keyword">throws</span> Exception;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">void</span> <span class="title function_">postHandle</span><span class="params">(HttpServletRequest var1, HttpServletResponse var2, Object var3, ModelAndView var4)</span> <span class="keyword">throws</span> Exception;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">void</span> <span class="title function_">afterCompletion</span><span class="params">(HttpServletRequest var1, HttpServletResponse var2, Object var3, Exception var4)</span> <span class="keyword">throws</span> Exception;</span><br><span class="line">}</span><br></pre></td></tr></tbody></table></figure><figure class="highlight java"><figcaption><span>LocaleChangeInterceptor</span></figcaption><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">LocaleChangeInterceptor</span> <span class="keyword">extends</span> <span class="title class_">HandlerInterceptorAdapter</span> {</span><br><span class="line">    ...<span class="comment">//源码省略</span></span><br><span class="line"></span><br><span class="line">    <span class="keyword">public</span> <span class="type">boolean</span> <span class="title function_">preHandle</span><span class="params">(HttpServletRequest request, HttpServletResponse response, Object handler)</span> <span class="keyword">throws</span> ServletException {</span><br><span class="line">        <span class="type">String</span> <span class="variable">newLocale</span> <span class="operator">=</span> request.getParameter(<span class="built_in">this</span>.getParamName());</span><br><span class="line">        <span class="keyword">if</span> (newLocale != <span class="literal">null</span> &amp;&amp; <span class="built_in">this</span>.checkHttpMethod(request.getMethod())) {</span><br><span class="line">            <span class="type">LocaleResolver</span> <span class="variable">localeResolver</span> <span class="operator">=</span> RequestContextUtils.getLocaleResolver(request);</span><br><span class="line">            <span class="keyword">if</span> (localeResolver == <span class="literal">null</span>) {</span><br><span class="line">                <span class="keyword">throw</span> <span class="keyword">new</span> <span class="title class_">IllegalStateException</span>(<span class="string">"No LocaleResolver found: not in a DispatcherServlet request?"</span>);</span><br><span class="line">            }</span><br><span class="line"></span><br><span class="line">            <span class="keyword">try</span> {</span><br><span class="line">                localeResolver.setLocale(request, response, <span class="built_in">this</span>.parseLocaleValue(newLocale));</span><br><span class="line">            } <span class="keyword">catch</span> (IllegalArgumentException var7) {</span><br><span class="line">                <span class="keyword">if</span> (!<span class="built_in">this</span>.isIgnoreInvalidLocale()) {</span><br><span class="line">                    <span class="keyword">throw</span> var7;</span><br><span class="line">                }</span><br><span class="line"></span><br><span class="line">                <span class="built_in">this</span>.logger.debug(<span class="string">"Ignoring invalid locale value ["</span> + newLocale + <span class="string">"]: "</span> + var7.getMessage());</span><br><span class="line">            }</span><br><span class="line">        }</span><br><span class="line"></span><br><span class="line">        <span class="keyword">return</span> <span class="literal">true</span>;</span><br><span class="line">    }</span><br><span class="line"></span><br><span class="line">    ...<span class="comment">//源码省略</span></span><br><span class="line">}</span><br></pre></td></tr></tbody></table></figure><h3 id="3-2-1、-Activiti工作流"><a href="#3-2-1、-Activiti工作流" class="headerlink" title="3.2.1、 Activiti工作流"></a>3.2.1、 Activiti 工作流</h3><p><indent>Activit 整体上采用命令模式，同时搭配其对应的责任链来完成，它的各操作方法其底层基本都是以命令模式来实现，从而实现代码功能解耦，将流程引擎大部分涉及到客户端的需求业务交给外部以具体命令实现类的实现。</indent></p><ul><li>Command 命令接口，所有的具体命令都需要实现该类，最终业务就是执行该类的 execute 方法。</li><li>CommandContext 命令上下文，为具体命令的执行提供上下文支撑。</li></ul><h1 id="四、总结"><a href="#四、总结" class="headerlink" title="四、总结"></a>四、总结</h1><div class="note info"><p>这种模式的特点：</p><ul><li>这种模式下，将请求的发送者和接收者进行了完美的解耦，调用着无须关心请求的传递过程和请求的处理细节。</li><li>增强了系统的可扩展性，可以根据需要增加或减少请求处理类，满足设计原则中的<a href="/posts/1376ad9f/" title="设计模式七大原则之开闭原则 (Open Close Principle)">开闭原则 (Open Close Principle)</a>。</li><li>这种模式下，每个接收者只需要处理自己负责的业务，不用处理的传递给下一个接受者完成，各个接收者责任范围分工明确，符合类的<a href="/posts/934a08a4/" title="设计模式七大原则之单一职责原则 (Single Responsibility Principle)">单一职责原则 (Single Responsibility Principle)</a>。</li><li>使用这种模式，特别是在链比较长的时候，性能会受到影响，因此需要尽可能控制链中最大节点数量，避免出现超长链无意识地破坏系统性能。</li></ul></div><p><font color="black" size="5" face="华文行楷">附：本次演示的项目地址</font><br><span class="exturl" data-url="aHR0cHM6Ly9naXRodWIuY29tL3N5c2hsYW5nL2phdmEtZGVzaWduLXBhdHRlcm5z">https://github.com/syshlang/java-design-patterns<i class="fa fa-external-link-alt"></i></span> <iframe src="https://ghbtns.com/github-btn.html?user=syshlang&amp;repo=java-design-patterns&amp;type=star&amp;count=true&amp;size=large" width="160px" height="30px" frameborder="0" loading="lazy" allowfullscreen=""></iframe></p><link rel="stylesheet" href="https://fastly.jsdelivr.net/npm/markmap-toolbar@0.18.10/dist/style.css"><script src="https://fastly.jsdelivr.net/npm/d3@7"></script><script src="https://fastly.jsdelivr.net/npm/markmap-view@0.18.10"></script><script src="https://fastly.jsdelivr.net/npm/markmap-toolbar@0.18.10"></script><link rel="stylesheet" href="/css/markmap.css"><script src="/js/markmap.js"></script>]]></content>
    
    
    <summary type="html">&lt;img src=&quot;https://refactoringguru.cn/images/patterns/cards/chain-of-responsibility-mini-2x.png&quot; class=&quot;&quot; width=&quot;600&quot; height=&quot;300&quot; title=&quot;责任链模式&quot; alt=&quot;责任链模式&quot;&gt;</summary>
    
    
    
    <category term="CODING" scheme="https://syshlang.com/categories/CODING/"/>
    
    <category term="后端" scheme="https://syshlang.com/categories/CODING/%E5%90%8E%E7%AB%AF/"/>
    
    <category term="设计模式" scheme="https://syshlang.com/categories/CODING/%E8%AE%BE%E8%AE%A1%E6%A8%A1%E5%BC%8F/"/>
    
    <category term="java" scheme="https://syshlang.com/categories/CODING/%E5%90%8E%E7%AB%AF/java/"/>
    
    
    <category term="JAVA" scheme="https://syshlang.com/tags/JAVA/"/>
    
    <category term="design" scheme="https://syshlang.com/tags/design/"/>
    
    <category term="patterns" scheme="https://syshlang.com/tags/patterns/"/>
    
  </entry>
  
  <entry>
    <title>2020 年总结</title>
    <link href="https://syshlang.com/posts/b95ce2a8/"/>
    <id>https://syshlang.com/posts/b95ce2a8/</id>
    <published>2020-12-31T14:18:54.000Z</published>
    <updated>2025-05-22T15:08:23.923Z</updated>
    
    <content type="html"><![CDATA[<div class="hbe hbe-container" id="hexo-blog-encrypt" data-wpm="密码错误，请重新输入......" data-whm="无权限修改！">  <script id="hbeData" type="hbeData" data-hmacdigest="b514b36a4641dd0c95b63427471d4c80e7335f959d6c807f94c598b3d3b506cf">2644238dac8f3f89c81ad046e3c1b96c4a715f7dd336812f6ab999de2a1274296e4c2eb1f8c9eeffe2724b7dbaaf8312841267cc1bc0f198e9a33188958c8b3088916ec850bbacc91a673e7b82bd31c5e2bbe2d5c45f18be6dd96398b850378fc6649a06b009277cce1ae4f4b33badcf307e46aaea16c8a735f34c756190791e26402b0f68a941f6f934539c423a339341c68a87a5be546efc9b561904f8bdf51e3fec5de26f9bf4e7a4dbe6e636ab3c44a02fa441034be74c253ae0b36e6034fd40a8e9372e168441af98587fa664e4842cf2204a423980b868edafbd1050d30e66b9411b51db118008c286d4e1c6150a31f522f3d3ac8b542de6ac3069ebe0dbeb35d4cf6b9506f2958d5139fd52d3f3b44e0e9bf4cdb2e00035ad577ba0899544df4c23280480bad014b20fd6e099a47c28102856364f9b4f02c842da4c49628b84aa16d20d5ea8239109bf0fbab6912e971752202c18abd9d00745b286ab3bbb0f67a4bfa9056e8fcfc61c3a77cd8a45ac1d9228af8946efed03ab064031b8268c18323fdbfe139c029834f75a407511dd38cd6eb71957eb47f5978112050cf9b562c94c23ac6d757946083ef90575c75b52374b3585e075b0097480696e94df5bf906c8d2ac9a7cea1837cb9152ff921f7874dfad9e3359df8c4cb89c877d366d880d8a1497735a48b99a7ffc109382c72d4e1e7047b11ca7b7f9bac00bc85dfd9dce26ebd1cc7a02a623c4276b0cdef1644c4f0d5b23130ed2c353a184e232e299d19122f753e73f4bd7bba0e499f270b11420ef6945d535687d7aeca2c70a144012075d3da3c4d09777f0cfabc4e2454e50ec1384a1df167e9faba8a45af4603547c0eaf144dcc24c261bf61d21640de88d5c94c2928f6495935c3947c33bb63a118355c321f9ef12ddac23acac3ff9f04529bb90195c8beea534b0bd173e463064686953b0fcc0c7f4af952a26c505903935039bc8446d83ad9cd241</script>  <div class="hbe hbe-content">    <div class="hbe hbe-input hbe-input-xray">      <input class="hbe hbe-input-field hbe-input-field-xray" type="password" id="hbePass">      <label class="hbe hbe-input-label hbe-input-label-xray" for="hbePass">        <span class="hbe hbe-input-label-content hbe-input-label-content-xray">请输入密码......</span>      </label>      <svg class="hbe hbe-graphic hbe-graphic-xray" width="300%" height="100%" viewBox="0 0 1200 60" preserveAspectRatio="none">        <path d="M0,56.5c0,0,298.666,0,399.333,0C448.336,56.5,513.994,46,597,46c77.327,0,135,10.5,200.999,10.5c95.996,0,402.001,0,402.001,0"></path>        <path d="M0,2.5c0,0,298.666,0,399.333,0C448.336,2.5,513.994,13,597,13c77.327,0,135-10.5,200.999-10.5c95.996,0,402.001,0,402.001,0"></path>      </svg>    </div>  </div></div><script data-pjax src="/lib/hbe.js"></script><link href="/css/hbe.style.css" rel="stylesheet" type="text/css">]]></content>
    
    
    <summary type="html">访问受限，请输入密码！</summary>
    
    
    
    <category term="LIFE" scheme="https://syshlang.com/categories/LIFE/"/>
    
    <category term="essay" scheme="https://syshlang.com/categories/LIFE/essay/"/>
    
    
    <category term="随笔" scheme="https://syshlang.com/tags/%E9%9A%8F%E7%AC%94/"/>
    
  </entry>
  
  <entry>
    <title>设计模式之结构型模式中的桥接模式（Bridge Pattern）</title>
    <link href="https://syshlang.com/posts/62f7f6a4/"/>
    <id>https://syshlang.com/posts/62f7f6a4/</id>
    <published>2020-05-30T12:56:21.000Z</published>
    <updated>2025-12-01T14:31:48.911Z</updated>
    
    <content type="html"><![CDATA[<link rel="stylesheet" class="aplayer-secondary-style-marker" href="/assets/css/APlayer.min.css"><script src="/assets/js/APlayer.min.js" class="aplayer-secondary-script-marker"></script><script class="meting-secondary-script-marker" src="/assets/js/Meting.min.js"></script><p><img src="https://oss.syshlang.com/blog/image/bb3b0353289b4144ab694ab13027c6cb.png" alt="Bridge Pattern"></p><span id="more"></span><h1 id="一、基本介绍"><a href="#一、基本介绍" class="headerlink" title="一、基本介绍"></a>一、基本介绍</h1><p><indent>桥接模式（Bridge Pattern）是一种结构型设计模式，基于类的最小设计原则，通过使用封装、聚合及继承等方式让不同的类承担不同的职责，从而把抽象 (Abstraction) 和实现 (Implementation) 分离开放在不同的层次中，使得各部分保持独立更加利于扩展。一句话来说，就是在抽象与实现之间提供一个桥梁，降低二者耦合度，达到二者可以独立变化而不互相影响的目的。</indent></p><h1 id="二、从“类爆炸”说起"><a href="#二、从“类爆炸”说起" class="headerlink" title="二、从“类爆炸”说起"></a>二、从 “类爆炸” 说起</h1><p><indent> :chestnut: 假如，现在有这样一个业务场景，需要对不同颜色不同品牌的手机实现相应的功能，如打电话、发短信、上网等。<br><indent>如果使用传统的方式，可能是这样的，类图如下：<br><img src="https://oss.syshlang.com/blog/image/ff7fc3da4949477eada3323b76005e58.png" alt="传统方法对应的类图"></indent></indent></p><blockquote><p><indent>从以上的类图中，可以看出存在的问题，首先，这种方式会带来扩展性的问题，随着功能和手机品牌的增加，需要维护的类的数量也会增加，扩展性极差:confounded:，产生 “类爆炸”；其次，从类的最小设计原则考虑，当需要手机的颜色时，同时还需增加所有品牌的手机，这种设计违反了<a href="/posts/934a08a4/" title="设计模式七大原则之单一职责原则 (Single Responsibility Principle)">单一职责原则</a>。</indent></p></blockquote><h1 id="三、桥接模式"><a href="#三、桥接模式" class="headerlink" title="三、桥接模式"></a>三、桥接模式</h1><p><indent>那么，基于以上两点的考虑，借鉴单一职责原则的核心思想，尝试将对象解耦，将类的功能职责粒度分解细化，通过使用封装、聚合及继承等行为让不同的类承担不同的职责。</indent></p><h2 id="3-1-采用桥接模式解决问题"><a href="#3-1-采用桥接模式解决问题" class="headerlink" title="3.1 采用桥接模式解决问题"></a>3.1 采用桥接模式解决问题</h2><ul><li>定义手机品牌接口 </li></ul><figure class="highlight java"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * 手机品牌接口</span></span><br><span class="line"><span class="comment"> *</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@author</span> sunys</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">interface</span> <span class="title class_">PhoneBrand</span> {</span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     * Call.</span></span><br><span class="line"><span class="comment">     * 打电话</span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    <span class="keyword">void</span> <span class="title function_">call</span><span class="params">()</span>;</span><br><span class="line"></span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     * Send message.</span></span><br><span class="line"><span class="comment">     * 发短信</span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    <span class="keyword">void</span> <span class="title function_">sendMessage</span><span class="params">()</span>;</span><br><span class="line"></span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     * Go online.</span></span><br><span class="line"><span class="comment">     * 上网</span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    <span class="keyword">void</span> <span class="title function_">goOnline</span><span class="params">()</span>;</span><br><span class="line">}</span><br></pre></td></tr></tbody></table></figure><ul><li>不同品牌手机接口的接口实现 </li></ul><figure class="highlight java"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@author</span> sunys</span></span><br><span class="line"><span class="comment"> * HuaWei品牌接口实现</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">HuaWeiPhoneBrandImpl</span> <span class="keyword">implements</span> <span class="title class_">PhoneBrand</span> {</span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">call</span><span class="params">()</span> {</span><br><span class="line">        System.out.println(<span class="string">"Call use HuaWei phone."</span>);</span><br><span class="line">    }</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">sendMessage</span><span class="params">()</span> {</span><br><span class="line">        System.out.println(<span class="string">"SendMessage use HuaWei phone."</span>);</span><br><span class="line">    }</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">goOnline</span><span class="params">()</span> {</span><br><span class="line">        System.out.println(<span class="string">"GoOnline use HuaWei phone."</span>);</span><br><span class="line">    }</span><br><span class="line">}</span><br></pre></td></tr></tbody></table></figure><figure class="highlight java"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@author</span> sunys</span></span><br><span class="line"><span class="comment"> * Apple品牌接口实现</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">ApplePhoneBrandImpl</span> <span class="keyword">implements</span> <span class="title class_">PhoneBrand</span> {</span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">call</span><span class="params">()</span> {</span><br><span class="line">        System.out.println(<span class="string">"Call use Apple phone."</span>);</span><br><span class="line">    }</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">sendMessage</span><span class="params">()</span> {</span><br><span class="line">        System.out.println(<span class="string">"SendMessage use Apple phone."</span>);</span><br><span class="line">    }</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">goOnline</span><span class="params">()</span> {</span><br><span class="line">        System.out.println(<span class="string">"GoOnline use Apple phone."</span>);</span><br><span class="line">    }</span><br><span class="line">}</span><br></pre></td></tr></tbody></table></figure><figure class="highlight java"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@author</span> sunys</span></span><br><span class="line"><span class="comment"> * XiaoMi品牌接口实现</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">XiaoMiPhoneBrandImpl</span> <span class="keyword">implements</span> <span class="title class_">PhoneBrand</span> {</span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">call</span><span class="params">()</span> {</span><br><span class="line">        System.out.println(<span class="string">"Call use XiaoMi phone."</span>);</span><br><span class="line">    }</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">sendMessage</span><span class="params">()</span> {</span><br><span class="line">        System.out.println(<span class="string">"SendMessage use XiaoMi phone."</span>);</span><br><span class="line">    }</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">goOnline</span><span class="params">()</span> {</span><br><span class="line">        System.out.println(<span class="string">"GoOnline use XiaoMi phone."</span>);</span><br><span class="line">    }</span><br><span class="line">}</span><br></pre></td></tr></tbody></table></figure><ul><li>创建手机这个抽象类，即 “桥”</li></ul><figure class="highlight java"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@author</span> sunys</span></span><br><span class="line"><span class="comment"> * 手机抽象类 相当于“桥”</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">abstract</span> <span class="keyword">class</span> <span class="title class_">Phone</span> {</span><br><span class="line"></span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     * PhoneBrand 类聚合</span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    <span class="keyword">private</span> PhoneBrand phoneBrand;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">public</span> <span class="title function_">Phone</span><span class="params">(PhoneBrand phoneBrand)</span> {</span><br><span class="line">        <span class="built_in">super</span>();</span><br><span class="line">        <span class="built_in">this</span>.phoneBrand = phoneBrand;</span><br><span class="line">    }</span><br><span class="line"></span><br><span class="line">    <span class="keyword">protected</span> <span class="keyword">void</span> <span class="title function_">call</span><span class="params">()</span> {</span><br><span class="line">        <span class="built_in">this</span>.phoneBrand.call();</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> <span class="keyword">void</span> <span class="title function_">sendMessage</span><span class="params">()</span> {</span><br><span class="line">        <span class="built_in">this</span>.phoneBrand.sendMessage();</span><br><span class="line">    }</span><br><span class="line"></span><br><span class="line">    <span class="keyword">protected</span> <span class="keyword">void</span> <span class="title function_">goOnline</span><span class="params">()</span> {</span><br><span class="line">        <span class="built_in">this</span>.phoneBrand.goOnline();</span><br><span class="line">    }</span><br><span class="line">}</span><br><span class="line"></span><br></pre></td></tr></tbody></table></figure><ul><li>不同颜色的具体手机 </li></ul><figure class="highlight java"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * The type Phone black.</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@author</span> sunys</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">PhoneBlack</span> <span class="keyword">extends</span> <span class="title class_">Phone</span>{</span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     * Instantiates a new Phone black.</span></span><br><span class="line"><span class="comment">     *</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@param</span> phoneBrand the phone brand</span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    <span class="keyword">public</span> <span class="title function_">PhoneBlack</span><span class="params">(PhoneBrand phoneBrand)</span> {</span><br><span class="line">        <span class="built_in">super</span>(phoneBrand);</span><br><span class="line">    }</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">call</span><span class="params">()</span> {</span><br><span class="line">        <span class="built_in">super</span>.call();</span><br><span class="line">        System.out.println(<span class="string">"The phone of call is Black."</span>);</span><br><span class="line">    }</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">sendMessage</span><span class="params">()</span> {</span><br><span class="line">        <span class="built_in">super</span>.sendMessage();</span><br><span class="line">        System.out.println(<span class="string">"The phone of sendMessage is Black."</span>);</span><br><span class="line">    }</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">goOnline</span><span class="params">()</span> {</span><br><span class="line">        <span class="built_in">super</span>.goOnline();</span><br><span class="line">        System.out.println(<span class="string">"The phone of goOnline is Black."</span>);</span><br><span class="line">    }</span><br><span class="line">}</span><br></pre></td></tr></tbody></table></figure><figure class="highlight java"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * The type Phone red.</span></span><br><span class="line"><span class="comment"> *</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@author</span> sunys</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">PhoneRed</span> <span class="keyword">extends</span> <span class="title class_">Phone</span>{</span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     * Instantiates a new Phone red.</span></span><br><span class="line"><span class="comment">     *</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@param</span> phoneBrand the phone brand</span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    <span class="keyword">public</span> <span class="title function_">PhoneRed</span><span class="params">(PhoneBrand phoneBrand)</span> {</span><br><span class="line">        <span class="built_in">super</span>(phoneBrand);</span><br><span class="line">    }</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">call</span><span class="params">()</span> {</span><br><span class="line">        <span class="built_in">super</span>.call();</span><br><span class="line">        System.out.println(<span class="string">"The phone of call is Red."</span>);</span><br><span class="line">    }</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">sendMessage</span><span class="params">()</span> {</span><br><span class="line">        <span class="built_in">super</span>.sendMessage();</span><br><span class="line">        System.out.println(<span class="string">"The phone of sendMessage is Red."</span>);</span><br><span class="line">    }</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">goOnline</span><span class="params">()</span> {</span><br><span class="line">        <span class="built_in">super</span>.goOnline();</span><br><span class="line">        System.out.println(<span class="string">"The phone of goOnline is Red."</span>);</span><br><span class="line">    }</span><br><span class="line">}</span><br><span class="line"></span><br></pre></td></tr></tbody></table></figure><figure class="highlight java"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * The type Phone white.</span></span><br><span class="line"><span class="comment"> *</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@author</span> sunys</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">PhoneWhite</span> <span class="keyword">extends</span> <span class="title class_">Phone</span>{</span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     * Instantiates a new Phone white.</span></span><br><span class="line"><span class="comment">     *</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@param</span> phoneBrand the phone brand</span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    <span class="keyword">public</span> <span class="title function_">PhoneWhite</span><span class="params">(PhoneBrand phoneBrand)</span> {</span><br><span class="line">        <span class="built_in">super</span>(phoneBrand);</span><br><span class="line">    }</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">call</span><span class="params">()</span> {</span><br><span class="line">        <span class="built_in">super</span>.call();</span><br><span class="line">        System.out.println(<span class="string">"The phone of call is White."</span>);</span><br><span class="line">    }</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">sendMessage</span><span class="params">()</span> {</span><br><span class="line">        <span class="built_in">super</span>.sendMessage();</span><br><span class="line">        System.out.println(<span class="string">"The phone of sendMessage is White."</span>);</span><br><span class="line">    }</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">goOnline</span><span class="params">()</span> {</span><br><span class="line">        <span class="built_in">super</span>.goOnline();</span><br><span class="line">        System.out.println(<span class="string">"The phone of goOnline is White."</span>);</span><br><span class="line">    }</span><br><span class="line">}</span><br></pre></td></tr></tbody></table></figure><ul><li>客户端调用 </li></ul><figure class="highlight java"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * The type Client.</span></span><br><span class="line"><span class="comment"> *</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@author</span> sunys</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">Client</span> {</span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     * Bridge test.</span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title function_">bridgeTest</span><span class="params">()</span> {</span><br><span class="line">        <span class="type">PhoneRed</span> <span class="variable">phoneRed</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">PhoneRed</span>(<span class="keyword">new</span> <span class="title class_">HuaWeiPhoneBrandImpl</span>());</span><br><span class="line">        phoneRed.call();</span><br><span class="line">        phoneRed.sendMessage();</span><br><span class="line">        phoneRed.goOnline();</span><br><span class="line">        <span class="type">PhoneBlack</span> <span class="variable">phoneBlack</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">PhoneBlack</span>(<span class="keyword">new</span> <span class="title class_">HuaWeiPhoneBrandImpl</span>());</span><br><span class="line">        phoneBlack.call();</span><br><span class="line">        phoneBlack.sendMessage();</span><br><span class="line">        phoneBlack.goOnline();</span><br><span class="line">        <span class="type">PhoneWhite</span> <span class="variable">phoneWhite</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">PhoneWhite</span>(<span class="keyword">new</span> <span class="title class_">XiaoMiPhoneBrandImpl</span>());</span><br><span class="line">        phoneWhite.call();</span><br><span class="line">        phoneWhite.sendMessage();</span><br><span class="line">        phoneWhite.goOnline();</span><br><span class="line">    }</span><br><span class="line">}</span><br></pre></td></tr></tbody></table></figure><blockquote><p><indent>至此，解决问题的方案改进完毕，通过这种方式，如果要添加新品牌的手机，只需让其实现 PhoneBrand 类即可；如果需要增加手机的颜色，只需让其继承 Phone 类即可。看下类图，如下：<br><img src="https://oss.syshlang.com/blog/image/c10f4c288eaf4bb99028218aea2ca2f2.png" alt="桥接模式方案类图"><br><indent>在这个方案中，Phone 这个抽象类是所有具体手机的父类，也是 PhoneBrand 类聚合的对象，起到了关键的 “桥” 的作用。基于类的最小设计原则，通过使用封装、聚合及继承等行为让不同的类承担不同的职责，将抽象与实现分离开保证不同层次独立改变互不影响更，从而极大的提高了系统的灵活性，有利于扩展。</indent></indent></p></blockquote><h1 id="四、桥接模式在JDBC中的应用"><a href="#四、桥接模式在JDBC中的应用" class="headerlink" title="四、桥接模式在JDBC中的应用"></a>四、桥接模式在 JDBC 中的应用</h1><ul><li>首先，来看一段客户端调用 JDBC 连接数据库的过程 </li></ul><figure class="highlight java"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * The type Client.</span></span><br><span class="line"><span class="comment"> *</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@author</span> sunys</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">Client</span> {</span><br><span class="line"></span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     * Jdbc test.</span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title function_">jdbcTest</span><span class="params">()</span>{</span><br><span class="line">        <span class="keyword">try</span> {</span><br><span class="line">            <span class="comment">// 1. 注册驱动</span></span><br><span class="line">            Class.forName(<span class="string">"com.mysql.jdbc.Driver"</span>);</span><br><span class="line">            <span class="comment">//2. 创建一个连接对象</span></span><br><span class="line">            <span class="type">Connection</span> <span class="variable">conn</span> <span class="operator">=</span> DriverManager.getConnection(<span class="string">"url"</span>,<span class="string">"user"</span>,<span class="string">"password"</span>);</span><br><span class="line">            <span class="comment">//3. 创建一个sql语句的发送命令对象</span></span><br><span class="line">            <span class="type">Statement</span> <span class="variable">stmt</span> <span class="operator">=</span> conn.createStatement();</span><br><span class="line">            <span class="comment">// 4. 执行sql,拿到查询的结果集对象</span></span><br><span class="line">            <span class="type">ResultSet</span> <span class="variable">rs</span> <span class="operator">=</span> stmt.executeQuery(<span class="string">"s"</span>);</span><br><span class="line">            <span class="comment">//5. 输出结果集的数据</span></span><br><span class="line">            <span class="keyword">while</span>(rs.next()){</span><br><span class="line">                System.out.println(rs);</span><br><span class="line">            }</span><br><span class="line">            <span class="comment">//6. 关闭连接，命令对象以及结果集。</span></span><br><span class="line">            rs.close();</span><br><span class="line">            stmt.close();</span><br><span class="line">            conn.close();</span><br><span class="line">        } <span class="keyword">catch</span> (ClassNotFoundException e) {</span><br><span class="line">            e.printStackTrace();</span><br><span class="line">        } <span class="keyword">catch</span> (SQLException throwables) {</span><br><span class="line">            throwables.printStackTrace();</span><br><span class="line">        }</span><br><span class="line">    }</span><br><span class="line">}</span><br></pre></td></tr></tbody></table></figure><ul><li>然后，看下相关类的 UML 类图<br><img src="https://oss.syshlang.com/blog/image/c63f8ca2c3564bd4bb48ea37cd73170d.png" alt="桥接模式在JDBC中的应用类图"></li></ul><blockquote><p><indent>从 UML 类图可以看出，com.mysql.jdbc.Driver 类是 java.sql.Driver 接口的实现类，注册驱动时，会执行 com.mysql.jdbc.Driver 类的静态块，该静态代码块中调用 java.sql.DriverManager#registerDriver (java.sql.Driver) 方法，将 Driver 对象注册到 DriverManager 中，DriverManager 就相当于是 “桥”; 调用 java.sql.DriverManager#getConnection (java.lang.String, java.lang.String, java.lang.String) 创建一个连接对象时，会根据驱动的类型调用不同类型数据库（mysql、oracle 等）实现的 Driver 的 connect () 方法，最终获得连接对象。</indent></p></blockquote><p><font color="black" size="5" face="华文行楷">附：本次演示的项目地址</font><br><span class="exturl" data-url="aHR0cHM6Ly9naXRodWIuY29tL3N5c2hsYW5nL2phdmEtZGVzaWduLXBhdHRlcm5z">https://github.com/syshlang/java-design-patterns<i class="fa fa-external-link-alt"></i></span> <iframe src="https://ghbtns.com/github-btn.html?user=syshlang&amp;repo=java-design-patterns&amp;type=star&amp;count=true&amp;size=large" width="160px" height="30px" frameborder="0" loading="lazy" allowfullscreen=""></iframe></p><link rel="stylesheet" href="https://fastly.jsdelivr.net/npm/markmap-toolbar@0.18.10/dist/style.css"><script src="https://fastly.jsdelivr.net/npm/d3@7"></script><script src="https://fastly.jsdelivr.net/npm/markmap-view@0.18.10"></script><script src="https://fastly.jsdelivr.net/npm/markmap-toolbar@0.18.10"></script><link rel="stylesheet" href="/css/markmap.css"><script src="/js/markmap.js"></script>]]></content>
    
    
    <summary type="html">&lt;p&gt;&lt;img src=&quot;https://oss.syshlang.com/blog/image/bb3b0353289b4144ab694ab13027c6cb.png&quot; alt=&quot;Bridge Pattern&quot;&gt;&lt;/p&gt;</summary>
    
    
    
    <category term="CODING" scheme="https://syshlang.com/categories/CODING/"/>
    
    <category term="后端" scheme="https://syshlang.com/categories/CODING/%E5%90%8E%E7%AB%AF/"/>
    
    <category term="设计模式" scheme="https://syshlang.com/categories/CODING/%E8%AE%BE%E8%AE%A1%E6%A8%A1%E5%BC%8F/"/>
    
    <category term="java" scheme="https://syshlang.com/categories/CODING/%E5%90%8E%E7%AB%AF/java/"/>
    
    
    <category term="JAVA" scheme="https://syshlang.com/tags/JAVA/"/>
    
    <category term="design" scheme="https://syshlang.com/tags/design/"/>
    
    <category term="patterns" scheme="https://syshlang.com/tags/patterns/"/>
    
  </entry>
  
  <entry>
    <title>基于 TrueLicense 实现 web 应用的 License 验证</title>
    <link href="https://syshlang.com/posts/45cf44f/"/>
    <id>https://syshlang.com/posts/45cf44f/</id>
    <published>2020-05-05T03:29:00.000Z</published>
    <updated>2025-12-01T14:31:49.928Z</updated>
    
    <content type="html"><![CDATA[<link rel="stylesheet" class="aplayer-secondary-style-marker" href="/assets/css/APlayer.min.css"><script src="/assets/js/APlayer.min.js" class="aplayer-secondary-script-marker"></script><script class="meting-secondary-script-marker" src="/assets/js/Meting.min.js"></script><p><indent>近期开发的 web 应用交付海外客户试用时，领导要求需要限制客户的试用期限，于是考虑使用 license 进行授权。查阅相关资料，了解到一个开源的证书管理引擎 ——TrueLicense，通过 TrueLicense 可以实现授权验证功能，用于 license 的生成和有效性的验证。</indent></p><span id="more"></span><indent># 一、License 授权和验证的原理&gt; - 首先，需要生成密钥对，生成方法有很多，本次项目使用的是 JDK 提供的 KeyTool 工具；&gt; - 在服务端，也就是授权者通过私钥（授权者保留，不能泄露）对包含授权信息（如起始日期、截止日期，服务器硬件的 MAC 地址等）的 license 进行数字签名；&gt; - 在客户端，也就是软件的使用方，通过公钥（一般放在验证的代码中使用）验证 license 是否符合使用条件。<h1 id="二、实现步骤"><a href="#二、实现步骤" class="headerlink" title="二、实现步骤"></a>二、实现步骤</h1><h2 id="1-生成密钥对"><a href="#1-生成密钥对" class="headerlink" title="1.生成密钥对"></a>1. 生成密钥对</h2><h3 id="1-1-生成私钥库"><a href="#1-1-生成私钥库" class="headerlink" title="1.1.生成私钥库"></a>1.1. 生成私钥库</h3><p><indent>安装 jdk，并配置环境变量，然后使用 KeyTool 工具生成密钥对，命令如下：</indent></p><figure class="highlight shell"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">keytool -genkeypair -keysize 1024 -validity 3650 -alias SYSHLANG -keystore privateKeys.keystore -storepass 12345678A -keypass 12345678A -dname "CN=syshlang, OU=syshlang, O=syshlang, L=GZ, ST=GD, C=CN"</span><br></pre></td></tr></tbody></table></figure><blockquote><p><strong>参数说明：</strong></p><ul><li>keysize 密钥长度</li><li> validity 私钥的有效期（单位：天）</li><li>alias 私钥别称</li><li> keystore 指定私钥库文件的名称 (生成在当前目录)</li><li>storepass 指定私钥库的密码 (keystore 文件存储密码)</li><li>keypass  指定别名条目的密码 (私钥加解密密码)</li><li>dname 证书个人信息<ul><li> CN 为你的姓名</li><li> OU 为你的组织单位名称</li><li> O 为你的组织名称</li><li> L 为你所在的城市名称</li><li> ST 为你所在的省份名称</li><li> C 为你的国家名称</li></ul></li></ul></blockquote><p><img src="https://oss.syshlang.com/blog/image/fde428b606bf4d589937ac9bd9b3dda1.png" alt="genkeypair"></p><h3 id="1-2-导出公钥"><a href="#1-2-导出公钥" class="headerlink" title="1.2.导出公钥"></a>1.2. 导出公钥</h3><p><indent>这一步主要是把私钥库内的公匙导出到一个文件中，命令如下：</indent></p><figure class="highlight shell"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">keytool -exportcert -alias SYSHLANG -keystore privateKeys.keystore -storepass 12345678A -file certfile.cer</span><br></pre></td></tr></tbody></table></figure><blockquote><p><strong>参数说明：</strong></p><ul><li>alias 私钥别称</li><li> keystore  指定私钥库文件的名称 (如果没有带路径，在当前目录查找)</li><li>storepass 指定私钥库的密码</li><li> file 导出证书文件名称</li></ul></blockquote><p><img src="https://oss.syshlang.com/blog/image/a51fae6f58384294a970f273a7890e2d.png" alt="exportcert"></p><h3 id="1-3-导入证书文件"><a href="#1-3-导入证书文件" class="headerlink" title="1.3.导入证书文件"></a>1.3. 导入证书文件</h3><p><indent>这一步主要是把上一步中导出的证书文件导入到公钥库中，命令如下：</indent></p><figure class="highlight shell"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">keytool -import -alias SYSHLANG -file certfile.cer -keystore publicCerts.keystore  -storepass 12345678A</span><br></pre></td></tr></tbody></table></figure><blockquote><p><strong>参数说明：</strong></p><ul><li>alias 公钥别称</li><li> file 证书文件名称</li><li> keystore 公钥文件名称</li><li> storepass 指定私钥库的密码</li></ul></blockquote><p><img src="https://oss.syshlang.com/blog/image/42d304bedb8a46f6bb00ddf215978f8c.png" alt="import"></p><blockquote><p><indent>是否信任此证书？[否]:”  ，那么请输入”Y”；此步骤中如果没有 storepass 会提示输入输入密钥库口令，输入之前设置的即可，如果没有设置口令，则输入信任证书默认密钥 “changeit”。</indent></p></blockquote><p><indent>如果顺利通过以上的步骤，会在当前目录下生成三个文件：<br><img src="https://oss.syshlang.com/blog/image/3d7ec9eddc2748e4813e6b3f02274898.png" alt="执行完成生成的三个文件"></indent></p><blockquote><ul><li>certfile.cer 认证证书，已经没用了，可以删掉</li><li> privateKeys.keystore 私钥，授权者保留，不能泄露</li><li> publicCerts.keystore 公钥，给客人使用（一般放在验证的代码中使用）</li></ul></blockquote><p><indent>此时，可以查看 cacerts 中的证书列表</indent></p><figure class="highlight shell"><table><tbody><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">keytool -list -keystore $JAVA_HOME/jre/lib/security/cacerts  -storepass changeit   //  查看所有</span><br><span class="line">keytool -list -alias SYSHLANG -keystore $JAVA_HOME/jre/lib/security/cacerts  -storepass changeit //  查看指定别名</span><br></pre></td></tr></tbody></table></figure><p><indent>删除 cacerts 中指定名称的证书</indent></p><figure class="highlight shell"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">keytool -delete -alias SYSHLANG -keystore $JAVA_HOME/jre/lib/security/cacerts  -storepass changeit</span><br></pre></td></tr></tbody></table></figure><h2 id="2-使用TrueLicense实现授权验证"><a href="#2-使用TrueLicense实现授权验证" class="headerlink" title="2.使用TrueLicense实现授权验证"></a>2. 使用 TrueLicense 实现授权验证</h2><h3 id="2-1服务端生成授权证书"><a href="#2-1服务端生成授权证书" class="headerlink" title="2.1服务端生成授权证书"></a>2.1 服务端生成授权证书</h3><blockquote><p><indent>TrueLicense 默认只帮我们验证了时间，可以自定义加入一些需要验证的其它信息，例如，服务器硬件的 MAC 地址、CPU 序列号等。然后调用私钥（privateKeys.keystore）生成授权证书文件（license.lic）。</indent></p></blockquote><figure class="highlight java"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">LicenseCreateParam</span> <span class="keyword">implements</span> <span class="title class_">Serializable</span> {</span><br><span class="line"> <span class="comment">// 添加自定义验证的信息字段</span></span><br><span class="line">}</span><br></pre></td></tr></tbody></table></figure><p><indent>具体代码实现，可以点击<span class="exturl" data-url="aHR0cHM6Ly9naXRodWIuY29tL3N5c2hsYW5nL0xpY2Vuc2VUb29scy90cmVlL21hc3Rlci9saWNlbnNlU2VydmVy">服务端代码链接<i class="fa fa-external-link-alt"></i></span>查看。</indent></p><h3 id="2-2客户端验证授权证书"><a href="#2-2客户端验证授权证书" class="headerlink" title="2.2客户端验证授权证书"></a>2.2 客户端验证授权证书</h3><blockquote><p><indent>在客户端，继承 LicenseManager 类，重写 verify 方法，校验自定义加入一些需要验证的其它信息。将公钥（publicCerts.keystore）放在项目中，将授权证书文件（license.lic）放到对应的路径即可。</indent></p></blockquote><figure class="highlight java"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">ClientLicenseManager</span> <span class="keyword">extends</span> <span class="title class_">LicenseManager</span> {</span><br><span class="line">     <span class="meta">@Override</span></span><br><span class="line">     <span class="keyword">protected</span> <span class="keyword">synchronized</span> LicenseContent <span class="title function_">verify</span><span class="params">(LicenseNotary licenseNotary)</span> <span class="keyword">throws</span> LicenseContentException {</span><br><span class="line">      <span class="comment">// 重写verify方法，校验自定义信息</span></span><br><span class="line">     }</span><br><span class="line">}</span><br></pre></td></tr></tbody></table></figure><p><indent>具体代码实现，可以点击<span class="exturl" data-url="aHR0cHM6Ly9naXRodWIuY29tL3N5c2hsYW5nL0xpY2Vuc2VUb29scy90cmVlL21hc3Rlci9saWNlbnNlQ2xpZW50">客户端代码链接<i class="fa fa-external-link-alt"></i></span>查看。</indent></p></indent><link rel="stylesheet" href="https://fastly.jsdelivr.net/npm/markmap-toolbar@0.18.10/dist/style.css"><script src="https://fastly.jsdelivr.net/npm/d3@7"></script><script src="https://fastly.jsdelivr.net/npm/markmap-view@0.18.10"></script><script src="https://fastly.jsdelivr.net/npm/markmap-toolbar@0.18.10"></script><link rel="stylesheet" href="/css/markmap.css"><script src="/js/markmap.js"></script>]]></content>
    
    
    <summary type="html">&lt;p&gt;&lt;indent&gt;近期开发的 web 应用交付海外客户试用时，领导要求需要限制客户的试用期限，于是考虑使用 license 进行授权。查阅相关资料，了解到一个开源的证书管理引擎 ——TrueLicense，通过 TrueLicense 可以实现授权验证功能，用于 license 的生成和有效性的验证。&lt;/indent&gt;&lt;/p&gt;</summary>
    
    
    
    <category term="CODING" scheme="https://syshlang.com/categories/CODING/"/>
    
    <category term="后端" scheme="https://syshlang.com/categories/CODING/%E5%90%8E%E7%AB%AF/"/>
    
    <category term="java" scheme="https://syshlang.com/categories/CODING/%E5%90%8E%E7%AB%AF/java/"/>
    
    
    <category term="JAVA" scheme="https://syshlang.com/tags/JAVA/"/>
    
    <category term="TrueLicense" scheme="https://syshlang.com/tags/TrueLicense/"/>
    
    <category term="License" scheme="https://syshlang.com/tags/License/"/>
    
  </entry>
  
  <entry>
    <title>设计模式之结构型模式中的适配器模式（Adapter Pattern）</title>
    <link href="https://syshlang.com/posts/fbfea71e/"/>
    <id>https://syshlang.com/posts/fbfea71e/</id>
    <published>2020-01-17T06:27:22.000Z</published>
    <updated>2025-12-01T14:31:49.255Z</updated>
    
    <content type="html"><![CDATA[<link rel="stylesheet" class="aplayer-secondary-style-marker" href="/assets/css/APlayer.min.css"><script src="/assets/js/APlayer.min.js" class="aplayer-secondary-script-marker"></script><script class="meting-secondary-script-marker" src="/assets/js/Meting.min.js"></script><p><img src="https://oss.syshlang.com/blog/image/5efe742a434943df8c3a8bc7e626df19.png" alt="Adapter Pattern"></p><span id="more"></span><h1 id="基本介绍"><a href="#基本介绍" class="headerlink" title="基本介绍"></a>基本介绍</h1><p><indent>生活中，我们接触到最多的适配器就是电源适配器，不管是电脑的电源适配器、手机电源适配器，还是其他的电子产品电源适配器，其主要目的都是将家用电源 220V 转换为电子产品需要的电压，这些适配器的接口有两孔的也有三孔的，实质就是把一个转接头或者电压变换成另外所需要的插口或者电压。拿到编程里面来说，就是将一个类的接口转换成客户端所希望的另外一个接口，这样使得原本不能一起工作的那些类 (接口不兼容，方法不匹配等) 可以一起工作，主要目的就是处理兼容性。</indent></p><h1 id="适配器模式分类"><a href="#适配器模式分类" class="headerlink" title="适配器模式分类"></a>适配器模式分类</h1><blockquote><p><indent>适配器模式主要分为三类：类适配器、对象适配器、接口适配器。类适配器和对象适配器在实现上有些差别，而接口适配器则差别较大。</indent></p></blockquote><h2 id="类适配器模式"><a href="#类适配器模式" class="headerlink" title="类适配器模式"></a>类适配器模式</h2><blockquote><p><indent>类适配器实现的原理主要是通过继承。适配器类（Adapter）通过继承需要被适配的类（Source），实现需要得到的类（Destination）接口，从而完成 Adapter 到 Destination 的适配。</indent></p></blockquote><h3 id="日常举例"><a href="#日常举例" class="headerlink" title="日常举例"></a>日常举例</h3><blockquote><p>手机充电的例子，通过手机充电器（Adapter）完成 220V 电源（Source）到 5V 电压（Destination）的转换</p></blockquote><figure class="highlight java"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">Source220V</span> {</span><br><span class="line">    <span class="keyword">public</span> <span class="type">int</span> <span class="title function_">outVoltage220v</span><span class="params">()</span>{</span><br><span class="line">        <span class="keyword">return</span> <span class="number">220</span>;</span><br><span class="line">    }</span><br><span class="line">}</span><br></pre></td></tr></tbody></table></figure><figure class="highlight java"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><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">interface</span> <span class="title class_">Destination5V</span> {</span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     *</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@return</span></span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    <span class="type">int</span> <span class="title function_">outVoltage5v</span><span class="params">()</span>;</span><br><span class="line">}</span><br></pre></td></tr></tbody></table></figure><figure class="highlight java"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">VoltageAdapter</span> <span class="keyword">extends</span> <span class="title class_">Source220V</span> <span class="keyword">implements</span> <span class="title class_">Destination5V</span>{</span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="keyword">public</span> <span class="type">int</span> <span class="title function_">outVoltage5v</span><span class="params">()</span> {</span><br><span class="line">        <span class="type">int</span> <span class="variable">voltage220v</span> <span class="operator">=</span> outVoltage220v();</span><br><span class="line">        <span class="keyword">return</span> voltage220v/<span class="number">44</span>;</span><br><span class="line">    }</span><br><span class="line">}</span><br></pre></td></tr></tbody></table></figure><figure class="highlight java"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">Client</span> {</span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title function_">main</span><span class="params">(String[] args)</span> {</span><br><span class="line">        <span class="type">VoltageAdapter</span> <span class="variable">voltageAdapter</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">VoltageAdapter</span>();</span><br><span class="line">        voltageAdapter.outVoltage5v();</span><br><span class="line">    }</span><br><span class="line">}</span><br></pre></td></tr></tbody></table></figure><h2 id="对象适配器模式"><a href="#对象适配器模式" class="headerlink" title="对象适配器模式"></a>对象适配器模式</h2><blockquote><p><indent>对象配器实现的原理主要是通过组合或聚合。在<a href="/posts/fd78ccb3/" title="设计模式七大原则之合成复用原则(Composite Reuse Principle)">《设计模式七大原则之合成复用原则 (Composite Reuse Principle)》</a>中，合成复用原则指出尽量使用合成 / 聚合，尽量不要使用类继承。对象适配器模式思路和类适配器模式基本相同，只不过将适配器类（Adapter）做修改，不再继承需要被适配的类（Source），而是直接持有需要被适配的类（Source），实现需要得到的类（Destination）接口，从而完成 Adapter 到 Destination 的适配。</indent></p></blockquote><h3 id="日常举例-1"><a href="#日常举例-1" class="headerlink" title="日常举例"></a>日常举例</h3><blockquote><p>仍然是手机充电的例子，只需要修改适配器类（Adapter），如下：</p></blockquote><figure class="highlight java"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">VoltageAdapter</span>  <span class="keyword">implements</span> <span class="title class_">Destination5V</span>{</span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     * 直接持有需要被适配的类（Source）</span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    <span class="keyword">private</span> Source220V source220V;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">public</span> <span class="title function_">VoltageAdapter</span><span class="params">(Source220V source220V)</span> {</span><br><span class="line">        <span class="built_in">this</span>.source220V = source220V;</span><br><span class="line">    }</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="keyword">public</span> <span class="type">int</span> <span class="title function_">outVoltage5v</span><span class="params">()</span> {</span><br><span class="line">        <span class="type">int</span> <span class="variable">voltage220v</span> <span class="operator">=</span> source220V.outVoltage220v();</span><br><span class="line">        <span class="keyword">return</span> voltage220v/<span class="number">44</span>;</span><br><span class="line">    }</span><br><span class="line">}</span><br></pre></td></tr></tbody></table></figure><figure class="highlight java"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">Client</span> {</span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title function_">main</span><span class="params">(String[] args)</span> {</span><br><span class="line">        <span class="type">VoltageAdapter</span> <span class="variable">voltageAdapter</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">VoltageAdapter</span>(<span class="keyword">new</span> <span class="title class_">Source220V</span>());</span><br><span class="line">        voltageAdapter.outVoltage5v();</span><br><span class="line">    }</span><br><span class="line">}</span><br></pre></td></tr></tbody></table></figure><h2 id="接口适配器模式"><a href="#接口适配器模式" class="headerlink" title="接口适配器模式"></a>接口适配器模式</h2><blockquote><p><indent>接口配器实现的原理主要是通过抽象类来实现适配。接口适配器模式的核心思路是，当不需要全部实现接口的方法时，可以先设计一个抽象的类实现接口，并为接口中的每个方法提供一个默认的实现，对于该抽象类的子类就可以有选择的覆盖父类的某些方法，从而达到适配的目的。因此，该模式也被称为缺省适配器模式或是默认适配器模式（Default Adapter Pattern）。</indent></p></blockquote><h3 id="日常举例-2"><a href="#日常举例-2" class="headerlink" title="日常举例"></a>日常举例</h3><blockquote><p>仍然是手机充电的例子，代码实现如下：</p></blockquote><figure class="highlight java"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">interface</span> <span class="title class_">Destination</span> {</span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     *</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@return</span></span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    <span class="type">int</span> <span class="title function_">outVoltage5v</span><span class="params">()</span>;</span><br><span class="line"></span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@return</span></span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    <span class="type">int</span> <span class="title function_">outVoltage10v</span><span class="params">()</span>;</span><br><span class="line"></span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@return</span></span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    <span class="type">int</span> <span class="title function_">outVoltage36v</span><span class="params">()</span>;</span><br><span class="line"></span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@return</span></span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    <span class="type">int</span> <span class="title function_">outVoltage220v</span><span class="params">()</span>;</span><br><span class="line">}</span><br></pre></td></tr></tbody></table></figure><figure class="highlight java"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><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="keyword">class</span> <span class="title class_">AbstractVoltageAdapter</span> <span class="keyword">implements</span> <span class="title class_">Destination</span>{</span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="keyword">public</span> <span class="type">int</span> <span class="title function_">outVoltage5v</span><span class="params">()</span> {</span><br><span class="line">        <span class="keyword">return</span> <span class="number">5</span>;</span><br><span class="line">    }</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="keyword">public</span> <span class="type">int</span> <span class="title function_">outVoltage10v</span><span class="params">()</span> {</span><br><span class="line">        <span class="keyword">return</span> <span class="number">10</span>;</span><br><span class="line">    }</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="keyword">public</span> <span class="type">int</span> <span class="title function_">outVoltage36v</span><span class="params">()</span> {</span><br><span class="line">        <span class="keyword">return</span> <span class="number">36</span>;</span><br><span class="line">    }</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="keyword">public</span> <span class="type">int</span> <span class="title function_">outVoltage220v</span><span class="params">()</span> {</span><br><span class="line">        <span class="keyword">return</span> <span class="number">220</span>;</span><br><span class="line">    }</span><br><span class="line">}</span><br></pre></td></tr></tbody></table></figure><figure class="highlight java"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">VoltageAdapter</span> <span class="keyword">extends</span> <span class="title class_">AbstractVoltageAdapter</span>{</span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="keyword">public</span> <span class="type">int</span> <span class="title function_">outVoltage5v</span><span class="params">()</span> {</span><br><span class="line">        <span class="type">int</span> <span class="variable">voltage220v</span> <span class="operator">=</span> outVoltage220v();</span><br><span class="line">        <span class="keyword">return</span> voltage220v/<span class="number">44</span>;</span><br><span class="line">    }</span><br><span class="line">}</span><br></pre></td></tr></tbody></table></figure><figure class="highlight java"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">Client</span> {</span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title function_">main</span><span class="params">(String[] args)</span> {</span><br><span class="line">        <span class="type">VoltageAdapter</span> <span class="variable">voltageAdapter</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">VoltageAdapter</span>();</span><br><span class="line">        voltageAdapter.outVoltage5v();</span><br><span class="line">    }</span><br><span class="line">}</span><br></pre></td></tr></tbody></table></figure><h1 id="适配器模式在Spring框架中的应用"><a href="#适配器模式在Spring框架中的应用" class="headerlink" title="适配器模式在Spring框架中的应用"></a>适配器模式在 Spring 框架中的应用</h1><p>对于 SpringMVC 有一个很重要的 servlet，它有一个方法：<br>org.springframework.web.servlet.DispatcherServlet#getHandlerAdapter<br>截取部分源码如下:<br>源码:org.springframework.web.servlet.DispatcherServlet（片段）</p><figure class="highlight java"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">DispatcherServlet</span> <span class="keyword">extends</span> <span class="title class_">FrameworkServlet</span> {</span><br><span class="line">...</span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment"> * Return the HandlerAdapter for this handler object.</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@param</span> handler the handler object to find an adapter for</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@throws</span> ServletException if no HandlerAdapter can be found for the handler. This is a fatal error.</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="keyword">protected</span> HandlerAdapter <span class="title function_">getHandlerAdapter</span><span class="params">(Object handler)</span> <span class="keyword">throws</span> ServletException {</span><br><span class="line"><span class="keyword">for</span> (HandlerAdapter ha : <span class="built_in">this</span>.handlerAdapters) {</span><br><span class="line"><span class="keyword">if</span> (logger.isTraceEnabled()) {</span><br><span class="line">logger.trace(<span class="string">"Testing handler adapter ["</span> + ha + <span class="string">"]"</span>);</span><br><span class="line">}</span><br><span class="line"><span class="keyword">if</span> (ha.supports(handler)) {</span><br><span class="line"><span class="keyword">return</span> ha;</span><br><span class="line">}</span><br><span class="line">}</span><br><span class="line"><span class="keyword">throw</span> <span class="keyword">new</span> <span class="title class_">ServletException</span>(<span class="string">"No adapter for handler ["</span> + handler +</span><br><span class="line"><span class="string">"]: The DispatcherServlet configuration needs to include a HandlerAdapter that supports this handler"</span>);</span><br><span class="line">}</span><br><span class="line">...</span><br><span class="line">}</span><br></pre></td></tr></tbody></table></figure><p>源码:org.springframework.web.servlet.HandlerAdapter</p><figure class="highlight java"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">interface</span> <span class="title class_">HandlerAdapter</span> {</span><br><span class="line"></span><br><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * Given a handler instance, return whether or not this {<span class="doctag">@code</span> HandlerAdapter}</span></span><br><span class="line"><span class="comment"> * can support it. Typical HandlerAdapters will base the decision on the handler</span></span><br><span class="line"><span class="comment"> * type. HandlerAdapters will usually only support one handler type each.</span></span><br><span class="line"><span class="comment"> * &lt;p&gt;A typical implementation:</span></span><br><span class="line"><span class="comment"> * &lt;p&gt;{<span class="doctag">@code</span></span></span><br><span class="line"><span class="comment"> * return (handler instanceof MyHandler);</span></span><br><span class="line"><span class="comment"> * }</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@param</span> handler handler object to check</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@return</span> whether or not this object can use the given handler</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="type">boolean</span> <span class="title function_">supports</span><span class="params">(Object handler)</span>;</span><br><span class="line"></span><br><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * Use the given handler to handle this request.</span></span><br><span class="line"><span class="comment"> * The workflow that is required may vary widely.</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@param</span> request current HTTP request</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@param</span> response current HTTP response</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@param</span> handler handler to use. This object must have previously been passed</span></span><br><span class="line"><span class="comment"> * to the {<span class="doctag">@code</span> supports} method of this interface, which must have</span></span><br><span class="line"><span class="comment"> * returned {<span class="doctag">@code</span> true}.</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@throws</span> Exception in case of errors</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@return</span> ModelAndView object with the name of the view and the required</span></span><br><span class="line"><span class="comment"> * model data, or {<span class="doctag">@code</span> null} if the request has been handled directly</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line">ModelAndView <span class="title function_">handle</span><span class="params">(HttpServletRequest request, HttpServletResponse response, Object handler)</span> <span class="keyword">throws</span> Exception;</span><br><span class="line"></span><br><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * Same contract as for HttpServlet's {<span class="doctag">@code</span> getLastModified} method.</span></span><br><span class="line"><span class="comment"> * Can simply return -1 if there's no support in the handler class.</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@param</span> request current HTTP request</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@param</span> handler handler to use</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@return</span> the lastModified value for the given handler</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@see</span> javax.servlet.http.HttpServlet#getLastModified</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@see</span> org.springframework.web.servlet.mvc.LastModified#getLastModified</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="type">long</span> <span class="title function_">getLastModified</span><span class="params">(HttpServletRequest request, Object handler)</span>;</span><br><span class="line"></span><br><span class="line">}</span><br></pre></td></tr></tbody></table></figure><p>再看 HandlerAdapter 的继承关系图：<br><img src="https://oss.syshlang.com/blog/image/fd80a0d38df54a76a39db5fdc12d8f10.png" alt="HandlerAdapter"></p><blockquote><p><strong>分析：</strong> 从上面的源码片段及 HandlerAdapter 的继承关系图，可以看出 Spring 定义了一个适配接口 HandlerAdapter，而其实现子类使得每一种 Controller 都有一种对应的适配器实现类，扩展 Controller 时只需增加对应的适配器实现类就可以了。</p></blockquote><p><font color="black" size="5" face="华文行楷">附：本次演示的项目地址</font><br><span class="exturl" data-url="aHR0cHM6Ly9naXRodWIuY29tL3N5c2hsYW5nL2phdmEtZGVzaWduLXBhdHRlcm5z">https://github.com/syshlang/java-design-patterns<i class="fa fa-external-link-alt"></i></span> <iframe src="https://ghbtns.com/github-btn.html?user=syshlang&amp;repo=java-design-patterns&amp;type=star&amp;count=true&amp;size=large" width="160px" height="30px" frameborder="0" loading="lazy" allowfullscreen=""></iframe></p><link rel="stylesheet" href="https://fastly.jsdelivr.net/npm/markmap-toolbar@0.18.10/dist/style.css"><script src="https://fastly.jsdelivr.net/npm/d3@7"></script><script src="https://fastly.jsdelivr.net/npm/markmap-view@0.18.10"></script><script src="https://fastly.jsdelivr.net/npm/markmap-toolbar@0.18.10"></script><link rel="stylesheet" href="/css/markmap.css"><script src="/js/markmap.js"></script>]]></content>
    
    
    <summary type="html">&lt;p&gt;&lt;img src=&quot;https://oss.syshlang.com/blog/image/5efe742a434943df8c3a8bc7e626df19.png&quot; alt=&quot;Adapter Pattern&quot;&gt;&lt;/p&gt;</summary>
    
    
    
    <category term="CODING" scheme="https://syshlang.com/categories/CODING/"/>
    
    <category term="后端" scheme="https://syshlang.com/categories/CODING/%E5%90%8E%E7%AB%AF/"/>
    
    <category term="设计模式" scheme="https://syshlang.com/categories/CODING/%E8%AE%BE%E8%AE%A1%E6%A8%A1%E5%BC%8F/"/>
    
    <category term="java" scheme="https://syshlang.com/categories/CODING/%E5%90%8E%E7%AB%AF/java/"/>
    
    
    <category term="JAVA" scheme="https://syshlang.com/tags/JAVA/"/>
    
    <category term="design" scheme="https://syshlang.com/tags/design/"/>
    
    <category term="patterns" scheme="https://syshlang.com/tags/patterns/"/>
    
  </entry>
  
  <entry>
    <title>设计模式之创建型模式中的建造者模式（Builder Pattern）</title>
    <link href="https://syshlang.com/posts/8cf99788/"/>
    <id>https://syshlang.com/posts/8cf99788/</id>
    <published>2020-01-17T00:56:09.000Z</published>
    <updated>2025-12-01T14:31:48.912Z</updated>
    
    <content type="html"><![CDATA[<link rel="stylesheet" class="aplayer-secondary-style-marker" href="/assets/css/APlayer.min.css"><script src="/assets/js/APlayer.min.js" class="aplayer-secondary-script-marker"></script><script class="meting-secondary-script-marker" src="/assets/js/Meting.min.js"></script><p><img src="https://oss.syshlang.com/blog/image/8516c5f1e8c04686b08240f878800170.jpg" alt="Builder Pattern"></p><span id="more"></span><h1 id="基本介绍"><a href="#基本介绍" class="headerlink" title="基本介绍"></a>基本介绍</h1><p><indent>在 java 开发过程中，我们经常会创建大量的对象，这些对象有简单的也有复杂的，但是不管是简单对象还是复杂对象，构建这些对象的过程是相对稳定的，只不过它们的内部属性（成员属性）不同，这也就意味着构建的具体内部细节不一样。那么，基于这种情况，我们可以将复杂的对象的构建过程抽象出来，通过抽象过程的不同实现方法来实现不同对象的内部细节构建过程，从某种意义上来说，这也是产品构建过程复杂度的解耦，这就是建造者模式（Builder Pattern）。<br><indent>建造者模式（Builder Pattern）又叫生成器模式，它把对象的创建步骤抽象成生成器，将一个产品的内部表象与产品的生产过程分割开来，一步一步构建一个复杂的对象，用户只用指定复杂对象的类型和内容，而无需知道内部的具体构建细节就可以构建它们。</indent></indent></p><h1 id="建造者模式的四个角色"><a href="#建造者模式的四个角色" class="headerlink" title="建造者模式的四个角色"></a>建造者模式的四个角色</h1><ul><li>产品角色（Product）：我们所要构建的产品对象；</li><li>抽象建造者（Builder）：创建产品（Product）对象的接口 / 抽象类。它定义了创建一个产品（Product）对象所需要的各个部件的操作（抽象方法），同时包含一个获取产品（Product）对象（获取成品）的方法。</li><li>具体建造者（ConcreteBuilder）：抽象建造者的具体实现，实现抽象建造者（Builder）的接口，构建和装配产品（Product）对象的各个部件，对于不同的部件或者构建步骤进行不同的详细实现，来完成不同的产品。</li><li>指导者（Director）：主要用来使用 Buider 接口，构建一个使用 Buider 接口的对象，以一个相对稳定且统一的过程生产产品（Product）对象。</li></ul><h1 id="日常举例"><a href="#日常举例" class="headerlink" title="日常举例"></a>日常举例</h1><blockquote><p>建造小汽车的例子：小汽车主要部件：发动机（Engine）、车身框架（Frame）、轮胎（Wheel）等，不管是什么品牌的汽车，都有这些部件，只不过内部的构造和质量等不一样。代码实现，如下：</p></blockquote><ul><li>产品角色（Product）</li></ul><figure class="highlight java"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">Car</span> {</span><br><span class="line">    <span class="keyword">private</span> String engine;</span><br><span class="line">    <span class="keyword">private</span> String frame;</span><br><span class="line">    <span class="keyword">private</span> String wheel;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">public</span> String <span class="title function_">getEngine</span><span class="params">()</span> {</span><br><span class="line">        <span class="keyword">return</span> engine;</span><br><span class="line">    }</span><br><span class="line"></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">setEngine</span><span class="params">(String engine)</span> {</span><br><span class="line">        <span class="built_in">this</span>.engine = engine;</span><br><span class="line">    }</span><br><span class="line"></span><br><span class="line">    <span class="keyword">public</span> String <span class="title function_">getFrame</span><span class="params">()</span> {</span><br><span class="line">        <span class="keyword">return</span> frame;</span><br><span class="line">    }</span><br><span class="line"></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">setFrame</span><span class="params">(String frame)</span> {</span><br><span class="line">        <span class="built_in">this</span>.frame = frame;</span><br><span class="line">    }</span><br><span class="line"></span><br><span class="line">    <span class="keyword">public</span> String <span class="title function_">getWheel</span><span class="params">()</span> {</span><br><span class="line">        <span class="keyword">return</span> wheel;</span><br><span class="line">    }</span><br><span class="line"></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">setWheel</span><span class="params">(String wheel)</span> {</span><br><span class="line">        <span class="built_in">this</span>.wheel = wheel;</span><br><span class="line">    }</span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="keyword">public</span> String <span class="title function_">toString</span><span class="params">()</span> {</span><br><span class="line">        <span class="keyword">return</span> <span class="string">"Car{"</span> +</span><br><span class="line">                <span class="string">"engine='"</span> + engine + <span class="string">'\''</span> +</span><br><span class="line">                <span class="string">", frame='"</span> + frame + <span class="string">'\''</span> +</span><br><span class="line">                <span class="string">", wheel='"</span> + wheel + <span class="string">'\''</span> +</span><br><span class="line">                <span class="string">'}'</span>;</span><br><span class="line">    }</span><br><span class="line">}</span><br></pre></td></tr></tbody></table></figure><ul><li>抽象建造者（Builder）</li></ul><figure class="highlight java"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">interface</span> <span class="title class_">CarBuilder</span> {</span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     * 建造发动机</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@return</span></span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    String <span class="title function_">buildEngine</span><span class="params">()</span>;</span><br><span class="line"></span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     * 建造车身框架</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@return</span></span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    String <span class="title function_">buildFrame</span><span class="params">()</span>;</span><br><span class="line"></span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     * 建造轮胎</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@return</span></span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    String <span class="title function_">buildWheel</span><span class="params">()</span>;</span><br><span class="line"></span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     * 获取小汽车成品</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@return</span></span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    Car <span class="title function_">getCar</span><span class="params">()</span>;</span><br><span class="line">}</span><br></pre></td></tr></tbody></table></figure><ul><li>具体建造者（ConcreteBuilder）</li></ul><figure class="highlight java"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">BaoMaCarBuilder</span> <span class="keyword">implements</span> <span class="title class_">CarBuilder</span>{</span><br><span class="line">    <span class="type">Car</span> <span class="variable">car</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">Car</span>();</span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">buildEngine</span><span class="params">()</span> {</span><br><span class="line">        <span class="built_in">this</span>.car.setEngine(<span class="string">"建造宝马发动机！"</span>);</span><br><span class="line">    }</span><br><span class="line"></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">buildFrame</span><span class="params">()</span> {</span><br><span class="line">        <span class="built_in">this</span>.car.setFrame(<span class="string">"建造宝马车身框架！"</span>);</span><br><span class="line">    }</span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">buildWheel</span><span class="params">()</span> {</span><br><span class="line">       <span class="built_in">this</span>.car.setWheel(<span class="string">"建造宝马轮胎！"</span>);</span><br><span class="line">    }</span><br><span class="line"></span><br><span class="line">    <span class="keyword">public</span> Car <span class="title function_">getCar</span><span class="params">()</span> {</span><br><span class="line">        <span class="keyword">return</span> <span class="built_in">this</span>.car;</span><br><span class="line">    }</span><br><span class="line">}</span><br></pre></td></tr></tbody></table></figure><ul><li>指导者（Director）</li></ul><figure class="highlight java"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">CarDirector</span> {</span><br><span class="line">    <span class="keyword">private</span> CarBuilder carBuilder;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">public</span> <span class="title function_">CarDirector</span><span class="params">(CarBuilder carBuilder)</span> {</span><br><span class="line">        <span class="built_in">this</span>.carBuilder = carBuilder;</span><br><span class="line">    }</span><br><span class="line"></span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     * 构建汽车的流程交给指导者</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@return</span></span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    <span class="keyword">public</span> Car <span class="title function_">builderCar</span><span class="params">()</span>{</span><br><span class="line">        carBuilder.buildEngine();</span><br><span class="line">        carBuilder.buildFrame();</span><br><span class="line">        carBuilder.buildWheel();</span><br><span class="line">        <span class="keyword">return</span> carBuilder.getCar();</span><br><span class="line">    }</span><br><span class="line">}</span><br></pre></td></tr></tbody></table></figure><ul><li>客户端使用 </li></ul><figure class="highlight java"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">Client</span> {</span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title function_">main</span><span class="params">(String[] args)</span> {</span><br><span class="line">        <span class="type">BaoMaCarBuilder</span> <span class="variable">baoMaCarBuilder</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">BaoMaCarBuilder</span>();</span><br><span class="line">        <span class="type">CarDirector</span> <span class="variable">carDirector</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">CarDirector</span>(baoMaCarBuilder);</span><br><span class="line">        <span class="type">Car</span> <span class="variable">car</span> <span class="operator">=</span> carDirector.builderCar();</span><br><span class="line">        System.out.println(car);</span><br><span class="line">    }</span><br><span class="line">}</span><br></pre></td></tr></tbody></table></figure><p>运行结果：<br><img src="https://oss.syshlang.com/blog/image/e1fff53715e04c07904698747142b970.png" alt="客户端运行结果"></p><h1 id="建造者模式在JDK中的应用"><a href="#建造者模式在JDK中的应用" class="headerlink" title="建造者模式在JDK中的应用"></a>建造者模式在 JDK 中的应用</h1><p>先看一张 StringBuilder 类图<br><img src="https://oss.syshlang.com/blog/image/edc35f18b4004060982a048141bed5ee.png" alt="StringBuilder类图"><br>源码:java.lang.Appendable</p><figure class="highlight java"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><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></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">interface</span> <span class="title class_">Appendable</span> {</span><br><span class="line"></span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     * Appends the specified character sequence to this &lt;tt&gt;Appendable&lt;/tt&gt;.</span></span><br><span class="line"><span class="comment">     *</span></span><br><span class="line"><span class="comment">     * &lt;p&gt; Depending on which class implements the character sequence</span></span><br><span class="line"><span class="comment">     * &lt;tt&gt;csq&lt;/tt&gt;, the entire sequence may not be appended.  For</span></span><br><span class="line"><span class="comment">     * instance, if &lt;tt&gt;csq&lt;/tt&gt; is a {<span class="doctag">@link</span> java.nio.CharBuffer} then</span></span><br><span class="line"><span class="comment">     * the subsequence to append is defined by the buffer's position and limit.</span></span><br><span class="line"><span class="comment">     *</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@param</span>  csq</span></span><br><span class="line"><span class="comment">     *         The character sequence to append.  If &lt;tt&gt;csq&lt;/tt&gt; is</span></span><br><span class="line"><span class="comment">     *         &lt;tt&gt;null&lt;/tt&gt;, then the four characters &lt;tt&gt;"null"&lt;/tt&gt; are</span></span><br><span class="line"><span class="comment">     *         appended to this Appendable.</span></span><br><span class="line"><span class="comment">     *</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@return</span>  A reference to this &lt;tt&gt;Appendable&lt;/tt&gt;</span></span><br><span class="line"><span class="comment">     *</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@throws</span>  IOException</span></span><br><span class="line"><span class="comment">     *          If an I/O error occurs</span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    Appendable <span class="title function_">append</span><span class="params">(CharSequence csq)</span> <span class="keyword">throws</span> IOException;</span><br><span class="line"></span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     * Appends a subsequence of the specified character sequence to this</span></span><br><span class="line"><span class="comment">     * &lt;tt&gt;Appendable&lt;/tt&gt;.</span></span><br><span class="line"><span class="comment">     *</span></span><br><span class="line"><span class="comment">     * &lt;p&gt; An invocation of this method of the form &lt;tt&gt;out.append(csq, start,</span></span><br><span class="line"><span class="comment">     * end)&lt;/tt&gt; when &lt;tt&gt;csq&lt;/tt&gt; is not &lt;tt&gt;null&lt;/tt&gt;, behaves in</span></span><br><span class="line"><span class="comment">     * exactly the same way as the invocation</span></span><br><span class="line"><span class="comment">     *</span></span><br><span class="line"><span class="comment">     * &lt;pre&gt;</span></span><br><span class="line"><span class="comment">     *     out.append(csq.subSequence(start, end)) &lt;/pre&gt;</span></span><br><span class="line"><span class="comment">     *</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@param</span>  csq</span></span><br><span class="line"><span class="comment">     *         The character sequence from which a subsequence will be</span></span><br><span class="line"><span class="comment">     *         appended.  If &lt;tt&gt;csq&lt;/tt&gt; is &lt;tt&gt;null&lt;/tt&gt;, then characters</span></span><br><span class="line"><span class="comment">     *         will be appended as if &lt;tt&gt;csq&lt;/tt&gt; contained the four</span></span><br><span class="line"><span class="comment">     *         characters &lt;tt&gt;"null"&lt;/tt&gt;.</span></span><br><span class="line"><span class="comment">     *</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@param</span>  start</span></span><br><span class="line"><span class="comment">     *         The index of the first character in the subsequence</span></span><br><span class="line"><span class="comment">     *</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@param</span>  end</span></span><br><span class="line"><span class="comment">     *         The index of the character following the last character in the</span></span><br><span class="line"><span class="comment">     *         subsequence</span></span><br><span class="line"><span class="comment">     *</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@return</span>  A reference to this &lt;tt&gt;Appendable&lt;/tt&gt;</span></span><br><span class="line"><span class="comment">     *</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@throws</span>  IndexOutOfBoundsException</span></span><br><span class="line"><span class="comment">     *          If &lt;tt&gt;start&lt;/tt&gt; or &lt;tt&gt;end&lt;/tt&gt; are negative, &lt;tt&gt;start&lt;/tt&gt;</span></span><br><span class="line"><span class="comment">     *          is greater than &lt;tt&gt;end&lt;/tt&gt;, or &lt;tt&gt;end&lt;/tt&gt; is greater than</span></span><br><span class="line"><span class="comment">     *          &lt;tt&gt;csq.length()&lt;/tt&gt;</span></span><br><span class="line"><span class="comment">     *</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@throws</span>  IOException</span></span><br><span class="line"><span class="comment">     *          If an I/O error occurs</span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    Appendable <span class="title function_">append</span><span class="params">(CharSequence csq, <span class="type">int</span> start, <span class="type">int</span> end)</span> <span class="keyword">throws</span> IOException;</span><br><span class="line"></span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     * Appends the specified character to this &lt;tt&gt;Appendable&lt;/tt&gt;.</span></span><br><span class="line"><span class="comment">     *</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@param</span>  c</span></span><br><span class="line"><span class="comment">     *         The character to append</span></span><br><span class="line"><span class="comment">     *</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@return</span>  A reference to this &lt;tt&gt;Appendable&lt;/tt&gt;</span></span><br><span class="line"><span class="comment">     *</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@throws</span>  IOException</span></span><br><span class="line"><span class="comment">     *          If an I/O error occurs</span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    Appendable <span class="title function_">append</span><span class="params">(<span class="type">char</span> c)</span> <span class="keyword">throws</span> IOException;</span><br><span class="line">}</span><br></pre></td></tr></tbody></table></figure><p>源码:java.lang.AbstractStringBuilder（片段）</p><figure class="highlight java"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">abstract</span> <span class="keyword">class</span> <span class="title class_">AbstractStringBuilder</span> <span class="keyword">implements</span> <span class="title class_">Appendable</span>, CharSequence {</span><br><span class="line">...</span><br><span class="line">  <span class="comment">// Documentation in subclasses because of synchro difference</span></span><br><span class="line">    <span class="keyword">public</span> AbstractStringBuilder <span class="title function_">append</span><span class="params">(StringBuffer sb)</span> {</span><br><span class="line">        <span class="keyword">if</span> (sb == <span class="literal">null</span>)</span><br><span class="line">            <span class="keyword">return</span> appendNull();</span><br><span class="line">        <span class="type">int</span> <span class="variable">len</span> <span class="operator">=</span> sb.length();</span><br><span class="line">        ensureCapacityInternal(count + len);</span><br><span class="line">        sb.getChars(<span class="number">0</span>, len, value, count);</span><br><span class="line">        count += len;</span><br><span class="line">        <span class="keyword">return</span> <span class="built_in">this</span>;</span><br><span class="line">    }</span><br><span class="line"></span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@since</span> 1.8</span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    AbstractStringBuilder <span class="title function_">append</span><span class="params">(AbstractStringBuilder asb)</span> {</span><br><span class="line">        <span class="keyword">if</span> (asb == <span class="literal">null</span>)</span><br><span class="line">            <span class="keyword">return</span> appendNull();</span><br><span class="line">        <span class="type">int</span> <span class="variable">len</span> <span class="operator">=</span> asb.length();</span><br><span class="line">        ensureCapacityInternal(count + len);</span><br><span class="line">        asb.getChars(<span class="number">0</span>, len, value, count);</span><br><span class="line">        count += len;</span><br><span class="line">        <span class="keyword">return</span> <span class="built_in">this</span>;</span><br><span class="line">    }</span><br><span class="line"></span><br><span class="line">    <span class="comment">// Documentation in subclasses because of synchro difference</span></span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="keyword">public</span> AbstractStringBuilder <span class="title function_">append</span><span class="params">(CharSequence s)</span> {</span><br><span class="line">        <span class="keyword">if</span> (s == <span class="literal">null</span>)</span><br><span class="line">            <span class="keyword">return</span> appendNull();</span><br><span class="line">        <span class="keyword">if</span> (s <span class="keyword">instanceof</span> String)</span><br><span class="line">            <span class="keyword">return</span> <span class="built_in">this</span>.append((String)s);</span><br><span class="line">        <span class="keyword">if</span> (s <span class="keyword">instanceof</span> AbstractStringBuilder)</span><br><span class="line">            <span class="keyword">return</span> <span class="built_in">this</span>.append((AbstractStringBuilder)s);</span><br><span class="line"></span><br><span class="line">        <span class="keyword">return</span> <span class="built_in">this</span>.append(s, <span class="number">0</span>, s.length());</span><br><span class="line">    }</span><br><span class="line">...</span><br><span class="line">}</span><br></pre></td></tr></tbody></table></figure><p>源码:java.lang.StringBuilder（片段）</p><figure class="highlight java"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">final</span> <span class="keyword">class</span> <span class="title class_">StringBuilder</span></span><br><span class="line">    <span class="keyword">extends</span> <span class="title class_">AbstractStringBuilder</span></span><br><span class="line">    <span class="keyword">implements</span> <span class="title class_">java</span>.io.Serializable, CharSequence</span><br><span class="line">{</span><br><span class="line">...</span><br><span class="line">   <span class="meta">@Override</span></span><br><span class="line">     <span class="keyword">public</span> StringBuilder <span class="title function_">append</span><span class="params">(Object obj)</span> {</span><br><span class="line">         <span class="keyword">return</span> append(String.valueOf(obj));</span><br><span class="line">     }</span><br><span class="line"></span><br><span class="line">     <span class="meta">@Override</span></span><br><span class="line">     <span class="keyword">public</span> StringBuilder <span class="title function_">append</span><span class="params">(String str)</span> {</span><br><span class="line">         <span class="built_in">super</span>.append(str);</span><br><span class="line">         <span class="keyword">return</span> <span class="built_in">this</span>;</span><br><span class="line">     }</span><br><span class="line"></span><br><span class="line">     <span class="comment">/**</span></span><br><span class="line"><span class="comment">      * Appends the specified {<span class="doctag">@code</span> StringBuffer} to this sequence.</span></span><br><span class="line"><span class="comment">      * &lt;p&gt;</span></span><br><span class="line"><span class="comment">      * The characters of the {<span class="doctag">@code</span> StringBuffer} argument are appended,</span></span><br><span class="line"><span class="comment">      * in order, to this sequence, increasing the</span></span><br><span class="line"><span class="comment">      * length of this sequence by the length of the argument.</span></span><br><span class="line"><span class="comment">      * If {<span class="doctag">@code</span> sb} is {<span class="doctag">@code</span> null}, then the four characters</span></span><br><span class="line"><span class="comment">      * {<span class="doctag">@code</span> "null"} are appended to this sequence.</span></span><br><span class="line"><span class="comment">      * &lt;p&gt;</span></span><br><span class="line"><span class="comment">      * Let &lt;i&gt;n&lt;/i&gt; be the length of this character sequence just prior to</span></span><br><span class="line"><span class="comment">      * execution of the {<span class="doctag">@code</span> append} method. Then the character at index</span></span><br><span class="line"><span class="comment">      * &lt;i&gt;k&lt;/i&gt; in the new character sequence is equal to the character at</span></span><br><span class="line"><span class="comment">      * index &lt;i&gt;k&lt;/i&gt; in the old character sequence, if &lt;i&gt;k&lt;/i&gt; is less than</span></span><br><span class="line"><span class="comment">      * &lt;i&gt;n&lt;/i&gt;; otherwise, it is equal to the character at index &lt;i&gt;k-n&lt;/i&gt;</span></span><br><span class="line"><span class="comment">      * in the argument {<span class="doctag">@code</span> sb}.</span></span><br><span class="line"><span class="comment">      *</span></span><br><span class="line"><span class="comment">      * <span class="doctag">@param</span>   sb   the {<span class="doctag">@code</span> StringBuffer} to append.</span></span><br><span class="line"><span class="comment">      * <span class="doctag">@return</span>  a reference to this object.</span></span><br><span class="line"><span class="comment">      */</span></span><br><span class="line">     <span class="keyword">public</span> StringBuilder <span class="title function_">append</span><span class="params">(StringBuffer sb)</span> {</span><br><span class="line">         <span class="built_in">super</span>.append(sb);</span><br><span class="line">         <span class="keyword">return</span> <span class="built_in">this</span>;</span><br><span class="line">     }</span><br><span class="line"></span><br><span class="line">     <span class="meta">@Override</span></span><br><span class="line">     <span class="keyword">public</span> StringBuilder <span class="title function_">append</span><span class="params">(CharSequence s)</span> {</span><br><span class="line">         <span class="built_in">super</span>.append(s);</span><br><span class="line">         <span class="keyword">return</span> <span class="built_in">this</span>;</span><br><span class="line">     }</span><br><span class="line">...</span><br><span class="line">}</span><br></pre></td></tr></tbody></table></figure><blockquote><p><strong>分析：</strong> 接口 Appendable 定义了多个 append 抽象方法，即为抽象建造者；抽象类 AbstractStringBuilder 实现了接口 Appendable 的方法，即为具体建造者，但是不能实例化；StringBuilder 继承了抽象类 AbstractStringBuilder，具体的方法已经由 AbstractStringBuilder 实现，StringBuilder 对部分方法进行了覆盖，由此可以看出，StringBuilder 既充当了具体建造者，也是指导者的角色。</p></blockquote><p><font color="black" size="5" face="华文行楷">附：本次演示的项目地址</font><br><span class="exturl" data-url="aHR0cHM6Ly9naXRodWIuY29tL3N5c2hsYW5nL2phdmEtZGVzaWduLXBhdHRlcm5z">https://github.com/syshlang/java-design-patterns<i class="fa fa-external-link-alt"></i></span> <iframe src="https://ghbtns.com/github-btn.html?user=syshlang&amp;repo=java-design-patterns&amp;type=star&amp;count=true&amp;size=large" width="160px" height="30px" frameborder="0" loading="lazy" allowfullscreen=""></iframe></p><link rel="stylesheet" href="https://fastly.jsdelivr.net/npm/markmap-toolbar@0.18.10/dist/style.css"><script src="https://fastly.jsdelivr.net/npm/d3@7"></script><script src="https://fastly.jsdelivr.net/npm/markmap-view@0.18.10"></script><script src="https://fastly.jsdelivr.net/npm/markmap-toolbar@0.18.10"></script><link rel="stylesheet" href="/css/markmap.css"><script src="/js/markmap.js"></script>]]></content>
    
    
    <summary type="html">&lt;p&gt;&lt;img src=&quot;https://oss.syshlang.com/blog/image/8516c5f1e8c04686b08240f878800170.jpg&quot; alt=&quot;Builder Pattern&quot;&gt;&lt;/p&gt;</summary>
    
    
    
    <category term="CODING" scheme="https://syshlang.com/categories/CODING/"/>
    
    <category term="后端" scheme="https://syshlang.com/categories/CODING/%E5%90%8E%E7%AB%AF/"/>
    
    <category term="设计模式" scheme="https://syshlang.com/categories/CODING/%E8%AE%BE%E8%AE%A1%E6%A8%A1%E5%BC%8F/"/>
    
    <category term="java" scheme="https://syshlang.com/categories/CODING/%E5%90%8E%E7%AB%AF/java/"/>
    
    
    <category term="JAVA" scheme="https://syshlang.com/tags/JAVA/"/>
    
    <category term="design" scheme="https://syshlang.com/tags/design/"/>
    
    <category term="patterns" scheme="https://syshlang.com/tags/patterns/"/>
    
  </entry>
  
  <entry>
    <title>原型模式（Prototype Pattern）之浅拷贝和深拷贝</title>
    <link href="https://syshlang.com/posts/a853c6f2/"/>
    <id>https://syshlang.com/posts/a853c6f2/</id>
    <published>2020-01-15T07:06:44.000Z</published>
    <updated>2025-12-01T14:31:49.304Z</updated>
    
    <content type="html"><![CDATA[<link rel="stylesheet" class="aplayer-secondary-style-marker" href="/assets/css/APlayer.min.css"><script src="/assets/js/APlayer.min.js" class="aplayer-secondary-script-marker"></script><script class="meting-secondary-script-marker" src="/assets/js/Meting.min.js"></script><p><img src="https://oss.syshlang.com/blog/image/3d175afc797c45f0a1eb48c41416e7dd.png" alt="浅拷贝和深拷贝"></p><span id="more"></span><p><indent>在<a href="/posts/129d022b/" title="设计模式之创建型模式中的原型模式（Prototype Pattern）">《设计模式之创建型模式中的原型模式（Prototype Pattern）》</a>中，通过克隆羊多莉的例子了解了原型模式，从客户端代码运行的结果，很容易发现：通过原型对象创建另一个新对象时，如果更改原型对象的某些类型的属性，新建的对象的属性也可能会发生变化。</indent></p><h2 id="浅拷贝"><a href="#浅拷贝" class="headerlink" title="浅拷贝"></a>浅拷贝</h2><blockquote><p><indent>通过原型对象创建另一个新对象时，将原型对象的非静态成员变量复制到新的对象，对于不同类型的成员变量，拷贝规则如下：</indent></p><ol><li>如果成员变量的数据类型是值类型（基本数据类型），浅拷贝会直接进行值传递，也就是直接复制一份该属性给新对象；</li><li>如果成员变量的数据类型是引用类型，则浅拷贝会进行引用传递，也就是只会将该成员变量的引用值（内存地址）复制一份给新对象，而不会复制引用的对象，那么也就意味着新对象和原型对象的该成员变量都指向同一个实例。因此，一个对象中修改成员变量的值会影响到另一个对象的该成员变量的值。</li></ol></blockquote><p><indent>上一文我们举的例子就是浅拷贝实现克隆羊，SheepPrototype 类实现了 Cloneable 接口，使用了默认的 super.clone () 方法。</indent></p><div class="note info"><ul><li>基本类型也称为值类型，分别是字符类型 char，布尔类型 boolean 以及数值类型 byte、short、int、long、float、double。</li><li>引用类型则包括类、接口、数组、枚举等。</li><li>Java 将内存空间分为堆和栈。基本类型直接在栈中存储数值，而引用类型是将引用放在栈中，实际存储的值是放在堆中，通过栈中的引用指向堆中存放的数据。</li></ul></div><h2 id="深拷贝"><a href="#深拷贝" class="headerlink" title="深拷贝"></a>深拷贝</h2><blockquote><p><indent>通过原型对象创建另一个新对象时，将原型对象的非静态成员变量复制到新的对象，不管成员变量的数据类型是值类型还是引用类型，深拷贝都会重新复制一份给新的对象。因此，修改其中一个对象的任何成员变量的值，都不会影响到另一个对象。</indent></p></blockquote><h3 id="深拷贝的实现"><a href="#深拷贝的实现" class="headerlink" title="深拷贝的实现"></a>深拷贝的实现</h3><p><strong>一、重写 clone () 方法</strong><br><indent>重写 clone () 方法，把要复制的对象所引用的对象都复制一遍，如下：</indent></p><figure class="highlight java"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">DeepConcreteSheepPrototype</span>  <span class="keyword">implements</span> <span class="title class_">Cloneable</span>{</span><br><span class="line">    <span class="keyword">private</span> String name;</span><br><span class="line">    <span class="keyword">private</span> <span class="type">int</span> age;</span><br><span class="line">    <span class="keyword">private</span> String color;</span><br><span class="line">    <span class="keyword">private</span> SheepPrototype mother;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">public</span> <span class="title function_">DeepConcreteSheepPrototype</span><span class="params">(String name, <span class="type">int</span> age, String color)</span> {</span><br><span class="line">        <span class="built_in">this</span>.name = name;</span><br><span class="line">        <span class="built_in">this</span>.age = age;</span><br><span class="line">        <span class="built_in">this</span>.color = color;</span><br><span class="line">    }</span><br><span class="line"></span><br><span class="line">    <span class="keyword">public</span> <span class="title function_">DeepConcreteSheepPrototype</span><span class="params">()</span> {</span><br><span class="line">    }</span><br><span class="line"></span><br><span class="line">    <span class="keyword">public</span> String <span class="title function_">getName</span><span class="params">()</span> {</span><br><span class="line">        <span class="keyword">return</span> name;</span><br><span class="line">    }</span><br><span class="line"></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">setName</span><span class="params">(String name)</span> {</span><br><span class="line">        <span class="built_in">this</span>.name = name;</span><br><span class="line">    }</span><br><span class="line"></span><br><span class="line">    <span class="keyword">public</span> <span class="type">int</span> <span class="title function_">getAge</span><span class="params">()</span> {</span><br><span class="line">        <span class="keyword">return</span> age;</span><br><span class="line">    }</span><br><span class="line"></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">setAge</span><span class="params">(<span class="type">int</span> age)</span> {</span><br><span class="line">        <span class="built_in">this</span>.age = age;</span><br><span class="line">    }</span><br><span class="line"></span><br><span class="line">    <span class="keyword">public</span> String <span class="title function_">getColor</span><span class="params">()</span> {</span><br><span class="line">        <span class="keyword">return</span> color;</span><br><span class="line">    }</span><br><span class="line"></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">setColor</span><span class="params">(String color)</span> {</span><br><span class="line">        <span class="built_in">this</span>.color = color;</span><br><span class="line">    }</span><br><span class="line"></span><br><span class="line">    <span class="keyword">public</span> SheepPrototype <span class="title function_">getMother</span><span class="params">()</span> {</span><br><span class="line">        <span class="keyword">return</span> mother;</span><br><span class="line">    }</span><br><span class="line"></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">setMother</span><span class="params">(SheepPrototype mother)</span> {</span><br><span class="line">        <span class="built_in">this</span>.mother = mother;</span><br><span class="line">    }</span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     * 克隆实例</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@return</span></span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="keyword">protected</span> DeepConcreteSheepPrototype <span class="title function_">clone</span><span class="params">()</span>  {</span><br><span class="line">        <span class="type">DeepConcreteSheepPrototype</span> <span class="variable">deepConcreteSheepPrototype</span> <span class="operator">=</span> <span class="literal">null</span>;</span><br><span class="line">        <span class="keyword">try</span> {</span><br><span class="line">            deepConcreteSheepPrototype = (DeepConcreteSheepPrototype) <span class="built_in">super</span>.clone();</span><br><span class="line">            <span class="type">SheepPrototype</span> <span class="variable">sheepPrototype</span> <span class="operator">=</span> mother.clone();</span><br><span class="line">            deepConcreteSheepPrototype.mother = sheepPrototype;</span><br><span class="line">        } <span class="keyword">catch</span> (CloneNotSupportedException e) {</span><br><span class="line">            e.printStackTrace();</span><br><span class="line">        }</span><br><span class="line">        <span class="keyword">return</span> deepConcreteSheepPrototype;</span><br><span class="line">    }</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="keyword">public</span> String <span class="title function_">toString</span><span class="params">()</span> {</span><br><span class="line">        <span class="keyword">return</span> <span class="string">"DeepConcreteSheepPrototype{"</span> +</span><br><span class="line">                <span class="string">"name='"</span> + name + <span class="string">'\''</span> +</span><br><span class="line">                <span class="string">", age="</span> + age +</span><br><span class="line">                <span class="string">", color='"</span> + color + <span class="string">'\''</span> +</span><br><span class="line">                <span class="string">", mother="</span> + mother +</span><br><span class="line">                <span class="string">'}'</span>;</span><br><span class="line">    }</span><br><span class="line">}</span><br></pre></td></tr></tbody></table></figure><figure class="highlight java"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">CloneDollyPrototype</span> {</span><br><span class="line"></span><br><span class="line">    <span class="keyword">static</span> <span class="keyword">class</span>  <span class="title class_">Client</span>{</span><br><span class="line">        <span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title function_">main</span><span class="params">(String[] args)</span> {</span><br><span class="line">            <span class="type">DeepConcreteSheepPrototype</span> <span class="variable">deepDolly</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">DeepConcreteSheepPrototype</span>(<span class="string">"dolly"</span>,<span class="number">2</span>,<span class="string">"gray"</span>);</span><br><span class="line">            deepDolly.setMother(<span class="keyword">new</span> <span class="title class_">ConcreteSheepPrototype</span>(<span class="string">"dolly"</span>,<span class="number">5</span>,<span class="string">"gray"</span>));</span><br><span class="line">            <span class="type">DeepConcreteSheepPrototype</span> <span class="variable">deepConcreteSheepPrototype</span> <span class="operator">=</span> deepDolly.clone();</span><br><span class="line">            <span class="type">DeepConcreteSheepPrototype</span> <span class="variable">deepConcreteSheepPrototype1</span> <span class="operator">=</span> deepDolly.clone();</span><br><span class="line">            <span class="type">DeepConcreteSheepPrototype</span> <span class="variable">deepConcreteSheepPrototypeN</span> <span class="operator">=</span> deepDolly.clone();</span><br><span class="line">            deepDolly.getMother().setColor(<span class="string">"red"</span>);</span><br><span class="line">            System.out.println(deepDolly);</span><br><span class="line">            System.out.println(deepConcreteSheepPrototype);</span><br><span class="line">            System.out.println(deepConcreteSheepPrototype1);</span><br><span class="line">            System.out.println(deepConcreteSheepPrototypeN);</span><br><span class="line">        }</span><br><span class="line">    }</span><br><span class="line">}</span><br></pre></td></tr></tbody></table></figure><p>运行结果如下：<br><img src="https://oss.syshlang.com/blog/image/24e40ed837e742c2a20e54b8c000561a.png" alt="重写clone()方法实现深拷贝"></p><p><strong>二、利用序列化</strong></p><figure class="highlight java"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br><span class="line">80</span><br><span class="line">81</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">DeepConcreteSheepSerializable</span> <span class="keyword">implements</span> <span class="title class_">Serializable</span> {</span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">static</span> <span class="keyword">final</span> <span class="type">long</span> <span class="variable">serialVersionUID</span> <span class="operator">=</span> <span class="number">8738439006982997247L</span>;</span><br><span class="line">    <span class="keyword">private</span> String name;</span><br><span class="line">    <span class="keyword">private</span> <span class="type">int</span> age;</span><br><span class="line">    <span class="keyword">private</span> String color;</span><br><span class="line">    <span class="keyword">private</span> SheepPrototype mother;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">public</span> String <span class="title function_">getName</span><span class="params">()</span> {</span><br><span class="line">        <span class="keyword">return</span> name;</span><br><span class="line">    }</span><br><span class="line"></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">setName</span><span class="params">(String name)</span> {</span><br><span class="line">        <span class="built_in">this</span>.name = name;</span><br><span class="line">    }</span><br><span class="line"></span><br><span class="line">    <span class="keyword">public</span> <span class="type">int</span> <span class="title function_">getAge</span><span class="params">()</span> {</span><br><span class="line">        <span class="keyword">return</span> age;</span><br><span class="line">    }</span><br><span class="line"></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">setAge</span><span class="params">(<span class="type">int</span> age)</span> {</span><br><span class="line">        <span class="built_in">this</span>.age = age;</span><br><span class="line">    }</span><br><span class="line"></span><br><span class="line">    <span class="keyword">public</span> String <span class="title function_">getColor</span><span class="params">()</span> {</span><br><span class="line">        <span class="keyword">return</span> color;</span><br><span class="line">    }</span><br><span class="line"></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">setColor</span><span class="params">(String color)</span> {</span><br><span class="line">        <span class="built_in">this</span>.color = color;</span><br><span class="line">    }</span><br><span class="line"></span><br><span class="line">    <span class="keyword">public</span> SheepPrototype <span class="title function_">getMother</span><span class="params">()</span> {</span><br><span class="line">        <span class="keyword">return</span> mother;</span><br><span class="line">    }</span><br><span class="line"></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">setMother</span><span class="params">(SheepPrototype mother)</span> {</span><br><span class="line">        <span class="built_in">this</span>.mother = mother;</span><br><span class="line">    }</span><br><span class="line"></span><br><span class="line">    <span class="keyword">public</span> Object <span class="title function_">deepClone</span><span class="params">()</span>{</span><br><span class="line">        <span class="type">ByteArrayOutputStream</span> <span class="variable">out</span> <span class="operator">=</span> <span class="literal">null</span>;</span><br><span class="line">        <span class="type">ObjectOutputStream</span> <span class="variable">obs</span> <span class="operator">=</span> <span class="literal">null</span>;</span><br><span class="line">        <span class="type">ByteArrayInputStream</span> <span class="variable">ios</span> <span class="operator">=</span> <span class="literal">null</span>;</span><br><span class="line">        <span class="type">ObjectInputStream</span> <span class="variable">ois</span> <span class="operator">=</span> <span class="literal">null</span>;</span><br><span class="line">        <span class="keyword">try</span> {</span><br><span class="line">            <span class="comment">//序列化</span></span><br><span class="line">            out = <span class="keyword">new</span> <span class="title class_">ByteArrayOutputStream</span>();</span><br><span class="line">            obs = <span class="keyword">new</span> <span class="title class_">ObjectOutputStream</span>(out);</span><br><span class="line">            obs.writeObject(<span class="built_in">this</span>);</span><br><span class="line">            obs.close();</span><br><span class="line"></span><br><span class="line">            <span class="comment">//反序列化</span></span><br><span class="line">            ios = <span class="keyword">new</span> <span class="title class_">ByteArrayInputStream</span>(out.toByteArray());</span><br><span class="line">            ois = <span class="keyword">new</span> <span class="title class_">ObjectInputStream</span>(ios);</span><br><span class="line">            <span class="comment">//返回生成的新对象</span></span><br><span class="line">            <span class="type">DeepConcreteSheepSerializable</span> <span class="variable">deepConcreteSheepSerializable</span> <span class="operator">=</span> (DeepConcreteSheepSerializable) ois.readObject();</span><br><span class="line">            ois.close();</span><br><span class="line">        } <span class="keyword">catch</span> (Exception e) {</span><br><span class="line">            e.printStackTrace();</span><br><span class="line">        } <span class="keyword">finally</span> {</span><br><span class="line">            <span class="keyword">try</span> {</span><br><span class="line">                out.close();</span><br><span class="line">                obs.close();</span><br><span class="line">                ios.close();</span><br><span class="line">                ois.close();</span><br><span class="line">            } <span class="keyword">catch</span> (IOException ex) {</span><br><span class="line">                ex.printStackTrace();</span><br><span class="line">            }</span><br><span class="line">        }</span><br><span class="line">        <span class="keyword">return</span> <span class="literal">null</span>;</span><br><span class="line">    }</span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="keyword">public</span> String <span class="title function_">toString</span><span class="params">()</span> {</span><br><span class="line">        <span class="keyword">return</span> <span class="string">"DeepConcreteSheepSerializable{"</span> +</span><br><span class="line">                <span class="string">"name='"</span> + name + <span class="string">'\''</span> +</span><br><span class="line">                <span class="string">", age="</span> + age +</span><br><span class="line">                <span class="string">", color='"</span> + color + <span class="string">'\''</span> +</span><br><span class="line">                <span class="string">", mother="</span> + mother +</span><br><span class="line">                <span class="string">'}'</span>;</span><br><span class="line">    }</span><br><span class="line">}</span><br></pre></td></tr></tbody></table></figure><figure class="highlight java"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">CloneDollyPrototype</span> {</span><br><span class="line"></span><br><span class="line">    <span class="keyword">static</span> <span class="keyword">class</span>  <span class="title class_">Client</span>{</span><br><span class="line">        <span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title function_">main</span><span class="params">(String[] args)</span> {</span><br><span class="line">            <span class="type">DeepConcreteSheepSerializable</span> <span class="variable">deepDollySerializable</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">DeepConcreteSheepSerializable</span>(<span class="string">"dolly"</span>,<span class="number">2</span>,<span class="string">"gray"</span>);</span><br><span class="line">            deepDollySerializable.setMother(<span class="keyword">new</span> <span class="title class_">ConcreteSheepPrototype</span>(<span class="string">"dolly"</span>,<span class="number">5</span>,<span class="string">"gray"</span>));</span><br><span class="line">            <span class="type">DeepConcreteSheepSerializable</span> <span class="variable">deepConcreteSheepSerializable</span> <span class="operator">=</span> deepDollySerializable.deepClone();</span><br><span class="line">            deepDollySerializable.getMother().setColor(<span class="string">"red"</span>);</span><br><span class="line">            deepDollySerializable.setAge(<span class="number">6</span>);</span><br><span class="line">            System.out.println(deepDollySerializable);</span><br><span class="line">            System.out.println(deepConcreteSheepSerializable);</span><br><span class="line">        }</span><br><span class="line">    }</span><br><span class="line">}</span><br></pre></td></tr></tbody></table></figure><p>运行结果：<br><img src="https://oss.syshlang.com/blog/image/879655e795484f61bf4697627673c4d7.png" alt="利用序列化实现深拷贝"></p><blockquote><p><indent>从上面可以看出，利用序列化实现深拷贝主要是在内存中通过字节流的拷贝来实现的。把原型对象序列化写入到字节流中，然后再从字节流中将其读出来进行反序列化，这样就可以创建一个新的对象，当然，此种方式不会存在原型对象与新对象之间引用共享的问题，推荐使用这种方式实现深拷贝。</indent></p></blockquote><p><font color="black" size="5" face="华文行楷">附：本次演示的项目地址</font><br><span class="exturl" data-url="aHR0cHM6Ly9naXRodWIuY29tL3N5c2hsYW5nL2phdmEtZGVzaWduLXBhdHRlcm5z">https://github.com/syshlang/java-design-patterns<i class="fa fa-external-link-alt"></i></span> <iframe src="https://ghbtns.com/github-btn.html?user=syshlang&amp;repo=java-design-patterns&amp;type=star&amp;count=true&amp;size=large" width="160px" height="30px" frameborder="0" loading="lazy" allowfullscreen=""></iframe></p><link rel="stylesheet" href="https://fastly.jsdelivr.net/npm/markmap-toolbar@0.18.10/dist/style.css"><script src="https://fastly.jsdelivr.net/npm/d3@7"></script><script src="https://fastly.jsdelivr.net/npm/markmap-view@0.18.10"></script><script src="https://fastly.jsdelivr.net/npm/markmap-toolbar@0.18.10"></script><link rel="stylesheet" href="/css/markmap.css"><script src="/js/markmap.js"></script>]]></content>
    
    
    <summary type="html">&lt;p&gt;&lt;img src=&quot;https://oss.syshlang.com/blog/image/3d175afc797c45f0a1eb48c41416e7dd.png&quot; alt=&quot;浅拷贝和深拷贝&quot;&gt;&lt;/p&gt;</summary>
    
    
    
    <category term="CODING" scheme="https://syshlang.com/categories/CODING/"/>
    
    <category term="后端" scheme="https://syshlang.com/categories/CODING/%E5%90%8E%E7%AB%AF/"/>
    
    <category term="设计模式" scheme="https://syshlang.com/categories/CODING/%E8%AE%BE%E8%AE%A1%E6%A8%A1%E5%BC%8F/"/>
    
    <category term="java" scheme="https://syshlang.com/categories/CODING/%E5%90%8E%E7%AB%AF/java/"/>
    
    
    <category term="JAVA" scheme="https://syshlang.com/tags/JAVA/"/>
    
    <category term="design" scheme="https://syshlang.com/tags/design/"/>
    
    <category term="patterns" scheme="https://syshlang.com/tags/patterns/"/>
    
  </entry>
  
  <entry>
    <title>设计模式之创建型模式中的原型模式（Prototype Pattern）</title>
    <link href="https://syshlang.com/posts/129d022b/"/>
    <id>https://syshlang.com/posts/129d022b/</id>
    <published>2020-01-03T00:58:09.000Z</published>
    <updated>2025-12-01T14:31:49.869Z</updated>
    
    <content type="html"><![CDATA[<link rel="stylesheet" class="aplayer-secondary-style-marker" href="/assets/css/APlayer.min.css"><script src="/assets/js/APlayer.min.js" class="aplayer-secondary-script-marker"></script><script class="meting-secondary-script-marker" src="/assets/js/Meting.min.js"></script><p><img src="https://oss.syshlang.com/blog/image/a34d1920b6ea4e7a973d91963ddd2c57.png" alt="Prototype Pattern"></p><span id="more"></span><h1 id="基本介绍"><a href="#基本介绍" class="headerlink" title="基本介绍"></a>基本介绍</h1><p><indent>原型模式（Prototype Pattern）主要用于在保证性能的情况下创建重复的对象，也就是创建当前对象的克隆，这种模式是实现了一个原型接口。在开发过程中，如果我们已经明确了所需要创建对象的种类，且创建这种类型对象的代价比较大（创建数量庞大，频繁的数据库交互对数据库等），就可以用原型实例指定所要创建对象的种类，然后通过拷贝这些原型创建新的对象。这就有点像我们生物里面学的细胞分裂。</indent></p><p><img src="https://oss.syshlang.com/blog/image/136204c1e11b49fba9707a4c90514d26.png" alt="Prototype Pattern"></p><h1 id="日常举例"><a href="#日常举例" class="headerlink" title="日常举例"></a>日常举例</h1><blockquote><p>克隆羊的例子：有一只克隆羊叫多莉，没错就是这只羊，长的就是下面图片中的样子。。。现在按照这个样子再克隆 N 只多莉。。。<br><img src="https://oss.syshlang.com/blog/image/95226345c15a4e0c8cc61c90e2397c70.jpg" alt="对，我是克隆羊多莉..."></p></blockquote><h2 id="传统方式"><a href="#传统方式" class="headerlink" title="传统方式"></a>传统方式</h2><figure class="highlight java"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">Sheep</span> {</span><br><span class="line">    <span class="keyword">private</span> String name;</span><br><span class="line">    <span class="keyword">private</span> <span class="type">int</span> age;</span><br><span class="line">    <span class="keyword">private</span> String color;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">public</span> <span class="title function_">Sheep</span><span class="params">(String name, <span class="type">int</span> age, String color)</span> {</span><br><span class="line">        <span class="built_in">this</span>.name = name;</span><br><span class="line">        <span class="built_in">this</span>.age = age;</span><br><span class="line">        <span class="built_in">this</span>.color = color;</span><br><span class="line">    }</span><br><span class="line"></span><br><span class="line">    <span class="keyword">public</span> <span class="title function_">Sheep</span><span class="params">()</span> {</span><br><span class="line">    }</span><br><span class="line"></span><br><span class="line">    <span class="keyword">public</span> String <span class="title function_">getName</span><span class="params">()</span> {</span><br><span class="line">        <span class="keyword">return</span> name;</span><br><span class="line">    }</span><br><span class="line"></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">setName</span><span class="params">(String name)</span> {</span><br><span class="line">        <span class="built_in">this</span>.name = name;</span><br><span class="line">    }</span><br><span class="line"></span><br><span class="line">    <span class="keyword">public</span> <span class="type">int</span> <span class="title function_">getAge</span><span class="params">()</span> {</span><br><span class="line">        <span class="keyword">return</span> age;</span><br><span class="line">    }</span><br><span class="line"></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">setAge</span><span class="params">(<span class="type">int</span> age)</span> {</span><br><span class="line">        <span class="built_in">this</span>.age = age;</span><br><span class="line">    }</span><br><span class="line"></span><br><span class="line">    <span class="keyword">public</span> String <span class="title function_">getColor</span><span class="params">()</span> {</span><br><span class="line">        <span class="keyword">return</span> color;</span><br><span class="line">    }</span><br><span class="line"></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">setColor</span><span class="params">(String color)</span> {</span><br><span class="line">        <span class="built_in">this</span>.color = color;</span><br><span class="line">    }</span><br><span class="line">}</span><br></pre></td></tr></tbody></table></figure><figure class="highlight java"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * 以传统的简单方式克隆多莉羊</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@author</span> sunys</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">CloneDollySimple</span> {</span><br><span class="line">    <span class="keyword">static</span> <span class="keyword">class</span>  <span class="title class_">Client</span>{</span><br><span class="line">        <span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title function_">main</span><span class="params">(String[] args)</span> {</span><br><span class="line">            <span class="type">Sheep</span> <span class="variable">dolly</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">Sheep</span>(<span class="string">"dolly"</span>,<span class="number">2</span>,<span class="string">"gray"</span>);</span><br><span class="line">            <span class="type">Sheep</span> <span class="variable">sheep</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">Sheep</span>(dolly.getName(),dolly.getAge(),dolly.getColor());</span><br><span class="line">            <span class="type">Sheep</span> <span class="variable">sheep1</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">Sheep</span>(dolly.getName(),dolly.getAge(),dolly.getColor());</span><br><span class="line">            <span class="type">Sheep</span> <span class="variable">sheepN</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">Sheep</span>(dolly.getName(),dolly.getAge(),dolly.getColor());</span><br><span class="line">        }</span><br><span class="line">    }</span><br><span class="line">}</span><br></pre></td></tr></tbody></table></figure><h2 id="原型模式"><a href="#原型模式" class="headerlink" title="原型模式"></a>原型模式</h2><figure class="highlight java"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><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> <span class="keyword">abstract</span> <span class="keyword">class</span> <span class="title class_">SheepPrototype</span> <span class="keyword">implements</span> <span class="title class_">Cloneable</span>{</span><br><span class="line">    <span class="keyword">private</span> String name;</span><br><span class="line">    <span class="keyword">private</span> <span class="type">int</span> age;</span><br><span class="line">    <span class="keyword">private</span> String color;</span><br><span class="line">    <span class="keyword">private</span> SheepPrototype mother;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">public</span> <span class="title function_">SheepPrototype</span><span class="params">(String name, <span class="type">int</span> age, String color)</span> {</span><br><span class="line">        <span class="built_in">this</span>.name = name;</span><br><span class="line">        <span class="built_in">this</span>.age = age;</span><br><span class="line">        <span class="built_in">this</span>.color = color;</span><br><span class="line">    }</span><br><span class="line"></span><br><span class="line">    <span class="keyword">public</span> <span class="title function_">SheepPrototype</span><span class="params">()</span> {</span><br><span class="line">    }</span><br><span class="line">    <span class="keyword">protected</span> <span class="keyword">abstract</span> String <span class="title function_">eat</span><span class="params">()</span>;</span><br><span class="line">    <span class="keyword">public</span> String <span class="title function_">getName</span><span class="params">()</span> {</span><br><span class="line">        <span class="keyword">return</span> name;</span><br><span class="line">    }</span><br><span class="line"></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">setName</span><span class="params">(String name)</span> {</span><br><span class="line">        <span class="built_in">this</span>.name = name;</span><br><span class="line">    }</span><br><span class="line"></span><br><span class="line">    <span class="keyword">public</span> <span class="type">int</span> <span class="title function_">getAge</span><span class="params">()</span> {</span><br><span class="line">        <span class="keyword">return</span> age;</span><br><span class="line">    }</span><br><span class="line"></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">setAge</span><span class="params">(<span class="type">int</span> age)</span> {</span><br><span class="line">        <span class="built_in">this</span>.age = age;</span><br><span class="line">    }</span><br><span class="line"></span><br><span class="line">    <span class="keyword">public</span> String <span class="title function_">getColor</span><span class="params">()</span> {</span><br><span class="line">        <span class="keyword">return</span> color;</span><br><span class="line">    }</span><br><span class="line"></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">setColor</span><span class="params">(String color)</span> {</span><br><span class="line">        <span class="built_in">this</span>.color = color;</span><br><span class="line">    }</span><br><span class="line">    <span class="keyword">public</span> SheepPrototype <span class="title function_">getMother</span><span class="params">()</span> {</span><br><span class="line">        <span class="keyword">return</span> mother;</span><br><span class="line">    }</span><br><span class="line"></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">setMother</span><span class="params">(SheepPrototype mother)</span> {</span><br><span class="line">        <span class="built_in">this</span>.mother = mother;</span><br><span class="line">    }</span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     * 克隆实例</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@return</span></span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="keyword">protected</span> SheepPrototype <span class="title function_">clone</span><span class="params">()</span>  {</span><br><span class="line">        <span class="type">SheepPrototype</span> <span class="variable">sheepPrototype</span> <span class="operator">=</span> <span class="literal">null</span>;</span><br><span class="line">        <span class="keyword">try</span> {</span><br><span class="line">            sheepPrototype = (SheepPrototype) <span class="built_in">super</span>.clone();</span><br><span class="line">        } <span class="keyword">catch</span> (CloneNotSupportedException e) {</span><br><span class="line">            e.printStackTrace();</span><br><span class="line">        }</span><br><span class="line">        <span class="keyword">return</span> sheepPrototype;</span><br><span class="line">    }</span><br><span class="line"></span><br><span class="line">   <span class="meta">@Override</span></span><br><span class="line">   <span class="keyword">public</span> String <span class="title function_">toString</span><span class="params">()</span> {</span><br><span class="line">       <span class="keyword">return</span> <span class="string">"SheepPrototype{"</span> +</span><br><span class="line">               <span class="string">"name='"</span> + name + <span class="string">'\''</span> +</span><br><span class="line">               <span class="string">", age="</span> + age +</span><br><span class="line">               <span class="string">", color='"</span> + color + <span class="string">'\''</span> +</span><br><span class="line">               <span class="string">", mother="</span> + mother +</span><br><span class="line">               <span class="string">'}'</span>;</span><br><span class="line">   }</span><br><span class="line">}</span><br></pre></td></tr></tbody></table></figure><figure class="highlight java"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * 创建当前对象的浅表副本</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@author</span> sunys</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">ConcreteSheepPrototype</span> <span class="keyword">extends</span> <span class="title class_">SheepPrototype</span>{</span><br><span class="line">    <span class="keyword">public</span> <span class="title function_">ConcreteSheepPrototype</span><span class="params">(String name, <span class="type">int</span> age, String color)</span> {</span><br><span class="line">        <span class="built_in">super</span>(name, age, color);</span><br><span class="line">    }</span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="keyword">protected</span> String <span class="title function_">eat</span><span class="params">()</span> {</span><br><span class="line">        <span class="keyword">return</span> <span class="string">"Eating green grass"</span>;</span><br><span class="line">    }</span><br><span class="line">}</span><br></pre></td></tr></tbody></table></figure><figure class="highlight java"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> *</span></span><br><span class="line"><span class="comment"> * 以原型模式克隆多莉羊</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@author</span> sunys</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">CloneDollyPrototype</span> {</span><br><span class="line"></span><br><span class="line">    <span class="keyword">static</span> <span class="keyword">class</span>  <span class="title class_">Client</span>{</span><br><span class="line">        <span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title function_">main</span><span class="params">(String[] args)</span> {</span><br><span class="line">            <span class="type">ConcreteSheepPrototype</span> <span class="variable">dolly</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">ConcreteSheepPrototype</span>(<span class="string">"dolly"</span>,<span class="number">2</span>,<span class="string">"gray"</span>);</span><br><span class="line">            dolly.setMother(<span class="keyword">new</span> <span class="title class_">ConcreteSheepPrototype</span>(<span class="string">"dolly"</span>,<span class="number">5</span>,<span class="string">"gray"</span>));</span><br><span class="line">            <span class="type">ConcreteSheepPrototype</span> <span class="variable">sheepPrototype</span> <span class="operator">=</span> (ConcreteSheepPrototype) dolly.clone();</span><br><span class="line">            <span class="type">ConcreteSheepPrototype</span> <span class="variable">sheepPrototype1</span> <span class="operator">=</span> (ConcreteSheepPrototype) dolly.clone();</span><br><span class="line">            <span class="type">ConcreteSheepPrototype</span> <span class="variable">sheepPrototypeN</span> <span class="operator">=</span> (ConcreteSheepPrototype) dolly.clone();</span><br><span class="line">            dolly.getMother().setColor(<span class="string">"red"</span>);</span><br><span class="line">            dolly.setAge(<span class="number">6</span>);</span><br><span class="line">            System.out.println(dolly);</span><br><span class="line">            System.out.println(sheepPrototype);</span><br><span class="line">            System.out.println(sheepPrototype1);</span><br><span class="line">            System.out.println(sheepPrototypeN);</span><br><span class="line">        }</span><br><span class="line">    }</span><br><span class="line">}</span><br></pre></td></tr></tbody></table></figure><p>运行结果：<br><img src="https://oss.syshlang.com/blog/image/51d5099897d9449a95aab8f764f4d1f4.png" alt="浅拷贝"></p><blockquote><p><indent>对比以上的两种方式很容易可以看出，传统的方式虽然易于理解，但是每次创建新的对象时，都需要重新初始化对象，并获取原始对象的属性，然后设置属性，显然，这种方式效率低；相对于传统方式，原型模式就方便快捷很多，在无需关注细节的情况下，就可以通过原型对象创建出另外的可定制对象，这种方式必须实现 Cloneable 接口。</indent></p></blockquote><div class="note info"><p>原型模式主要包含 3 个角色：</p><ol><li>Prototype (抽象原型类)：声明克隆方法的接口，是所有具体原型类的公共父类，它可是抽象类也可以是接口，甚至可以是具体实现类。</li><li>ConcretePrototype (具体原型类)：它实现抽象原型类中声明的克隆方法，在克隆方法中返回自己的一个克隆对象。</li><li>Client (客户端)：在客户类中，让一个原型对象克隆自身从而创建一个新的对象。</li></ol></div><h1 id="原型模式在Spring框架中的应用"><a href="#原型模式在Spring框架中的应用" class="headerlink" title="原型模式在Spring框架中的应用"></a>原型模式在 Spring 框架中的应用</h1><figure class="highlight xml"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line"><span class="tag">&lt;<span class="name">bean</span> <span class="attr">id</span>=<span class="string">"pointcut"</span> <span class="attr">class</span>=<span class="string">"org.springframework.aop.support.JdkRegexpMethodPointcut"</span> <span class="attr">scope</span>=<span class="string">"prototype"</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">property</span> <span class="attr">name</span>=<span class="string">"patterns"</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">list</span>&gt;</span></span><br><span class="line">            <span class="tag">&lt;<span class="name">value</span>&gt;</span>com.syshlang.service..*Service.*(..)<span class="tag">&lt;/<span class="name">value</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;/<span class="name">list</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;/<span class="name">property</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;/<span class="name">bean</span>&gt;</span></span><br></pre></td></tr></tbody></table></figure><figure class="highlight java"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">@Bean</span></span><br><span class="line"><span class="meta">@Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE )</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">Sheep</span> {</span><br><span class="line">    <span class="keyword">private</span> String name;</span><br><span class="line">    <span class="keyword">private</span> <span class="type">int</span> age;</span><br><span class="line">    <span class="keyword">private</span> String color;</span><br><span class="line">}</span><br></pre></td></tr></tbody></table></figure><blockquote><p><indent>在上面的配置中可以看到一个标签属性 scope=”prototype”，原型模式与名称为 prototype 的作用域相似。在 Spring 中，一个类如果被标记为”prototype”, 那么将该类注入到另一个 bean 中或者调用容器的 getBean () 方法时，都会产生一个新的 bean 实例。其实，这个产生新的 bean 的过程就是利用了原型模式。</indent></p></blockquote><p>源码:org.springframework.beans.factory.support.AbstractBeanFactory#getBean (java.lang.String)</p><figure class="highlight java"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">//---------------------------------------------------------------------</span></span><br><span class="line"><span class="comment">// Implementation of BeanFactory interface</span></span><br><span class="line"><span class="comment">//---------------------------------------------------------------------</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">public</span> Object <span class="title function_">getBean</span><span class="params">(String name)</span> <span class="keyword">throws</span> BeansException {</span><br><span class="line">    <span class="keyword">return</span> doGetBean(name, <span class="literal">null</span>, <span class="literal">null</span>, <span class="literal">false</span>);</span><br><span class="line">}</span><br></pre></td></tr></tbody></table></figure><p>源码:org.springframework.beans.factory.support.AbstractBeanFactory#doGetBean ()（片段）</p><figure class="highlight java"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">protected</span> &lt;T&gt; T <span class="title function_">doGetBean</span><span class="params">(</span></span><br><span class="line"><span class="params"><span class="keyword">final</span> String name, <span class="keyword">final</span> Class&lt;T&gt; requiredType, <span class="keyword">final</span> Object[] args, <span class="type">boolean</span> typeCheckOnly)</span></span><br><span class="line"><span class="keyword">throws</span> BeansException {</span><br><span class="line">...</span><br><span class="line"><span class="keyword">else</span> <span class="keyword">if</span> (mbd.isPrototype()) {</span><br><span class="line"><span class="comment">// It's a prototype -&gt; create a new instance.</span></span><br><span class="line"><span class="type">Object</span> <span class="variable">prototypeInstance</span> <span class="operator">=</span> <span class="literal">null</span>;</span><br><span class="line"><span class="keyword">try</span> {</span><br><span class="line">beforePrototypeCreation(beanName);</span><br><span class="line">prototypeInstance = createBean(beanName, mbd, args);</span><br><span class="line">}</span><br><span class="line"><span class="keyword">finally</span> {</span><br><span class="line">afterPrototypeCreation(beanName);</span><br><span class="line">}</span><br><span class="line">bean = getObjectForBeanInstance(prototypeInstance, name, beanName, mbd);</span><br><span class="line">}</span><br><span class="line">}</span><br><span class="line">...</span><br></pre></td></tr></tbody></table></figure><p><font color="black" size="5" face="华文行楷">附：本次演示的项目地址</font><br><span class="exturl" data-url="aHR0cHM6Ly9naXRodWIuY29tL3N5c2hsYW5nL2phdmEtZGVzaWduLXBhdHRlcm5z">https://github.com/syshlang/java-design-patterns<i class="fa fa-external-link-alt"></i></span> <iframe src="https://ghbtns.com/github-btn.html?user=syshlang&amp;repo=java-design-patterns&amp;type=star&amp;count=true&amp;size=large" width="160px" height="30px" frameborder="0" loading="lazy" allowfullscreen=""></iframe></p><link rel="stylesheet" href="https://fastly.jsdelivr.net/npm/markmap-toolbar@0.18.10/dist/style.css"><script src="https://fastly.jsdelivr.net/npm/d3@7"></script><script src="https://fastly.jsdelivr.net/npm/markmap-view@0.18.10"></script><script src="https://fastly.jsdelivr.net/npm/markmap-toolbar@0.18.10"></script><link rel="stylesheet" href="/css/markmap.css"><script src="/js/markmap.js"></script>]]></content>
    
    
    <summary type="html">&lt;p&gt;&lt;img src=&quot;https://oss.syshlang.com/blog/image/a34d1920b6ea4e7a973d91963ddd2c57.png&quot; alt=&quot;Prototype Pattern&quot;&gt;&lt;/p&gt;</summary>
    
    
    
    <category term="CODING" scheme="https://syshlang.com/categories/CODING/"/>
    
    <category term="后端" scheme="https://syshlang.com/categories/CODING/%E5%90%8E%E7%AB%AF/"/>
    
    <category term="设计模式" scheme="https://syshlang.com/categories/CODING/%E8%AE%BE%E8%AE%A1%E6%A8%A1%E5%BC%8F/"/>
    
    <category term="java" scheme="https://syshlang.com/categories/CODING/%E5%90%8E%E7%AB%AF/java/"/>
    
    
    <category term="JAVA" scheme="https://syshlang.com/tags/JAVA/"/>
    
    <category term="design" scheme="https://syshlang.com/tags/design/"/>
    
    <category term="patterns" scheme="https://syshlang.com/tags/patterns/"/>
    
  </entry>
  
  <entry>
    <title>Oracle 和 Mysql 数据库转换</title>
    <link href="https://syshlang.com/posts/463fcbf0/"/>
    <id>https://syshlang.com/posts/463fcbf0/</id>
    <published>2019-12-30T02:22:40.000Z</published>
    <updated>2025-12-01T14:31:49.411Z</updated>
    
    <content type="html"><![CDATA[<link rel="stylesheet" class="aplayer-secondary-style-marker" href="/assets/css/APlayer.min.css"><script src="/assets/js/APlayer.min.js" class="aplayer-secondary-script-marker"></script><script class="meting-secondary-script-marker" src="/assets/js/Meting.min.js"></script><p><img src="https://oss.syshlang.com/blog/image/3bb849c2f9874fbe8be711d43d13dfac.jpg"></p><span id="more"></span><h1 id="前言"><a href="#前言" class="headerlink" title="前言"></a>前言</h1><p><indent>我们的项目一直使用的是 SSH 框架 + mysql+tomcat 服务器，但是最近一个新项目对数据库的使用和 web 服务器提出了硬性要求，按照他们的要求，需要使用 Oracle 数据库，并采用 Weblogic 进行服务部署。本文针对本次数据库转换（Mysql 转换 Oracle）过程中遇到的一些问题做记录，对于部署服务器的更换（tomcat 更换为 Weblogic）另一文章将继续分享。</indent></p><h1 id="问题分析"><a href="#问题分析" class="headerlink" title="问题分析"></a>问题分析</h1><p><indent>首先，我们系统的架构情况如下：</indent></p><blockquote><p>开发环境：</p><ul><li>MAVEN:3.5</li><li>JDK:1.8</li><li>Spring: 4.3.9.RELEASE</li><li>hibernate： 4.3.11.Final</li><li>Shiro：1.4.0</li></ul></blockquote><blockquote><p>开发工具:</p><ul><li>MySql: 5.7.x</li><li>ORACLE: 11.2.0.1.0</li><li>Tomcat: 9.0</li><li>EZDML:2.06 (表结构设计器)</li></ul></blockquote><p><indent>分析了一下部分代码，发现 Mysql 转换至 Oracle，整体框架无需大的改动，由于使用了 hibernate 框架做持久层，业务操作基本都是 HQL, 所以无需做单独处理，需要变动的主要涉及到数据库结构的转换和一些公共自定义 sql 的兼容性处理，明确了解决问题的要点，现在开始着手。</indent></p><h1 id="数据库转换"><a href="#数据库转换" class="headerlink" title="数据库转换"></a>数据库转换</h1><h2 id="数据库结构"><a href="#数据库结构" class="headerlink" title="数据库结构"></a>数据库结构</h2><h3 id="首先，使用EZDML表结构设计器进行数据库的整体转换"><a href="#首先，使用EZDML表结构设计器进行数据库的整体转换" class="headerlink" title="首先，使用EZDML表结构设计器进行数据库的整体转换"></a>首先，使用 EZDML 表结构设计器进行数据库的整体转换</h3><p>1、打开 EZDML，点击模型，选择导入数据库：<br><img src="https://oss.syshlang.com/blog/image/6d9e4a978a4341e3ac370a9df47d8b06.png"><br>2、点击确定，选择需要转换的表名，确定之后就可以看到对应的表结构模型；<br>3、在左边单击表明，就可以看到详细的表结构设计，选择生成，就可以看到不同类型数据库对应的建表 DDL。<br><img src="https://oss.syshlang.com/blog/image/2e1fc60ac49b404c86e6cf9931d4fd7d.png"></p><h3 id="然后，对表结构进行微调"><a href="#然后，对表结构进行微调" class="headerlink" title="然后，对表结构进行微调"></a>然后，对表结构进行微调</h3><h4 id="Mysql和Oracle之间的数据类型转换"><a href="#Mysql和Oracle之间的数据类型转换" class="headerlink" title="Mysql和Oracle之间的数据类型转换"></a>Mysql 和 Oracle 之间的数据类型转换</h4><table><thead><tr><th align="center">MySQL Data Type</th><th align="center">Oracle Data Type</th></tr></thead><tbody><tr><td align="center">BIGINT</td><td align="center">NUMBER(19, 0)</td></tr><tr><td align="center">BIT</td><td align="center">RAW</td></tr><tr><td align="center">BLOB</td><td align="center">BLOB, RAW</td></tr><tr><td align="center">CHAR</td><td align="center">CHAR</td></tr><tr><td align="center">DATE</td><td align="center">DATE</td></tr><tr><td align="center">DATETIME</td><td align="center">DATE</td></tr><tr><td align="center">DECIMAL</td><td align="center">FLOAT (24)</td></tr><tr><td align="center">DOUBLE</td><td align="center">FLOAT (24)</td></tr><tr><td align="center">DOUBLE PRECISION</td><td align="center">FLOAT (24)</td></tr><tr><td align="center">ENUM</td><td align="center">VARCHAR2</td></tr><tr><td align="center">FLOAT</td><td align="center">FLOAT</td></tr><tr><td align="center">INT</td><td align="center">NUMBER(10, 0)</td></tr><tr><td align="center">INTEGER</td><td align="center">NUMBER(10, 0)</td></tr><tr><td align="center">LONGBLOB</td><td align="center">BLOB, RAW</td></tr><tr><td align="center">LONGTEXT</td><td align="center">CLOB, RAW</td></tr><tr><td align="center">MEDIUMBLOB</td><td align="center">BLOB, RAW</td></tr><tr><td align="center">MEDIUMINT</td><td align="center">NUMBER(7, 0)</td></tr><tr><td align="center">MEDIUMTEXT</td><td align="center">CLOB, RAW</td></tr><tr><td align="center">NUMERIC</td><td align="center">NUMBER</td></tr><tr><td align="center">REAL</td><td align="center">FLOAT (24)</td></tr><tr><td align="center">SET</td><td align="center">VARCHAR2</td></tr><tr><td align="center">SMALLINT</td><td align="center">NUMBER(5, 0)</td></tr><tr><td align="center">TEXT</td><td align="center">VARCHAR2, CLOB</td></tr><tr><td align="center">TIME</td><td align="center">DATE</td></tr><tr><td align="center">TIMESTAMP</td><td align="center">DATE</td></tr><tr><td align="center">TINYBLOB</td><td align="center">RAW</td></tr><tr><td align="center">TINYINT</td><td align="center">NUMBER(3, 0)</td></tr><tr><td align="center">TINYTEXT</td><td align="center">VARCHAR2</td></tr><tr><td align="center">VARCHAR</td><td align="center">VARCHAR2, CLOB</td></tr><tr><td align="center">YEAR</td><td align="center">NUMBER</td></tr></tbody></table><h4 id="自增序列处理"><a href="#自增序列处理" class="headerlink" title="自增序列处理"></a>自增序列处理</h4><table><thead><tr><th align="center">MySQL Data Type</th><th align="center">Oracle Data Type</th></tr></thead><tbody><tr><td align="center"> 有自动增长的数据类型，插入记录时不用操作此字段，会自动获得数据值</td><td align="center"> ORACLE 没有自动增长的数据类型，需要建立一个自动增长的序列号，插入记录时要把序列号的下一个值赋于此字段</td></tr></tbody></table><p>Mysql 自动增长的数据类型</p><figure class="highlight sql"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">create table</span> table_name</span><br><span class="line">(</span><br><span class="line">    ID <span class="type">int</span> auto_increment <span class="comment">-- 自增长</span></span><br><span class="line">        <span class="keyword">primary key</span></span><br><span class="line">);</span><br></pre></td></tr></tbody></table></figure><p>Oracle 自动增长的数据类型</p><figure class="highlight sql"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><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">create</span> sequence table_name_pk</span><br><span class="line">minvalue <span class="number">1</span></span><br><span class="line">maxvalue <span class="number">9999999999999999999999999999</span></span><br><span class="line"><span class="keyword">start</span> <span class="keyword">with</span> <span class="number">447</span></span><br><span class="line">increment <span class="keyword">by</span> <span class="number">1</span></span><br><span class="line">cache <span class="number">20</span>;</span><br><span class="line"></span><br><span class="line"><span class="keyword">create table</span> table_name</span><br><span class="line">(</span><br><span class="line">ID <span class="type">int</span> generated <span class="keyword">as</span> <span class="keyword">identity</span></span><br><span class="line"><span class="keyword">constraint</span> table_name_pk</span><br><span class="line"><span class="keyword">primary key</span></span><br><span class="line">)</span><br><span class="line"><span class="operator">/</span></span><br></pre></td></tr></tbody></table></figure><h4 id="长字符串处理"><a href="#长字符串处理" class="headerlink" title="长字符串处理"></a>长字符串处理</h4><p><indent>在 ORACLE 中，进行 INSERT 和 UPDATE 时，最大可操作的字符串长度必须小于等于 4000 个单字节。如果超出这个长度，要插入更长的字符串数据，可以考虑使用 CLOB 类型。在本项目转换过程中，使用到了指纹数据的 16 进制字符串的存取，长度超过 4000，在 Mysql 中使用 BLOB 类型存储没问题，但是 ORACLE 下不可以。BLOB 全称为二进制大型对象（Binary   Large   Object)，它用于存储数据库中的大型二进制对象，原来在 ORACLE 下，对二进制对象有严格要求，所以采用 CLOB 类型存储。</indent></p><h4 id="日期字段处理"><a href="#日期字段处理" class="headerlink" title="日期字段处理"></a>日期字段处理</h4><p> <indent>在 MYSQL 中，日期字段可以分 DATE（包含年月日）和 DATETIME（包含年月日时分秒）两种类型，而 ORACLE 日期字段只有 DATE（包含年月日时分秒）一种类型。所以在实际业务使用过程中需要进行格式转换。</indent></p><table><thead><tr><th align="center">比较项</th><th align="center"> MySQL Data Type</th><th align="center">Oracle Data Type</th></tr></thead><tbody><tr><td align="center"> 数据类型</td><td align="center"> DATE（包含年月日）、<br>DATETIME（包含年月日时分秒）</td><td align="center">DATE（包含年月日时分秒）</td></tr><tr><td align="center">日期格式</td><td align="center"> % Y：代表 4 位的年份<br>% y：代表 2 为的年份<br>% m：代表月，格式为 (01……12) <br>% c：代表月，格式为 (1……12)<br>% d：代表月份中的天数，格式为 (00……31)<br>% e：代表月份中的天数，格式为 (0……31)<br>% H：代表小时，格式为 (00……23)<br>% k：代表 小时，格式为 (0……23)<br>% h： 代表小时，格式为 (01……12)<br>% I： 代表小时，格式为 (01……12)<br>% l ：代表小时，格式为 (1……12)<br>% i： 代表分钟，格式为 (00……59)<br>% r：代表 时间，格式为 12 小时 (hh:mm:ss [AP] M)<br>% T：代表 时间，格式为 24 小时 (hh:mm:ss)<br>% S：代表 秒，格式为 (00……59)<br>% s：代表 秒，格式为 (00……59)</td><td align="center">YYYY、YYY、YY 分别代表 4 位、3 位、2 位的数字年<br>MM 数字月<br>DD 数字日<br>AM 表示上午或者下午<br>HH24、HH12 代表 24 小时制或 12 小时制<br>MI 分钟<br>SS 秒钟</td></tr><tr><td align="center">当前日期</td><td align="center"> sysdate()、current_date、current_time</td><td align="center">sysdate、current_date、current_timestamp</td></tr><tr><td align="center"> 日期和字符互转</td><td align="center"><strong> DATE_FORMAT</strong><br>例如：DATE_FORMAT (sysdate (),’% Y-% m-% d % H:% i:% s’)<br><strong>STR_TO_DATE</strong><br>例如：STR_TO_DATE (‘2019-12-30 19:25:34’,’% Y-% m-% d % H:% i:% s’)</td><td align="center"><strong>TO_CHAR</strong><br>例如：TO_CHAR (sysdate,’YYYY-MM-DD HH24:MI:SS’)<br><strong>TO_DATE</strong><br>例如：TO_DATE (‘2019-12-30 19:25:34’,’YYYY-MM-DD HH24:MI:SS’)</td></tr><tr><td align="center"> 日期 / 时间增减</td><td align="center">增减一小时：<br>date_sub(createDate, interval -1 hour)<br>date_sub(createDate, interval 1 hour)<br>增减一天：<br>date_sub(createDate, interval -1 day)<br>date_sub(createDate, interval 1 day)<br>增减一月：<br>date_sub(createDate, interval -1 month)<br>date_sub(createDate, interval 1 month)<br>增减一季度：<br>date_sub(createDate, interval -3 month)<br>date_sub(createDate, interval 3 month)<br>增减一年：<br>date_sub(createDate, interval -1 year)<br>date_sub(createDate, interval 1 year)</td><td align="center"> 增减一小时：<br>createDate+1/24 <br>createDate-1/24<br>增减一天：<br>createDate+1<br>createDate-1<br>增减一月：<br>add_months(createDate, 1)<br>add_months(createDate, -1)<br>增减一季度：<br>add_months(createDate, 3)<br>add_months(createDate, -3)<br>增减一年：<br>add_months(createDate, 12) <br>add_months(createDate, -12)</td></tr><tr><td align="center"> 日期 / 时间比较</td><td align="center">①直接比较<br>②转成 unix 时间戳比较<br>③转换为日期类型比较<br></td><td align="center">①直接比较<br>②转换为日期类型比较</td></tr></tbody></table><p>日期 / 时间比较</p><figure class="highlight sql"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">-- Mysql</span></span><br><span class="line"><span class="comment">-- 直接比较（此种方式不走索引，一定程度上会降低性能）</span></span><br><span class="line"><span class="keyword">select</span> sysdate() <span class="keyword">from</span> dual <span class="keyword">where</span> <span class="string">'2019-12-30 19:39:05'</span> <span class="operator">&gt;</span> <span class="string">'2019-12-30 17:39:05'</span>;</span><br><span class="line"><span class="comment">-- 用unix_timestamp函数，将字符型的时间，转成unix时间戳</span></span><br><span class="line"><span class="keyword">select</span> sysdate() <span class="keyword">from</span> dual <span class="keyword">where</span> unix_timestamp(<span class="string">'2019-12-30 19:39:05'</span>) <span class="operator">&gt;</span> unix_timestamp(<span class="string">'2019-12-30 17:39:05'</span>);</span><br><span class="line"><span class="comment">-- 将字符串转换为相同格式相同进制的日期类型</span></span><br><span class="line"><span class="keyword">select</span> sysdate() <span class="keyword">from</span> dual <span class="keyword">where</span> str_to_date(<span class="string">'2019-12-30 19:39:05'</span>,<span class="string">'%Y-%m-%d %H:%i:%s'</span>) <span class="operator">&gt;</span> str_to_date(<span class="string">'2019-12-30 17:39:05'</span>,<span class="string">'%Y-%m-%d %H:%i:%s'</span>);</span><br><span class="line"></span><br><span class="line"><span class="comment">-- Oracle</span></span><br><span class="line"><span class="comment">-- 直接比较</span></span><br><span class="line"><span class="keyword">select</span> sysdate <span class="keyword">from</span> dual <span class="keyword">where</span> <span class="string">'2019-12-30 19:39:05'</span> <span class="operator">&gt;</span> <span class="string">'2019-12-30 17:39:05'</span>;</span><br><span class="line"><span class="comment">-- 将字符串转换为相同格式相同进制的日期类型</span></span><br><span class="line"><span class="keyword">select</span>  sysdate <span class="keyword">from</span> dual <span class="keyword">where</span> to_date(<span class="string">'2019-12-30 19:39:05'</span>,<span class="string">'yyyy-mm-dd hh24:mi:ss'</span>) <span class="operator">&gt;</span> to_date(<span class="string">'2019-12-30 17:39:05'</span>,<span class="string">'yyyy-mm-dd hh24:mi:ss'</span>);</span><br></pre></td></tr></tbody></table></figure><h4 id="返回Map列名大小写问题"><a href="#返回Map列名大小写问题" class="headerlink" title="返回Map列名大小写问题"></a>返回 Map 列名大小写问题</h4><p><indent>转换过程发现，使用 Hibernate 返回实体类型的数据我们可以不用关注列名大小写问题，但是返回 Map 类型的数据集合时，对 Mysql 和 Oracle 返回的 Map 的 key 大小写是不一致的，也就是返回列名大小写不统一，对于两数据库不同的系统环境列名大小写情况如下：</indent></p><table><thead><tr><th align="center">系统</th><th align="center"> MySQL Data Type</th><th align="center">Oracle Data Type</th></tr></thead><tbody><tr><td align="center">Windows</td><td align="center"> 默认都不区分大小写，<br>可通过修改配置来区分大小写：<br>lower_case_table_names = 0（0：区分大小写，1：不区分大小写）</td><td align="center">1、在 Oracle 中，如果字段名称被双引号（””）包裹，Oracle 会区分大小写；<br>2、如果字段名称没有被双引号（””）包裹，则全部转换成大写来执行。<br>3、如果表结构设计时，字段名称使用了数据库的保留字，SQL 中的字段名称必须用双引号（””）包裹，以避免 SQL 语句执行出错。</td></tr><tr><td align="center">Linux</td><td align="center">1、数据库名与表名是严格区分大小写的；<br>2、表的别名是严格区分大小写的；<br>3、列名与列的别名在所有的情况下均是忽略大小写的；<br>4、变量名也是严格区分大小写的；</td><td align="center">同 Windows</td></tr></tbody></table><p><indent>根据以上，可以看出，我们要解决大小写问题，需要对返回列表的字段名加引号（””），这种做法工作量过大，于是查看了一下返回 Map 集合的接口实现，如下：</indent></p><figure class="highlight java"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><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> List&lt;Map&gt; <span class="title function_">findMapResultBySql</span><span class="params">(String sql)</span> {</span><br><span class="line">    <span class="type">SQLQuery</span> <span class="variable">query</span> <span class="operator">=</span> <span class="built_in">this</span>.getCurrentSession().createSQLQuery(sql);</span><br><span class="line">    query.setResultTransformer(Transformers.ALIAS_TO_ENTITY_MAP);</span><br><span class="line">    <span class="keyword">return</span> query.list();</span><br><span class="line">}</span><br></pre></td></tr></tbody></table></figure><p><indent>很容易可以看出，处理结果集返回的关键是 Transformers.ALIAS_TO_ENTITY_MAP，查看源码</indent></p><figure class="highlight java"><figcaption><span>AliasToEntityMapResultTransformer.java</span></figcaption><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">AliasToEntityMapResultTransformer</span> <span class="keyword">extends</span> <span class="title class_">AliasedTupleSubsetResultTransformer</span> {</span><br><span class="line"></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">final</span> <span class="type">AliasToEntityMapResultTransformer</span> <span class="variable">INSTANCE</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">AliasToEntityMapResultTransformer</span>();</span><br><span class="line"></span><br><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * Disallow instantiation of AliasToEntityMapResultTransformer.</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="keyword">private</span> <span class="title function_">AliasToEntityMapResultTransformer</span><span class="params">()</span> {</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="meta">@Override</span></span><br><span class="line"><span class="keyword">public</span> Object <span class="title function_">transformTuple</span><span class="params">(Object[] tuple, String[] aliases)</span> {</span><br><span class="line"><span class="type">Map</span> <span class="variable">result</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">HashMap</span>(tuple.length);</span><br><span class="line"><span class="keyword">for</span> ( <span class="type">int</span> i=<span class="number">0</span>; i&lt;tuple.length; i++ ) {</span><br><span class="line"><span class="type">String</span> <span class="variable">alias</span> <span class="operator">=</span> aliases[i];</span><br><span class="line"><span class="keyword">if</span> ( alias!=<span class="literal">null</span> ) {</span><br><span class="line">result.put( alias, tuple[i] );</span><br><span class="line">}</span><br><span class="line">}</span><br><span class="line"><span class="keyword">return</span> result;</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="meta">@Override</span></span><br><span class="line"><span class="keyword">public</span> <span class="type">boolean</span> <span class="title function_">isTransformedValueATupleElement</span><span class="params">(String[] aliases, <span class="type">int</span> tupleLength)</span> {</span><br><span class="line"><span class="keyword">return</span> <span class="literal">false</span>;</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * Serialization hook for ensuring singleton uniqueing.</span></span><br><span class="line"><span class="comment"> *</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@return</span> The singleton instance : {<span class="doctag">@link</span> #INSTANCE}</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="keyword">private</span> Object <span class="title function_">readResolve</span><span class="params">()</span> {</span><br><span class="line"><span class="keyword">return</span> INSTANCE;</span><br><span class="line">}</span><br><span class="line">}</span><br></pre></td></tr></tbody></table></figure><p><indent>可以看出，AliasToEntityMapResultTransformer 类的 transformTuple 方法即是解决问题的关键，于是自定义自己的返回结果集处理工具，如下：</indent></p><figure class="highlight java"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * 返回map统一小写</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@author</span> sunys</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">CustomResultTransformer</span> <span class="keyword">extends</span> <span class="title class_">AliasedTupleSubsetResultTransformer</span> {</span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">final</span> <span class="type">CustomResultTransformer</span> <span class="variable">INSTANCE</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">CustomResultTransformer</span>();</span><br><span class="line">    <span class="keyword">private</span> <span class="title function_">CustomResultTransformer</span><span class="params">()</span> {</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="keyword">public</span> Object <span class="title function_">transformTuple</span><span class="params">(Object[] tuple, String[] aliases)</span> {</span><br><span class="line">        <span class="type">Map</span> <span class="variable">result</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">HashMap</span>(tuple.length);</span><br><span class="line">        <span class="keyword">for</span> ( <span class="type">int</span> i=<span class="number">0</span>; i&lt;tuple.length; i++ ) {</span><br><span class="line">            <span class="type">String</span> <span class="variable">alias</span> <span class="operator">=</span> aliases[i];</span><br><span class="line">            <span class="keyword">if</span> ( alias!=<span class="literal">null</span> ) {</span><br><span class="line">                <span class="comment">//将Map的key转为小写返回</span></span><br><span class="line">                result.put( alias.toLowerCase(), tuple[i] );</span><br><span class="line">            }</span><br><span class="line">        }</span><br><span class="line">        <span class="keyword">return</span> result;</span><br><span class="line">    }</span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="keyword">public</span> <span class="type">boolean</span> <span class="title function_">isTransformedValueATupleElement</span><span class="params">(String[] aliases, <span class="type">int</span> tupleLength)</span> {</span><br><span class="line">        <span class="keyword">return</span> <span class="literal">false</span>;</span><br><span class="line">    }</span><br><span class="line">    <span class="keyword">private</span> Object <span class="title function_">readResolve</span><span class="params">()</span> {</span><br><span class="line">        <span class="keyword">return</span> INSTANCE;</span><br><span class="line">    }</span><br><span class="line">}</span><br></pre></td></tr></tbody></table></figure><p><indent>然后设置 SQLQuery 的 ResultTransformer，如下：</indent></p><figure class="highlight java"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><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><br><span class="line"><span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">final</span> <span class="type">CustomResultTransformer</span> <span class="variable">ALIAS_TO_ENTITY_LOWERCASE_MAP</span> <span class="operator">=</span></span><br><span class="line">CustomResultTransformer.INSTANCE;</span><br><span class="line"></span><br><span class="line"><span class="keyword">public</span> List&lt;Map&gt; <span class="title function_">findMapResultBySql</span><span class="params">(String sql)</span> {</span><br><span class="line">    <span class="type">SQLQuery</span> <span class="variable">query</span> <span class="operator">=</span> <span class="built_in">this</span>.getCurrentSession().createSQLQuery(sql);</span><br><span class="line">    query.setResultTransformer(ALIAS_TO_ENTITY_LOWERCASE_MAP);</span><br><span class="line">    <span class="keyword">return</span> query.list();</span><br><span class="line">}</span><br></pre></td></tr></tbody></table></figure><p><indent>至此，Oracle 和 Mysql 数据库返回 Map 列名统一大小写问题得以解决。</indent></p><link rel="stylesheet" href="https://fastly.jsdelivr.net/npm/markmap-toolbar@0.18.10/dist/style.css"><script src="https://fastly.jsdelivr.net/npm/d3@7"></script><script src="https://fastly.jsdelivr.net/npm/markmap-view@0.18.10"></script><script src="https://fastly.jsdelivr.net/npm/markmap-toolbar@0.18.10"></script><link rel="stylesheet" href="/css/markmap.css"><script src="/js/markmap.js"></script>]]></content>
    
    
    <summary type="html">&lt;p&gt;&lt;img src=&quot;https://oss.syshlang.com/blog/image/3bb849c2f9874fbe8be711d43d13dfac.jpg&quot;&gt;&lt;/p&gt;</summary>
    
    
    
    <category term="CODING" scheme="https://syshlang.com/categories/CODING/"/>
    
    <category term="database" scheme="https://syshlang.com/categories/CODING/database/"/>
    
    <category term="mysql" scheme="https://syshlang.com/categories/CODING/database/mysql/"/>
    
    <category term="oracle" scheme="https://syshlang.com/categories/CODING/database/oracle/"/>
    
    
    <category term="Mysql" scheme="https://syshlang.com/tags/Mysql/"/>
    
    <category term="JAVA" scheme="https://syshlang.com/tags/JAVA/"/>
    
    <category term="Oracle" scheme="https://syshlang.com/tags/Oracle/"/>
    
  </entry>
  
  <entry>
    <title>Manjaro 安装使用</title>
    <link href="https://syshlang.com/posts/55ce8373/"/>
    <id>https://syshlang.com/posts/55ce8373/</id>
    <published>2019-11-20T12:36:29.000Z</published>
    <updated>2025-12-01T14:31:49.639Z</updated>
    
    <content type="html"><![CDATA[<link rel="stylesheet" class="aplayer-secondary-style-marker" href="/assets/css/APlayer.min.css"><script src="/assets/js/APlayer.min.js" class="aplayer-secondary-script-marker"></script><script class="meting-secondary-script-marker" src="/assets/js/Meting.min.js"></script><p><img src="https://oss.syshlang.com/blog/image/d5abc620924e4245914a034d285cc031.jpg" alt="Manjaro"></p><span id="more"></span><p><indent>用一句话描述 Manjaro：Manjaro 是一款基于 Arch Linux、对用户友好、拥有最齐全的 Linux 软件库、<span class="exturl" data-url="aHR0cHM6Ly9kaXN0cm93YXRjaC5jb20vZHdyZXMucGhwP3Jlc291cmNlPXBvcHVsYXJpdHk=">全球排名<i class="fa fa-external-link-alt"></i></span>前三的 Linux 发行版。</indent></p><h1 id="一、安装"><a href="#一、安装" class="headerlink" title="一、安装"></a>一、安装</h1><p>先上一张效果图<br><img src="https://oss.syshlang.com/blog/image/43311d799e764b94aeac66fa1bd3d664.png" alt="Manjaro安装桌面"></p><h2 id="1-1-下载安装文件"><a href="#1-1-下载安装文件" class="headerlink" title="1.1 下载安装文件"></a>1.1 下载安装文件</h2><p><span class="exturl" data-url="aHR0cHM6Ly9tYW5qYXJvLm9yZy8=">Manjaro 官网<i class="fa fa-external-link-alt"></i></span><br><span class="exturl" data-url="aHR0cHM6Ly9tYW5qYXJvLm9yZy5jbi8=">Manjaro 中文网站<i class="fa fa-external-link-alt"></i></span></p><h2 id="1-2-制作安装启动盘及安装"><a href="#1-2-制作安装启动盘及安装" class="headerlink" title="1.2 制作安装启动盘及安装"></a>1.2 制作安装启动盘及安装</h2><p>准备一个 U 盘，制作安装启动盘的工具比较多，常用的如下：</p><blockquote><p><span class="exturl" data-url="aHR0cHM6Ly9ydWZ1cy5pZS96aC8=">Rufus<i class="fa fa-external-link-alt"></i></span>、<span class="exturl" data-url="aHR0cHM6Ly93d3cuYmFsZW5hLmlvL2V0Y2hlci8=">Etcher<i class="fa fa-external-link-alt"></i></span>、<span class="exturl" data-url="aHR0cHM6Ly93d3cuZGVlcGluLm9yZy96aC9vcmlnaW5hbC9kZWVwaW4tYm9vdC1tYWtlci8=">深度启动盘制作工具<i class="fa fa-external-link-alt"></i></span></p></blockquote><p>工具任选其一，具体的制作及安装过程懂的都懂，不详述。</p><div class="note warning"><p>如果电脑主板为 UEFI 模式，建议制作支持 BIOS+UEFI 的启动盘，并在安装之前在主板设置中关闭安全启动。</p></div><h1 id="二、系统配置优化及软件安装"><a href="#二、系统配置优化及软件安装" class="headerlink" title="二、系统配置优化及软件安装"></a>二、系统配置优化及软件安装</h1><h2 id="2-1-设置国内镜像源"><a href="#2-1-设置国内镜像源" class="headerlink" title="2.1 设置国内镜像源"></a>2.1 设置国内镜像源</h2><figure class="highlight shell"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta prompt_"># </span><span class="language-bash">手动更改源排名,选快的</span></span><br><span class="line">sudo pacman-mirrors -c China -i -m rank</span><br></pre></td></tr></tbody></table></figure><p>也可以手动编辑镜像源配置文件</p><figure class="highlight shell"><table><tbody><tr><td class="gutter"><pre><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 prompt_"># </span><span class="language-bash">备份配置</span></span><br><span class="line">sudo cp /etc/pacman.d/mirrorlist /etc/pacman.d/mirrorlist.backup</span><br><span class="line"><span class="meta prompt_"># </span><span class="language-bash">编辑配置文件，在文件中添加镜像源</span></span><br><span class="line">sudo vim /etc/pacman.d/mirrorlist</span><br></pre></td></tr></tbody></table></figure><div class="note info"><p>相关镜像源地址如下</p><figure class="highlight shell"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><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 prompt_"># </span><span class="language-bash">manjaro 稳定源</span></span><br><span class="line"><span class="meta prompt_"># </span><span class="language-bash">中科大</span></span><br><span class="line">Server = https://mirrors.ustc.edu.cn/manjaro/stable/$repo/$arch</span><br><span class="line"><span class="meta prompt_"># </span><span class="language-bash"> 清华大学</span></span><br><span class="line">Server = https://mirrors.tuna.tsinghua.edu.cn/manjaro/stable/$repo/$arch</span><br><span class="line"><span class="meta prompt_"># </span><span class="language-bash">上海交通大学</span></span><br><span class="line">Server = https://mirrors.sjtug.sjtu.edu.cn/manjaro/stable/$repo/$arch</span><br><span class="line"><span class="meta prompt_"># </span><span class="language-bash">浙江大学</span></span><br><span class="line">Server = https://mirrors.zju.edu.cn/manjaro/stable/$repo/$arch</span><br><span class="line"><span class="meta prompt_"></span></span><br><span class="line"><span class="meta prompt_"># </span><span class="language-bash">archlinux 稳定源</span></span><br><span class="line"><span class="meta prompt_"># </span><span class="language-bash">清华源</span></span><br><span class="line">Server = https://mirrors.tuna.tsinghua.edu.cn/archlinux/$repo/os/$arch</span><br><span class="line"><span class="meta prompt_"># </span><span class="language-bash">163 源</span></span><br><span class="line">Server = http://mirrors.163.com/archlinux/$repo/os/$arch</span><br><span class="line"><span class="meta prompt_"># </span><span class="language-bash">阿里源</span></span><br><span class="line">Server = http://mirrors.aliyun.com/archlinux/$repo/os/$arch</span><br></pre></td></tr></tbody></table></figure></div><h2 id="2-2-设置国内软件社区源"><a href="#2-2-设置国内软件社区源" class="headerlink" title="2.2 设置国内软件社区源"></a>2.2 设置国内软件社区源</h2><figure class="highlight shell"><table><tbody><tr><td class="gutter"><pre><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 prompt_"># </span><span class="language-bash">备份配置</span></span><br><span class="line">sudo cp /etc/pacman.conf /etc/pacman.conf.backup</span><br><span class="line"><span class="meta prompt_"># </span><span class="language-bash">编辑配置文件，在文件中添加镜像源</span></span><br><span class="line">sudo vim /etc/pacman.conf</span><br></pre></td></tr></tbody></table></figure><div class="note info"><p>相关软件社区源地址如下</p><figure class="highlight shell"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><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">[archlinuxcn]</span><br><span class="line">SigLevel = Optional TrustedOnly</span><br><span class="line"><span class="meta prompt_"># </span><span class="language-bash">阿里源</span></span><br><span class="line">Server = https://mirrors.aliyun.com/archlinuxcn/$arch</span><br><span class="line"><span class="meta prompt_"># </span><span class="language-bash">清华源</span></span><br><span class="line">Server = http://mirrors.tuna.tsinghua.edu.cn/archlinuxcn/$arch</span><br><span class="line"><span class="meta prompt_"># </span><span class="language-bash">中科大源</span></span><br><span class="line">Server = https://mirrors.ustc.edu.cn/archlinuxcn/$arch</span><br><span class="line"></span><br><span class="line">[antergos]</span><br><span class="line">SigLevel = TrustAll</span><br><span class="line">Server = http://mirrors.tuna.tsinghua.edu.cn/antergos/$repo/$arch</span><br><span class="line">Server = https://mirrors.ustc.edu.cn/antergos/$repo/$arch</span><br><span class="line"></span><br><span class="line">[arch4edu]</span><br><span class="line">SigLevel = TrustAll</span><br><span class="line">Server = https://mirrors.aliyun.com/arch4edu/$arch</span><br><span class="line">Server = http://mirrors.tuna.tsinghua.edu.cn/arch4edu/$arch</span><br></pre></td></tr></tbody></table></figure><p>注意：以上同一类型的源，只能添加一个 Server 地址，例如：<br><img src="https://oss.syshlang.com/blog/image/385bdfc903f343c0ba8148ce86ca4548.png" alt="社区源地址"></p></div><h2 id="2-3-更新源导入GPG-Key"><a href="#2-3-更新源导入GPG-Key" class="headerlink" title="2.3 更新源导入GPG Key"></a>2.3 更新源导入 GPG Key</h2><figure class="highlight shell"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta prompt_"># </span><span class="language-bash">更新数据源</span></span><br><span class="line">sudo pacman -Syy</span><br><span class="line"><span class="meta prompt_">#</span><span class="language-bash">安装导入GPG key</span></span><br><span class="line">sudo pacman -S archlinuxcn-keyring</span><br><span class="line">sudo pacman -S antergos-keyrin</span><br><span class="line"><span class="meta prompt_"># </span><span class="language-bash">同步包数据库并升级所有软件包,更新系统</span></span><br><span class="line">sudo pacman -Syu</span><br></pre></td></tr></tbody></table></figure><h2 id="2-4-添加-AUR-源"><a href="#2-4-添加-AUR-源" class="headerlink" title="2.4 添加 AUR 源"></a>2.4 添加 AUR 源</h2><h3 id="2-4-1-安装使用yay及配置AUR-源"><a href="#2-4-1-安装使用yay及配置AUR-源" class="headerlink" title="2.4.1 安装使用yay及配置AUR 源"></a>2.4.1 安装使用 yay 及配置 AUR 源</h3><figure class="highlight shell"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><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 prompt_"># </span><span class="language-bash">安装yay</span></span><br><span class="line">sudo pacman -S yay</span><br><span class="line"><span class="meta prompt_"># </span><span class="language-bash">安装yay编译包时需要的一些工具，不然会报错缺少 fakeroot</span></span><br><span class="line">sudo pacman -S base-devel binutils</span><br><span class="line"><span class="meta prompt_"># </span><span class="language-bash">添加 AUR 源 ,此时会生成 config.json 配置文件</span></span><br><span class="line">yay --aururl https://aur.tuna.tsinghua.edu.cn --save</span><br><span class="line"><span class="meta prompt_"># </span><span class="language-bash">查看修改配置文件</span></span><br><span class="line">sudo vim ~/.config/yay/config.json</span><br><span class="line"><span class="meta prompt_"># </span><span class="language-bash">查看配置</span></span><br><span class="line">yay -P -g</span><br></pre></td></tr></tbody></table></figure><h3 id="2-4-2-安装使用yaourt及配置AUR-源"><a href="#2-4-2-安装使用yaourt及配置AUR-源" class="headerlink" title="2.4.2 安装使用yaourt及配置AUR 源"></a>2.4.2 安装使用 yaourt 及配置 AUR 源</h3><figure class="highlight shell"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><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 prompt_"># </span><span class="language-bash">安装yaourt</span></span><br><span class="line">pacman -S yaourt</span><br><span class="line"><span class="meta prompt_"># </span><span class="language-bash">备份配置</span></span><br><span class="line">sudo cp /etc/yaourtrc /etc/yaourtrc.backup</span><br><span class="line"><span class="meta prompt_"># </span><span class="language-bash">编辑配置文件</span></span><br><span class="line">sudo vim /etc/yaourtrc</span><br><span class="line"><span class="meta prompt_"># </span><span class="language-bash">在配置文件中添加如下内容</span></span><br><span class="line">AURURL="https://aur.tuna.tsinghua.edu.cn"</span><br></pre></td></tr></tbody></table></figure><h3 id="2-4-3-AUR-优化"><a href="#2-4-3-AUR-优化" class="headerlink" title="2.4.3 AUR 优化"></a>2.4.3 AUR 优化</h3><figure class="highlight shell"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta prompt_"># </span><span class="language-bash">开启 pacman 和 yay 的彩色输出</span></span><br><span class="line">sudo sed -i "s/#Color/Color/g" /etc/pacman.conf</span><br><span class="line"><span class="meta prompt_"># </span><span class="language-bash">加速 AUR 包构建</span></span><br><span class="line"><span class="meta prompt_"># </span><span class="language-bash">默认情况下 makepkg 构建 AUR 包时会启用压缩，本机安装浪费时间，以下语句设置构建包时不进行压缩</span></span><br><span class="line">sudo sed -i "s/PKGEXT='.pkg.tar.xz'/PKGEXT='.pkg.tar'/g" /etc/makepkg.conf</span><br></pre></td></tr></tbody></table></figure><h2 id="2-5-软件安装卸载"><a href="#2-5-软件安装卸载" class="headerlink" title="2.5 软件安装卸载"></a>2.5 软件安装卸载</h2><h3 id="2-5-1-pacman"><a href="#2-5-1-pacman" class="headerlink" title="2.5.1 pacman"></a>2.5.1 pacman</h3><blockquote><p><indent>pacman 命令详解 Pacman 是一个软件包管理器。在 manjaro 中既可以使用命令也可以使用它提供的 GUI 界面进行操作，常用命令如下：</indent></p></blockquote><figure class="highlight shell"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><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="meta prompt_"># </span><span class="language-bash">安装软件,同时安装多个包时以空格分隔包名</span></span><br><span class="line">pacman -S [packageName1 packageName2 ...]</span><br><span class="line"><span class="meta prompt_"># </span><span class="language-bash">下载软件包数据库软件列表</span></span><br><span class="line">pacman -Sy [packageName]</span><br><span class="line"><span class="meta prompt_"># </span><span class="language-bash">安装软件，并显示详细的信息</span></span><br><span class="line">pacman -Sv [packageName]</span><br><span class="line"><span class="meta prompt_"># </span><span class="language-bash">下载软件包，但不安装。</span></span><br><span class="line">pacman -Sw [packageName]</span><br><span class="line"><span class="meta prompt_"># </span><span class="language-bash">安装本地软件包，例如： 软件名.pkg.tar.gz</span></span><br><span class="line">pacman -U [packageName].pkg.tar.gz</span><br><span class="line"><span class="meta prompt_"># </span><span class="language-bash">安装一个远程软件包，例如： http://www.example.com/repo/example.pkg.tar.xz</span></span><br><span class="line">pacman -U [url].pkg.tar.gz</span><br><span class="line"><span class="meta prompt_"></span></span><br><span class="line"><span class="meta prompt_"># </span><span class="language-bash">删除软件，但保留其全部已经安装的依赖关系</span></span><br><span class="line">pacman -R [packageName]</span><br><span class="line"><span class="meta prompt_"># </span><span class="language-bash">删除软件，并显示详细的信息</span></span><br><span class="line">pacman -Rv [packageName]</span><br><span class="line"><span class="meta prompt_"># </span><span class="language-bash">删除软件，并同时删除只有该软件依赖的依赖关系</span></span><br><span class="line">pacman -Rs [packageName]</span><br><span class="line"><span class="meta prompt_"># </span><span class="language-bash">删除软件，并同时删除所有该软件的依赖关系，该命令可能导致其他共用依赖的软件无法使用，不建议使用</span></span><br><span class="line">pacman -Rsc [packageName]</span><br><span class="line"><span class="meta prompt_"># </span><span class="language-bash">删除软件,并同时删除不再被任何软件所需要的依赖</span></span><br><span class="line">pacman -Ru [packageName]</span><br><span class="line"><span class="meta prompt_"></span></span><br><span class="line"><span class="meta prompt_"># </span><span class="language-bash">搜索含关键字的软件包</span></span><br><span class="line">pacman -Ss [关键字]</span><br><span class="line"><span class="meta prompt_"># </span><span class="language-bash">显示软件仓库中所有软件的列表</span></span><br><span class="line">pacman -Sl</span><br><span class="line"><span class="meta prompt_"># </span><span class="language-bash">根据关键字显示软件仓库中软件的列表</span></span><br><span class="line">pacman -Sl | [关键字]</span><br><span class="line"><span class="meta prompt_"># </span><span class="language-bash">搜索已安装的软件</span></span><br><span class="line">pacman -Qs [关键字]</span><br><span class="line"><span class="meta prompt_"># </span><span class="language-bash">列出所有可升级的软件包</span></span><br><span class="line">pacman -Qu</span><br><span class="line"><span class="meta prompt_"># </span><span class="language-bash">列出不被任何软件依赖的软件包</span></span><br><span class="line">pacman -Qt</span><br><span class="line"><span class="meta prompt_"></span></span><br><span class="line"><span class="meta prompt_"># </span><span class="language-bash">查看软件包是否已安装，已安装则显示软件包名称和版本</span></span><br><span class="line">pacman -Q [packageName]</span><br><span class="line"><span class="meta prompt_"># </span><span class="language-bash">查看软件包信息</span></span><br><span class="line">pacman -Qi [packageName]</span><br><span class="line"><span class="meta prompt_"># </span><span class="language-bash">列出软件包内所有文件</span></span><br><span class="line">pacman -Ql [packageName]</span><br><span class="line"><span class="meta prompt_"></span></span><br><span class="line"><span class="meta prompt_"># </span><span class="language-bash">清理位于 /var/cache/pacman/pkg/ 目录中未安装的包文件</span></span><br><span class="line">pacman -Sc</span><br><span class="line"><span class="meta prompt_"># </span><span class="language-bash">清理所有的缓存文件</span></span><br><span class="line">pacman -Scc</span><br><span class="line"><span class="meta prompt_"></span></span><br><span class="line"><span class="meta prompt_"># </span><span class="language-bash">同步包数据库并升级所有软件包,更新系统</span></span><br><span class="line">sudo pacman -Syu</span><br></pre></td></tr></tbody></table></figure><h3 id="2-5-2-yay"><a href="#2-5-2-yay" class="headerlink" title="2.5.2 yay"></a>2.5.2 yay</h3><figure class="highlight shell"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><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 prompt_"># </span><span class="language-bash">从 AUR 安装软件包</span></span><br><span class="line">yay -S [packageName]</span><br><span class="line"><span class="meta prompt_"># </span><span class="language-bash">删除包</span></span><br><span class="line">yay -Rns [packageName]</span><br><span class="line"><span class="meta prompt_"># </span><span class="language-bash">升级所有已安装的包</span></span><br><span class="line">yay -Syu</span><br><span class="line"><span class="meta prompt_"># </span><span class="language-bash">打印系统统计信息</span></span><br><span class="line">yay -Ps</span><br><span class="line"><span class="meta prompt_"># </span><span class="language-bash">检查安装的版本</span></span><br><span class="line">yay -Qi [packageName]</span><br></pre></td></tr></tbody></table></figure><h3 id="2-5-3-yaourt"><a href="#2-5-3-yaourt" class="headerlink" title="2.5.3 yaourt"></a>2.5.3 yaourt</h3><figure class="highlight shell"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><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 prompt_"># </span><span class="language-bash">从AUR安装软件包</span></span><br><span class="line">yaourt -S [packageName]</span><br><span class="line"><span class="meta prompt_"># </span><span class="language-bash">删除软件包</span></span><br><span class="line">yaourt -R [packageName]</span><br><span class="line"><span class="meta prompt_"># </span><span class="language-bash">使用关键字搜索软件包</span></span><br><span class="line">yaourt -Ss [packageName]</span><br><span class="line"><span class="meta prompt_"># </span><span class="language-bash">从AUR升级本地软件数据库并安装更新</span></span><br><span class="line">yaourt -Syu –-aur</span><br><span class="line"><span class="meta prompt_"># </span><span class="language-bash">列出软件包信息</span></span><br><span class="line">yaourt -Si [packageName]</span><br><span class="line"><span class="meta prompt_"># </span><span class="language-bash">从缓存中清除旧的软件包</span></span><br><span class="line">yaourt -Sc</span><br><span class="line"><span class="meta prompt_"># </span><span class="language-bash">安装AUR中的更新软件包</span></span><br><span class="line">yaourt -Su</span><br><span class="line"><span class="meta prompt_"># </span><span class="language-bash">获取最新的AUR软件包数据库</span></span><br><span class="line">yaourt -Sy</span><br><span class="line"><span class="meta prompt_"># </span><span class="language-bash">清除AUR软件包数据库</span></span><br><span class="line">yaourt -Cd</span><br></pre></td></tr></tbody></table></figure><h2 id="2-6-使用debtap安装deb包"><a href="#2-6-使用debtap安装deb包" class="headerlink" title="2.6 使用debtap安装deb包"></a>2.6 使用 debtap 安装 deb 包</h2><figure class="highlight shell"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><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 prompt_"># </span><span class="language-bash">安装debtap</span></span><br><span class="line">sudo pacman -S pacaur</span><br><span class="line">pacaur -S debtap</span><br><span class="line"><span class="meta prompt_"># </span><span class="language-bash">或者 直接yay安装</span></span><br><span class="line">yay -S debtap</span><br><span class="line"><span class="meta prompt_"># </span><span class="language-bash">初始化 debtap</span></span><br><span class="line">sudo debtap -u</span><br><span class="line"><span class="meta prompt_"># </span><span class="language-bash">使用debtap将deb包转换为<span class="built_in">arch</span>包</span></span><br><span class="line">sudo debtap your_package.deb</span><br><span class="line"><span class="meta prompt_"># </span><span class="language-bash">使用pacman安装转换的<span class="built_in">arch</span>包</span></span><br><span class="line">sudo pacman -U your-converted-pakage.pkg.tar.zst</span><br></pre></td></tr></tbody></table></figure><h2 id="2-7-使用Snap安装软件"><a href="#2-7-使用Snap安装软件" class="headerlink" title="2.7 使用Snap安装软件"></a>2.7 使用 Snap 安装软件</h2><figure class="highlight shell"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><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 prompt_"># </span><span class="language-bash">安装Snap</span></span><br><span class="line">sudo pacman -S snapd</span><br><span class="line"><span class="meta prompt_"># </span><span class="language-bash">启动并启用snapd服务</span></span><br><span class="line">sudo systemctl enable --now snapd.socket</span><br><span class="line"><span class="meta prompt_"># </span><span class="language-bash">查看服务状态</span></span><br><span class="line">systemctl status snapd.socket</span><br><span class="line"><span class="meta prompt_"># </span><span class="language-bash">创建符号链接</span></span><br><span class="line">sudo ln -s /var/lib/snapd/snap /snap</span><br><span class="line"><span class="meta prompt_"># </span><span class="language-bash">安装软件</span></span><br><span class="line">sudo snap install hello-world</span><br><span class="line"><span class="meta prompt_"># </span><span class="language-bash">列出已安装的snaps</span></span><br><span class="line">snap list</span><br><span class="line"><span class="meta prompt_"># </span><span class="language-bash">删除已安装的snaps</span></span><br><span class="line">sudo snap remove hello-world</span><br><span class="line"><span class="meta prompt_"># </span><span class="language-bash">安装Snap Store</span></span><br><span class="line">sudo snap install snap-store</span><br><span class="line"><span class="meta prompt_"># </span><span class="language-bash"> 启动 snap-store 然后可以通过snap-store商店安装软件</span></span><br><span class="line">snap-store</span><br></pre></td></tr></tbody></table></figure><h2 id="2-8-常用的软件安装"><a href="#2-8-常用的软件安装" class="headerlink" title="2.8 常用的软件安装"></a>2.8 常用的软件安装</h2><figure class="highlight shell"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><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 prompt_"># </span><span class="language-bash">安装QQ</span></span><br><span class="line">yay -S deepin.com.qq.im</span><br><span class="line"><span class="meta prompt_"># </span><span class="language-bash">安装微信</span></span><br><span class="line">yay -S deepin-wine-wechat</span><br><span class="line"><span class="meta prompt_"># </span><span class="language-bash">安装markdown编辑器</span></span><br><span class="line">sudo pacman -S typora</span><br><span class="line"><span class="meta prompt_"># </span><span class="language-bash">安装vscode几种方法</span></span><br><span class="line"><span class="meta prompt_"># </span><span class="language-bash">1、pacman安装  可能不是最新版本</span></span><br><span class="line">sudo pacman -S visual-studio-code-bin</span><br><span class="line"><span class="meta prompt_"># </span><span class="language-bash">2、从AUR安装  可能不是最新版本</span></span><br><span class="line">yaourt -S visual-studio-code-bin</span><br><span class="line">yay -S visual-studio-code-bin</span><br><span class="line"><span class="meta prompt_"># </span><span class="language-bash">3、使用Snap安装 ，非vscode 官方编译发布的安装包，存在输入法bug</span></span><br><span class="line">sudo snap install code --classic</span><br><span class="line"><span class="meta prompt_"># </span><span class="language-bash">4、使用源码安装</span></span><br><span class="line">cd ~/tmp</span><br><span class="line">git clone https://aur.archlinux.org/visual-studio-code-bin.git</span><br><span class="line">cd visual-studio-code-bin/</span><br><span class="line">makepkg -si</span><br><span class="line"><span class="meta prompt_"># </span><span class="language-bash">安装360压缩</span></span><br><span class="line">yay -S aur/360zip</span><br><span class="line"><span class="meta prompt_">#</span><span class="language-bash">安装docky</span></span><br><span class="line">sudo pacman -S docky</span><br><span class="line"><span class="meta prompt_"># </span><span class="language-bash">安装flameshot截图</span></span><br><span class="line">sudo pacman -S flameshot</span><br><span class="line"><span class="meta prompt_"># </span><span class="language-bash">安装深度截图</span></span><br><span class="line">sudo pacman -S deepin-screenshot</span><br><span class="line"><span class="meta prompt_"># </span><span class="language-bash">安装WPS及wps字体</span></span><br><span class="line">sudo pacman -S wps-office-cn wps-office-mime-cn wps-office-mui-zh-cn</span><br><span class="line">sudo pacman -S ttf-wps-fonts</span><br><span class="line"><span class="meta prompt_"># </span><span class="language-bash">安装坚果云</span></span><br><span class="line">yay -S nutstore</span><br><span class="line"><span class="meta prompt_"># </span><span class="language-bash">如果出现坚果云打不开或者没有登录页面的情况，安装坚果云相关依赖</span></span><br><span class="line">yay -S python-gobject</span><br><span class="line"><span class="meta prompt_"># </span><span class="language-bash">安装搜狗输入法</span></span><br><span class="line">sudo pacman -S fcitx-im fcitx-configtool fcitx-sogoupinyin</span><br><span class="line">yay -S fcitx-sogoupinyin</span><br><span class="line">安装网易云音乐</span><br><span class="line">sudo pacman -S netease-cloud-music</span><br><span class="line"><span class="meta prompt_"># </span><span class="language-bash">安装chrome</span></span><br><span class="line">sudo pacman -S google-chrome</span><br><span class="line"><span class="meta prompt_"># </span><span class="language-bash">安装edge浏览器</span></span><br><span class="line">yay -S microsoft-edge-dev-bin</span><br></pre></td></tr></tbody></table></figure><h2 id="2-9-字体安装"><a href="#2-9-字体安装" class="headerlink" title="2.9 字体安装"></a>2.9 字体安装</h2><p>可参考以下：<br><span class="exturl" data-url="aHR0cHM6Ly93aWtpLmFyY2hsaW51eC5vcmcvdGl0bGUvTG9jYWxpemF0aW9uXyglRTclQUUlODAlRTQlQkQlOTMlRTQlQjglQUQlRTYlOTYlODcpL1NpbXBsaWZpZWRfQ2hpbmVzZV8oJUU3JUFFJTgwJUU0JUJEJTkzJUU0JUI4JUFEJUU2JTk2JTg3KQ==">Localization (简体中文)/Simplified Chinese (简体中文)<i class="fa fa-external-link-alt"></i></span><br><span class="exturl" data-url="aHR0cHM6Ly93aWtpLmFyY2hsaW51eC5vcmcvdGl0bGUvRm9udF9Db25maWd1cmF0aW9uXyglRTclQUUlODAlRTQlQkQlOTMlRTQlQjglQUQlRTYlOTYlODcpL0NoaW5lc2VfKCVFNyVBRSU4MCVFNCVCRCU5MyVFNCVCOCVBRCVFNiU5NiU4Nyk=">Font Configuration (简体中文)/Chinese (简体中文)<i class="fa fa-external-link-alt"></i></span></p><p>有些字体在安装源内可以使用命令直接安装，如下：</p><figure class="highlight shell"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta prompt_"># </span><span class="language-bash">文泉驿</span></span><br><span class="line">sudo pacman -S ttf-roboto noto-fonts ttf-dejavu</span><br><span class="line">sudo pacman -S wqy-bitmapfont wqy-microhei wqy-microhei-lite wqy-zenhei</span><br><span class="line"><span class="meta prompt_"># </span><span class="language-bash">思源字体</span></span><br><span class="line">sudo pacman -S noto-fonts-cjk adobe-source-han-sans-cn-fonts adobe-source-han-serif-cn-fonts</span><br></pre></td></tr></tbody></table></figure><p>有些字体则需要手动下载拷贝安装，如 jetbrains mono 字体：<br>首先，<span class="exturl" data-url="aHR0cHM6Ly93d3cuamV0YnJhaW5zLmNvbS9scC9tb25vLw==">下载 jetbrains mono 字体<i class="fa fa-external-link-alt"></i></span>;</p><figure class="highlight shell"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><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 prompt_"># </span><span class="language-bash">解压 JetBrainsMono-2.242.zip</span></span><br><span class="line">unzip JetBrainsMono-2.242.zip -d JetBrainsMono-2.242</span><br><span class="line"><span class="meta prompt_"># </span><span class="language-bash">将目录中所有文件复制到/usr/share/fonts （或者~/.local/share/fonts）下</span></span><br><span class="line">sudo cp JetBrainsMono-2.242/*  /usr/share/fonts</span><br><span class="line"><span class="meta prompt_"># </span><span class="language-bash">更新字体缓存</span></span><br><span class="line">fc-cache -f -v</span><br></pre></td></tr></tbody></table></figure><link rel="stylesheet" href="https://fastly.jsdelivr.net/npm/markmap-toolbar@0.18.10/dist/style.css"><script src="https://fastly.jsdelivr.net/npm/d3@7"></script><script src="https://fastly.jsdelivr.net/npm/markmap-view@0.18.10"></script><script src="https://fastly.jsdelivr.net/npm/markmap-toolbar@0.18.10"></script><link rel="stylesheet" href="/css/markmap.css"><script src="/js/markmap.js"></script>]]></content>
    
    
    <summary type="html">&lt;p&gt;&lt;img src=&quot;https://oss.syshlang.com/blog/image/d5abc620924e4245914a034d285cc031.jpg&quot; alt=&quot;Manjaro&quot;&gt;&lt;/p&gt;</summary>
    
    
    
    <category term="CODING" scheme="https://syshlang.com/categories/CODING/"/>
    
    <category term="操作系统" scheme="https://syshlang.com/categories/CODING/%E6%93%8D%E4%BD%9C%E7%B3%BB%E7%BB%9F/"/>
    
    <category term="linux" scheme="https://syshlang.com/categories/CODING/%E6%93%8D%E4%BD%9C%E7%B3%BB%E7%BB%9F/linux/"/>
    
    
    <category term="linux" scheme="https://syshlang.com/tags/linux/"/>
    
    <category term="arch" scheme="https://syshlang.com/tags/arch/"/>
    
    <category term="manjaro" scheme="https://syshlang.com/tags/manjaro/"/>
    
  </entry>
  
</feed>
